Merge branch 'develop' into trunk
This commit is contained in:
commit
d3b324ce1d
221 changed files with 30528 additions and 3115 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,6 +2,7 @@
|
|||
/public/hot
|
||||
/public/storage
|
||||
/public/fichiers
|
||||
/public/*
|
||||
/storage/*.key
|
||||
/vendor
|
||||
.env
|
||||
|
|
|
|||
10
CHANGELOG.md
Normal file
10
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
### 01.08 -> 0.0.1
|
||||
- Ajout d'un algo de charge
|
||||
- Ajout changement de mot de passe
|
||||
- Ajout messages http explicites
|
||||
- Correction couleurs
|
||||
|
||||
#### 30.07
|
||||
- Ajout changelog
|
||||
- Ajout footer infos
|
||||
- Correction couleurs
|
||||
38
IDEAS.md
38
IDEAS.md
|
|
@ -2,5 +2,41 @@
|
|||
- ~~permettre d'insérer des @user dans le texte avec lien vers user~~
|
||||
~~pas de liens vers user mais mise en form~~
|
||||
- ~~parse de @user + envoie de mail si un seul user est trouvé~~
|
||||
- permettre de modifier son mot de passe ? (je crois que ça n'as pas été mis exprès)
|
||||
- ~~permettre de modifier son mot de passe ? (je crois que ça n'as pas été mis exprès)~~
|
||||
- ~~parser les liens http~~
|
||||
|
||||
|
||||
Algorithme charge de travail ticket
|
||||
|
||||
|
||||
delais 7 jours 168h
|
||||
travaillé 5 jours 37h
|
||||
temps tickets 1 jours 7.4h
|
||||
|
||||
personnes 4 (TpP) 3.7h / semaine * prs
|
||||
|
||||
délais moyen ticket (Dmt) 0.75h
|
||||
|
||||
délais max attente pour 1 ticket (Dam) 7jours
|
||||
|
||||
niveaux de charge 4: - faible
|
||||
- stable
|
||||
- élevée
|
||||
- saturation
|
||||
|
||||
états tickets(Et) : - nouveaux (non traités)
|
||||
- en attente (charge pendante)
|
||||
- traités
|
||||
|
||||
Algo : linéaire
|
||||
|
||||
TpP : 3.7h
|
||||
P : 4
|
||||
|
||||
Et(nvx) * Dam
|
||||
|
||||
Nv Charge = (P * TpP) / (Et(nvx) * Dam) - (P * TpP) / (Et(nvx) * DmT) - (Et(att) * DmT)
|
||||
|
||||
|
||||
Si nv Charge < 0 => saturation
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Models\Project;
|
||||
use App\Models\Site;
|
||||
use App\Models\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
|
@ -11,6 +11,7 @@ use Illuminate\Support\Facades\Hash;
|
|||
|
||||
class CreateUser extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
|
|
@ -25,16 +26,6 @@ class CreateUser extends Command
|
|||
*/
|
||||
protected $description = 'Create a user';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
|
|
@ -52,16 +43,6 @@ class CreateUser extends Command
|
|||
$user->role = Role::admin();
|
||||
} else {
|
||||
$user->role = Role::client();
|
||||
try {
|
||||
$project = Project::where(
|
||||
'id',
|
||||
$this->ask("What's the id of the project (s)he's working on?")
|
||||
)->firstOrFail();
|
||||
$user->projects()->sync([$project->id]);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
$this->error('The project does not exists');
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
$user->password = Hash::make($pwd);
|
||||
$user->save();
|
||||
|
|
|
|||
|
|
@ -7,14 +7,13 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
|||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
//
|
||||
];
|
||||
protected $commands = [];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
|
|
@ -24,7 +23,6 @@ class Kernel extends ConsoleKernel
|
|||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// $schedule->command('inspire')->hourly();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -34,8 +32,7 @@ class Kernel extends ConsoleKernel
|
|||
*/
|
||||
protected function commands()
|
||||
{
|
||||
$this->load(__DIR__.'/Commands');
|
||||
|
||||
$this->load(__DIR__ . '/Commands');
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
<?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'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
<?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 json()
|
||||
* @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,
|
||||
'json' => 4,
|
||||
'email' => 5,
|
||||
'password' => 6,
|
||||
'float' => 7,
|
||||
'enum' => 8,
|
||||
'belongsTo' => 9,
|
||||
'belongsToMany' => 10
|
||||
];
|
||||
}
|
||||
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'boolean' => trans('Booléen'),
|
||||
'int' => trans('Nombre'),
|
||||
'string' => trans('Chaîne de charactères simple'),
|
||||
'text' => trans('Texte'),
|
||||
'json' => trans('Json'),
|
||||
'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')
|
||||
];
|
||||
}
|
||||
}
|
||||
41
app/Enums/FileCategory.php
Normal file
41
app/Enums/FileCategory.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @method static self collectionNeeds()
|
||||
* @method static self specifications()
|
||||
* @method static self quotation()
|
||||
* @method static self tests()
|
||||
* @method static self informations()
|
||||
* @method static self feedback()
|
||||
*/
|
||||
|
||||
class FileCategory extends BaseEnum
|
||||
{
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'collectionNeeds' => 0,
|
||||
'specifications' => 1,
|
||||
'quotation' => 2,
|
||||
'tests' => 3,
|
||||
'informations' => 4,
|
||||
'feedback' => 5
|
||||
];
|
||||
}
|
||||
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'collectionNeeds' => trans('Recueil des besoins'),
|
||||
'specifications' => trans('Cahier des charges'),
|
||||
'quotation' => trans('Devis'),
|
||||
'tests' => trans('Recettage'),
|
||||
'informations' => trans('Informations'),
|
||||
'feedback' => trans('Retours client'),
|
||||
];
|
||||
}
|
||||
}
|
||||
29
app/Enums/FileType.php
Normal file
29
app/Enums/FileType.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @method static self stringlink()
|
||||
* @method static self binfile()
|
||||
*/
|
||||
|
||||
class FileType extends BaseEnum
|
||||
{
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'stringlink' => 0,
|
||||
'binfile' => 1
|
||||
];
|
||||
}
|
||||
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'stringlink' => trans('Lien uri'),
|
||||
'binfile' => trans('Fichier hebergé')
|
||||
];
|
||||
}
|
||||
}
|
||||
35
app/Enums/LoadLevel.php
Normal file
35
app/Enums/LoadLevel.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @method static self weak()
|
||||
* @method static self stable()
|
||||
* @method static self high()
|
||||
* @method static self overload()
|
||||
*/
|
||||
|
||||
class LoadLevel extends BaseEnum
|
||||
{
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'weak' => 0,
|
||||
'stable' => 1,
|
||||
'high' => 2,
|
||||
'overload' => 3
|
||||
];
|
||||
}
|
||||
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'weak' => trans('Faible'),
|
||||
'stable' => trans('Stable'),
|
||||
'high' => trans('Élevée'),
|
||||
'overload' => trans('Surcharge')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ namespace App\Enums;
|
|||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @method static self developper()
|
||||
* @method static self admin()
|
||||
* @method static self client()
|
||||
*/
|
||||
|
|
@ -14,6 +15,7 @@ class Role extends BaseEnum
|
|||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'developper' => 0,
|
||||
'admin' => 1,
|
||||
'client' => 2
|
||||
];
|
||||
|
|
@ -22,6 +24,7 @@ class Role extends BaseEnum
|
|||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'developper' => trans('Developpeur'),
|
||||
'admin' => trans('Administrateur'),
|
||||
'client' => trans('Client')
|
||||
];
|
||||
|
|
|
|||
28
app/Events/FileCreated.php
Normal file
28
app/Events/FileCreated.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\User;
|
||||
|
||||
class FileCreated extends Loggable
|
||||
{
|
||||
|
||||
/** @var File */
|
||||
public $file;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(File $file, User $user)
|
||||
{
|
||||
parent::__construct($user);
|
||||
$this->file = $file;
|
||||
$this->message = trans(':username à crée le fichier :filename', [
|
||||
'username' => $user->name,
|
||||
'filename' => $file->name
|
||||
]);
|
||||
}
|
||||
}
|
||||
31
app/Events/Loggable.php
Normal file
31
app/Events/Loggable.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
abstract class Loggable
|
||||
{
|
||||
use Dispatchable;
|
||||
use SerializesModels;
|
||||
|
||||
/** @var User */
|
||||
public $user;
|
||||
|
||||
public $message = '';
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->message = trans(':username did someting', [
|
||||
'username' => $user->name
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ use App\Models\Ticket;
|
|||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class NotifySubscriptions
|
||||
{
|
||||
|
|
@ -19,14 +20,18 @@ class NotifySubscriptions
|
|||
/** @var string */
|
||||
public $corpsMaj;
|
||||
|
||||
/** @var Collection */
|
||||
public $ignoreUsers;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Ticket $ticket, string $corpsMaj)
|
||||
public function __construct(Ticket $ticket, string $corpsMaj, Collection $ignoreUsers = null)
|
||||
{
|
||||
$this->ticket = $ticket;
|
||||
$this->corpsMaj = $corpsMaj;
|
||||
$this->ignoreUsers = $ignoreUsers;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
32
app/Events/NotifyTicketCreator.php
Normal file
32
app/Events/NotifyTicketCreator.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class NotifyTicketCreator
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithSockets;
|
||||
use SerializesModels;
|
||||
|
||||
/** @var Ticket */
|
||||
public $ticket;
|
||||
|
||||
/** @var string */
|
||||
public $corpsMaj;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Ticket $ticket, string $corpsMaj)
|
||||
{
|
||||
$this->ticket = $ticket;
|
||||
$this->corpsMaj = $corpsMaj;
|
||||
}
|
||||
}
|
||||
28
app/Events/ProjectUpdated.php
Normal file
28
app/Events/ProjectUpdated.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\Project;
|
||||
use App\Models\User;
|
||||
|
||||
class ProjectUpdated extends Loggable
|
||||
{
|
||||
|
||||
/** @var Project */
|
||||
public $project;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Project $project, User $user)
|
||||
{
|
||||
parent::__construct($user);
|
||||
$this->project = $project;
|
||||
$this->message = trans(':username à mis à jour projet :projectname', [
|
||||
'username' => $user->name,
|
||||
'projectname' => $project->name
|
||||
]);
|
||||
}
|
||||
}
|
||||
28
app/Events/SiteUpdated.php
Normal file
28
app/Events/SiteUpdated.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\Site;
|
||||
use App\Models\User;
|
||||
|
||||
class SiteUpdated extends Loggable
|
||||
{
|
||||
|
||||
/** @var Site */
|
||||
public $site;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Site $site, User $user)
|
||||
{
|
||||
parent::__construct($user);
|
||||
$this->site = $site;
|
||||
$this->message = trans(':username à mis à jour site :sitename', [
|
||||
'username' => $user->name,
|
||||
'sitename' => $site->name
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,22 +2,28 @@
|
|||
|
||||
namespace App\Events;
|
||||
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class TicketStatusChanged
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
use Dispatchable;
|
||||
use InteractsWithSockets;
|
||||
use SerializesModels;
|
||||
|
||||
public $ticket, $status;
|
||||
/** @var Ticket */
|
||||
public $ticket;
|
||||
|
||||
public $status;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($ticket, $status)
|
||||
public function __construct(Ticket $ticket, $status)
|
||||
{
|
||||
$this->ticket = $ticket;
|
||||
$this->status = $status;
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class CrudException extends Exception
|
||||
{
|
||||
}
|
||||
39
app/Http/Controllers/Auth/ConfirmPasswordController.php
Normal file
39
app/Http/Controllers/Auth/ConfirmPasswordController.php
Normal 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');
|
||||
}
|
||||
}
|
||||
26
app/Http/Controllers/Auth/ForgotPasswordController.php
Normal file
26
app/Http/Controllers/Auth/ForgotPasswordController.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?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;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest:web');
|
||||
}
|
||||
}
|
||||
39
app/Http/Controllers/Auth/LoginController.php
Normal file
39
app/Http/Controllers/Auth/LoginController.php
Normal 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');
|
||||
}
|
||||
}
|
||||
51
app/Http/Controllers/Auth/ResetPasswordController.php
Normal file
51
app/Http/Controllers/Auth/ResetPasswordController.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Get the password reset validation rules.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function rules()
|
||||
{
|
||||
return [
|
||||
'token' => 'required',
|
||||
'email' => 'required|email',
|
||||
'password' => [
|
||||
'required', 'confirmed', Password::min(8)
|
||||
->mixedCase()
|
||||
->letters()
|
||||
->numbers()
|
||||
->symbols()
|
||||
->uncompromised()
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -9,5 +9,7 @@ use Illuminate\Routing\Controller as BaseController;
|
|||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
use AuthorizesRequests;
|
||||
use DispatchesJobs;
|
||||
use ValidatesRequests;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,241 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\CrudException;
|
||||
use App\Http\Requests\CrudIndexRequest;
|
||||
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\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
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 storedModel(Model &$model)
|
||||
{
|
||||
}
|
||||
|
||||
public function updateModel(Model &$model)
|
||||
{
|
||||
}
|
||||
|
||||
public function updatedModel(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($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(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder */
|
||||
$models = static::$modelClass::query();
|
||||
|
||||
// Search on crud Label column
|
||||
if ($request->search) {
|
||||
$model = static::$modelClass;
|
||||
$col = (new $model())->crudLabelColumn;
|
||||
$models = $models->where($col, 'LIKE', "%$request->search%");
|
||||
}
|
||||
|
||||
// Sort columns
|
||||
$tableName = (new static::$modelClass())->getTable();
|
||||
if ($request->rst) {
|
||||
Session::remove("crud.$tableName.sort_col");
|
||||
Session::remove("crud.$tableName.sort_way");
|
||||
}
|
||||
if ($request->sort_col) {
|
||||
Session::put("crud.$tableName.sort_col", $request->sort_col);
|
||||
Session::save();
|
||||
}
|
||||
if ($request->sort_way) {
|
||||
Session::put("crud.$tableName.sort_way", $request->sort_way);
|
||||
Session::save();
|
||||
}
|
||||
|
||||
if (
|
||||
Session::has("crud.$tableName.sort_col") and
|
||||
Session::has("crud.$tableName.sort_way") and
|
||||
Schema::hasColumn($tableName, Session::get("crud.$tableName.sort_col"))
|
||||
) {
|
||||
$models = $models->orderBy(
|
||||
Session::get("crud.$tableName.sort_col"),
|
||||
Session::get("crud.$tableName.sort_way")
|
||||
);
|
||||
}
|
||||
|
||||
$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);
|
||||
$this->storedModel($model);
|
||||
return \redirect()->route(sprintf('%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);
|
||||
$this->updatedModel($model);
|
||||
return \redirect()->route(sprintf('%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('%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());
|
||||
}
|
||||
}
|
||||
54
app/Http/Controllers/FileController.php
Normal file
54
app/Http/Controllers/FileController.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\File;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
|
||||
use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
||||
|
||||
class FileController extends Controller
|
||||
{
|
||||
use CrudController {
|
||||
index as crudIndex;
|
||||
create as crudCreate;
|
||||
}
|
||||
|
||||
/** @var File */
|
||||
public static $modelClass = File::class;
|
||||
|
||||
public static $layout = 'layout.app';
|
||||
|
||||
public function indexQuery(Builder $query): Builder
|
||||
{
|
||||
$parent = request()->site ?? request()->project;
|
||||
if ($parent) {
|
||||
$query->whereHasMorph('parent', [get_class($parent)], function ($q) use ($parent) {
|
||||
return $q->whereId($parent->id);
|
||||
});
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
$view = $this->crudIndex($request);
|
||||
if ($request->ajax()) {
|
||||
return $view;
|
||||
}
|
||||
return view('files.index', $view->getData());
|
||||
}
|
||||
|
||||
public function create(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
$view = $this->crudCreate($request);
|
||||
return view('files.create', $view->getData());
|
||||
}
|
||||
|
||||
public function storeModel(File &$file)
|
||||
{
|
||||
$file->parent()->associate(request()->site ?? request()->project);
|
||||
}
|
||||
}
|
||||
16
app/Http/Controllers/HomeController.php
Normal file
16
app/Http/Controllers/HomeController.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Parsedown;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
public function home()
|
||||
{
|
||||
$Parsedown = new Parsedown();
|
||||
$changelog = $Parsedown->text(File::get(\app_path('../CHANGELOG.md')));
|
||||
return view('home', ['changelog' => $changelog]);
|
||||
}
|
||||
}
|
||||
54
app/Http/Controllers/LogController.php
Normal file
54
app/Http/Controllers/LogController.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Log;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
|
||||
use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
||||
|
||||
class LogController extends Controller
|
||||
{
|
||||
use CrudController {
|
||||
index as crudIndex;
|
||||
}
|
||||
|
||||
/** @var Log */
|
||||
public static $modelClass = Log::class;
|
||||
|
||||
public static $layout = 'layout.app';
|
||||
|
||||
public function indexQuery(Builder $query): Builder
|
||||
{
|
||||
$parent = request()->site ?? request()->project;
|
||||
if ($parent) {
|
||||
$query->whereHasMorph('parent', [get_class($parent)], function ($q) use ($parent) {
|
||||
return $q->whereId($parent->id);
|
||||
});
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
// Autosort
|
||||
if (!$request->sort_col and !$request->sort_way) {
|
||||
$request->merge([
|
||||
'sort_col' => 'created_at',
|
||||
'sort_way' => 'desc',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @var \Illuminate\View\View */
|
||||
$view = $this->crudIndex($request);
|
||||
if ($request->ajax()) {
|
||||
return $view;
|
||||
}
|
||||
return view('logs.index', $view->getData());
|
||||
}
|
||||
|
||||
public function storeModel(Log &$log)
|
||||
{
|
||||
$log->parent()->associate(request()->site ?? request()->project);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
public function index() {
|
||||
return view('login');
|
||||
}
|
||||
|
||||
public function authenticate(Request $request) {
|
||||
if (Auth::attempt(["email" => $request->email, "password" => $request->password], $request->remember !== null ? true : false)) {
|
||||
return redirect()->route('home');
|
||||
} else {
|
||||
return back()->withInput($request->except("password"))->withErrors("Les identifiants entrés sont incorrects, veuillez réessayer.");
|
||||
}
|
||||
}
|
||||
|
||||
public function logout() {
|
||||
Auth::logout();
|
||||
return redirect()->route('home');
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,35 @@
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Project;
|
||||
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
|
||||
use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
||||
|
||||
class ProjectController extends CrudController
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
use CrudController {
|
||||
index as crudIndex;
|
||||
show as crudShow;
|
||||
}
|
||||
|
||||
/** @var Project */
|
||||
protected static $modelClass = Project::class;
|
||||
public static $modelClass = Project::class;
|
||||
|
||||
public static $layout = 'layout.app';
|
||||
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
$view = $this->crudIndex($request);
|
||||
if ($request->ajax()) {
|
||||
return $view;
|
||||
}
|
||||
return view('projects.index', $view->getData());
|
||||
}
|
||||
|
||||
public function show(Project $model)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
$view = $this->crudshow($model);
|
||||
return view('projects.show', $view->getData());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
app/Http/Controllers/SiteController.php
Normal file
29
app/Http/Controllers/SiteController.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Site;
|
||||
use Kwaadpepper\CrudPolicies\Http\Requests\CrudIndexRequest;
|
||||
use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
||||
|
||||
class SiteController extends Controller
|
||||
{
|
||||
use CrudController {
|
||||
index as crudIndex;
|
||||
}
|
||||
|
||||
/** @var Site */
|
||||
public static $modelClass = Site::class;
|
||||
|
||||
public static $layout = 'layout.app';
|
||||
|
||||
public function index(CrudIndexRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\View\View */
|
||||
$view = $this->crudIndex($request);
|
||||
if ($request->ajax()) {
|
||||
return $view;
|
||||
}
|
||||
return view('sites.index', $view->getData());
|
||||
}
|
||||
}
|
||||
|
|
@ -2,24 +2,19 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Enums\TicketStatus;
|
||||
use App\Enums\TicketType;
|
||||
use App\Events\NotifySubscriptions;
|
||||
use App\Http\Requests\ChangeTicketStatus;
|
||||
use App\Http\Requests\CreateMessage;
|
||||
use App\Http\Requests\SortTickets;
|
||||
use App\Http\Requests\StoreTicket;
|
||||
use App\Http\Requests\UpdateTicket;
|
||||
use App\Jobs\SendTicketMentionMail;
|
||||
use App\Mail\NewTicket;
|
||||
use App\Mail\TicketMention;
|
||||
use App\Models\Message;
|
||||
use App\Models\Project;
|
||||
use App\Models\Site;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
|
@ -39,9 +34,9 @@ class TicketController extends Controller
|
|||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
$tickets = Ticket::with('project');
|
||||
if (!$user->role->equals(Role::admin())) {
|
||||
$tickets = $tickets->whereHas('project', function ($q) use ($user) {
|
||||
$tickets = Ticket::with('site')->has('site');
|
||||
if (!$user->isAdmin()) {
|
||||
$tickets = $tickets->whereHas('site', function ($q) use ($user) {
|
||||
$q->whereHas('users', function ($q) use ($user) {
|
||||
$q->whereId($user->id);
|
||||
});
|
||||
|
|
@ -54,9 +49,9 @@ class TicketController extends Controller
|
|||
$tickets = $this->filterTickets($tickets, $request);
|
||||
$tickets = $tickets->paginate();
|
||||
|
||||
$projects = Project::select('id', 'name')->orderBy('name')->get();
|
||||
$sites = Site::select('id', 'name')->orderBy('name')->get();
|
||||
|
||||
return view('ticket.index', compact('tickets', 'projects'));
|
||||
return view('ticket.index', compact('tickets', 'sites'));
|
||||
}
|
||||
|
||||
private function filterTickets(Builder $tickets, Request $request): Builder
|
||||
|
|
@ -81,14 +76,14 @@ class TicketController extends Controller
|
|||
Session::save();
|
||||
|
||||
if (Session::has('tickets.pr')) {
|
||||
$tickets = $tickets->where('project_id', Session::get('tickets.pr'));
|
||||
$tickets = $tickets->where('site_id', Session::get('tickets.pr'));
|
||||
}
|
||||
|
||||
if (
|
||||
Session::has('tickets.sort_col') and
|
||||
Session::has('tickets.sort_way') and
|
||||
in_array(Session::get('tickets.sort_col'), [
|
||||
'id', 'project', 'created_at', 'created_by',
|
||||
'id', 'site_id', 'created_at', 'created_by',
|
||||
'type', 'status'
|
||||
])
|
||||
) {
|
||||
|
|
@ -119,8 +114,9 @@ class TicketController extends Controller
|
|||
})->map(function (TicketType $ticket) {
|
||||
return $ticket->value;
|
||||
});
|
||||
$q->where('text', 'LIKE', "%$word%")
|
||||
->orWhereIn('status', $s)
|
||||
$q->where('id', $word)
|
||||
->orwhere('text', 'LIKE', "%$word%")
|
||||
->orWhereIn('status', $s)
|
||||
->orWhereIn('type', $t)
|
||||
->orWhere('file', 'LIKE', "%$word%")
|
||||
->orWhere('file2', 'LIKE', "%$word%")
|
||||
|
|
@ -141,31 +137,32 @@ class TicketController extends Controller
|
|||
|
||||
public function create()
|
||||
{
|
||||
$user = Auth::user();
|
||||
if ($user->role->value === 1) {
|
||||
$projects = Project::orderBy('name')->get();
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if ($user->isAdmin()) {
|
||||
$sites = Site::orderBy('name')->get();
|
||||
} else {
|
||||
$projects = $user->projects;
|
||||
$sites = $user->sites;
|
||||
}
|
||||
|
||||
if (!count($projects)) {
|
||||
return redirect()->route('project.create')
|
||||
->with('warning', 'Vous devez tout d\'abord créer un projet pour y ajouter un ticket');
|
||||
if (!count($sites)) {
|
||||
return redirect()->route('sites.create')
|
||||
->with('warning', 'Vous devez tout d\'abord créer un site pour y ajouter un ticket');
|
||||
}
|
||||
return view('ticket.create', compact('projects'));
|
||||
return view('ticket.create', compact('sites'));
|
||||
}
|
||||
|
||||
public function store(StoreTicket $request)
|
||||
{
|
||||
$project = Project::findOrFail($request->project_id);
|
||||
$site = Site::findOrFail($request->site_id);
|
||||
|
||||
$ticket = new Ticket($request->validated());
|
||||
|
||||
if ($request->file_ticket) {
|
||||
$ticket->file = Ticket::saveFile($project, $ticket, $ticket->type, $request->file_ticket);
|
||||
$ticket->file = Ticket::saveFile($site, $ticket, $ticket->type, $request->file_ticket);
|
||||
}
|
||||
if ($request->file_ticket2) {
|
||||
$ticket->file2 = Ticket::saveFile($project, $ticket, $ticket->type, $request->file_ticket2);
|
||||
$ticket->file2 = Ticket::saveFile($site, $ticket, $ticket->type, $request->file_ticket2);
|
||||
}
|
||||
|
||||
if ($request->type === TicketType::evolution()) {
|
||||
|
|
@ -190,31 +187,32 @@ class TicketController extends Controller
|
|||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
public function edit(Ticket $ticket)
|
||||
{
|
||||
$user = Auth::user();
|
||||
if ($user->role->value === 1) {
|
||||
$projects = Project::orderBy('name')->get();
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if ($user->isAdmin()) {
|
||||
$sites = Site::orderBy('name')->get();
|
||||
} else {
|
||||
$projects = $user->projects;
|
||||
$sites = $user->sites;
|
||||
}
|
||||
return view('ticket.edit', compact('ticket', 'projects'));
|
||||
return view('ticket.edit', compact('ticket', 'sites'));
|
||||
}
|
||||
|
||||
public function update(UpdateTicket $request, Ticket $ticket)
|
||||
{
|
||||
$project = Project::findOrFail($request->project_id);
|
||||
$site = Site::findOrFail($request->site_id);
|
||||
$ticket->fill($request->validated());
|
||||
|
||||
if ($request->file_ticket) {
|
||||
if (File::exists($ticket->file)) {
|
||||
File::delete($ticket->file);
|
||||
}
|
||||
$ticket->file = Ticket::saveFile($project, $ticket, $ticket->type, $request->file_ticket);
|
||||
$ticket->file = Ticket::saveFile($site, $ticket, $ticket->type, $request->file_ticket);
|
||||
}
|
||||
if ($request->file_ticket2) {
|
||||
if (File::exists($ticket->file2)) {
|
||||
File::delete($ticket->file2);
|
||||
}
|
||||
$ticket->file2 = Ticket::saveFile($project, $ticket, $ticket->type, $request->file_ticket2);
|
||||
$ticket->file2 = Ticket::saveFile($site, $ticket, $ticket->type, $request->file_ticket2);
|
||||
}
|
||||
|
||||
$ticket->save();
|
||||
|
|
@ -237,11 +235,11 @@ class TicketController extends Controller
|
|||
$message = new Message($request->validated());
|
||||
$message->user()->associate(auth()->user());
|
||||
$message->ticket()->associate($ticket);
|
||||
$message->saveOrFail();
|
||||
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
|
||||
// Toggle auth user subscription
|
||||
if ($request->subscribe) {
|
||||
if (!$user->subscriptions()->find($ticket->id)) {
|
||||
$user->subscriptions()->attach($ticket->id);
|
||||
|
|
@ -251,41 +249,16 @@ class TicketController extends Controller
|
|||
}
|
||||
|
||||
if ($request->file_message) {
|
||||
$projectSlug = Str::slug($ticket->project->name);
|
||||
$siteSlug = Str::slug($ticket->site->name);
|
||||
$fileMessage = $request->file_message;
|
||||
$fileName = $projectSlug . '-ticket-' . $ticket->id . '-message-' . $message->id . '.' . $fileMessage->extension();
|
||||
$fileMessage->move(public_path('fichiers/' . $projectSlug . '/' . $ticket->id . '/'), $fileName);
|
||||
$filePath = 'fichiers/' . $projectSlug . '/' . $ticket->id . '/' . $fileName;
|
||||
$fileName = $siteSlug . '-ticket-' . $ticket->id .
|
||||
'-message-' . $message->id . '.' . $fileMessage->extension();
|
||||
$fileMessage->move(public_path('fichiers/' . $siteSlug . '/' . $ticket->id . '/'), $fileName);
|
||||
$filePath = 'fichiers/' . $siteSlug . '/' . $ticket->id . '/' . $fileName;
|
||||
$message->file = $filePath;
|
||||
$message->save();
|
||||
}
|
||||
|
||||
event(new NotifySubscriptions(
|
||||
$message->ticket,
|
||||
trans('Nouvelle réponse de :name :<br> :message', [
|
||||
'name' => $message->user->name,
|
||||
'message' => $message->message
|
||||
])
|
||||
));
|
||||
|
||||
// find @user + send mail si un seul user est trouvé
|
||||
$matches = [];
|
||||
\preg_match_all('/@\w*/', $message->message, $matches);
|
||||
$matches = collect($matches[0] ?? [])->unique();
|
||||
foreach ($matches as $pseudo_usertag) {
|
||||
try {
|
||||
$user = User::where('name', 'LIKE', '%' . ltrim($pseudo_usertag, '@') . '%')
|
||||
->whereDoesntHave('subscriptions', function ($q) use ($ticket) {
|
||||
$q->where('id', $ticket->id);
|
||||
})->firstOrFail();
|
||||
SendTicketMentionMail::dispatchAfterResponse(
|
||||
$user,
|
||||
new TicketMention($ticket, $message->message)
|
||||
);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$message->saveOrFail();
|
||||
|
||||
return back();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,33 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Auth\ForgotPasswordController;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Request;
|
||||
use Kwaadpepper\CrudPolicies\Traits\CrudController;
|
||||
|
||||
class UserController extends CrudController
|
||||
class UserController extends Controller
|
||||
{
|
||||
use CrudController;
|
||||
|
||||
/** @var User */
|
||||
protected static $modelClass = User::class;
|
||||
public static $modelClass = User::class;
|
||||
|
||||
public static $layout = 'layout.app';
|
||||
|
||||
public function storedModel(Model &$model)
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$model = $model;
|
||||
$model->sendMailConfirm();
|
||||
}
|
||||
|
||||
public function passwordResetSubmit()
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
$request = new Request(['email' => $user->email]);
|
||||
return app(ForgotPasswordController::class)->sendResetLinkEmail($request);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
|||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
|
|
@ -15,7 +16,6 @@ class Kernel extends HttpKernel
|
|||
*/
|
||||
protected $middleware = [
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Fruitcake\Cors\HandleCors::class,
|
||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
|
|
@ -33,7 +33,6 @@ class Kernel extends HttpKernel
|
|||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ namespace App\Http\Middleware;
|
|||
use App\Enums\Role;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class IsAdmin
|
||||
{
|
||||
|
|
@ -18,7 +17,9 @@ class IsAdmin
|
|||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (Auth::user()->role->equals(Role::admin())) {
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if ($user->isAdmin()) {
|
||||
return $next($request);
|
||||
}
|
||||
return redirect()->route('home');
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use Illuminate\Http\Request;
|
|||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
|
||||
/**
|
||||
* The trusted proxies for this application.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ class ChangeTicketStatus extends FormRequest
|
|||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check() and
|
||||
auth()->user()->role->equals(Role::admin());
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
return auth()->check() and $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@ class CreateMessage extends FormRequest
|
|||
*/
|
||||
public function authorize()
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
$this->merge([
|
||||
'subscribe' => $this->subscribe ? true : false
|
||||
]);
|
||||
return auth()->check() and (auth()->user()->role->equals(Role::admin()) or
|
||||
auth()->user()->projects->contains(request()->route()->ticket->project_id));
|
||||
return auth()->check() and ($user->isAdmin() or
|
||||
auth()->user()->sites->contains(request()->route()->ticket->site_id));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -28,9 +30,11 @@ class CreateMessage extends FormRequest
|
|||
*/
|
||||
public function rules()
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
return [
|
||||
'subscribe' => 'required|boolean',
|
||||
'message' => 'required|min:' . (auth()->user()->role->equals(Role::admin()) ? '5' : '20'),
|
||||
'message' => 'required|min:' . ($user->isAdmin() ? '5' : '20'),
|
||||
'file_message' => 'sometimes|mimes:jpeg,jpg,png,pdf|max:2048'
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CrudIndexRequest 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 [
|
||||
'search' => 'nullable|string'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
<?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();
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ class SortTickets extends FormRequest
|
|||
}
|
||||
}
|
||||
],
|
||||
'pr' => 'sometimes|int|exists:projects,id'
|
||||
'pr' => 'sometimes|int|exists:sites,id'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
<?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();
|
||||
$authorized = auth()->check() and $user->can('create', $model);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Enums\Role;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreProject extends FormRequest
|
||||
class StoreSite extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
|
|
@ -14,8 +13,9 @@ class StoreProject extends FormRequest
|
|||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check() and
|
||||
auth()->user()->role->equals(Role::admin());
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
return auth()->check() and $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Enums\TicketStatus;
|
||||
use App\Enums\TicketType;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
|
@ -15,6 +16,13 @@ class StoreTicket extends FormRequest
|
|||
*/
|
||||
public function authorize()
|
||||
{
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if (!$user->isAdmin()) {
|
||||
$this->merge([
|
||||
'status' => TicketStatus::new()->value,
|
||||
]);
|
||||
}
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
|
|
@ -26,7 +34,23 @@ class StoreTicket extends FormRequest
|
|||
public function rules()
|
||||
{
|
||||
return [
|
||||
'project_id' => 'required|exists:projects,id',
|
||||
'site_id' => [
|
||||
'required',
|
||||
'exists:sites,id',
|
||||
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
function ($attribute, $value, $fail) {
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if ($user->isAdmin()) {
|
||||
return;
|
||||
}
|
||||
if (!in_array($value, $user->sites()->pluck('id')->all())) {
|
||||
$fail(__('Veuillez choisir entre :values', [
|
||||
'values' => $user->sites()->pluck('name')->implode(',')
|
||||
]));
|
||||
}
|
||||
}
|
||||
],
|
||||
'type' => [
|
||||
'required',
|
||||
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
<?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();
|
||||
$authorized = auth()->check() and $user->can('update', $model);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
class UpdateProject extends StoreProject
|
||||
class UpdateSite extends StoreSite
|
||||
{
|
||||
}
|
||||
|
|
@ -9,6 +9,13 @@ class UpdateTicket extends StoreTicket
|
|||
{
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check() and (auth()->user()->role->equals(Role::admin()));
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
if (!$user->isAdmin()) {
|
||||
$this->merge(['status' => $this->site->status->value,
|
||||
]);
|
||||
}
|
||||
return auth()->check() and ($user->isAdmin() or
|
||||
request()->route()->ticket->created_by === auth()->user()->id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
<?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
|
||||
];
|
||||
}
|
||||
}
|
||||
44
app/Jobs/SendUserCreatedEmail.php
Normal file
44
app/Jobs/SendUserCreatedEmail.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Mail\UserCreatedMail;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendUserCreatedEmail implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
|
||||
/** @var User */
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user->withoutRelations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Mail::to(['email' => $this->user->email])
|
||||
->send(new UserCreatedMail($this->user));
|
||||
Log::debug(sprintf('Sent %s to %s', UserCreatedMail::class, $this->user->email));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
<?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) : ''
|
||||
]);
|
||||
if (config('app.policyDebug')) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace App\Lib;
|
||||
|
||||
use App\Enums\LoadLevel;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
||||
class Utils
|
||||
{
|
||||
public static function plainToHtml(string $text)
|
||||
|
|
@ -9,6 +12,7 @@ class Utils
|
|||
$text = e($text);
|
||||
// url replace and email replace
|
||||
$text = preg_replace_callback(
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
'/(?P<email>[^\s@]+@([^\s@.,]+\.)+[^\s@.,]{2,})|(?P<user>@\w*)|(?P<url>(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/',
|
||||
function ($matches) {
|
||||
$match = collect($matches)->first() ?? '';
|
||||
|
|
@ -38,4 +42,69 @@ class Utils
|
|||
$m = [];
|
||||
return \preg_match('/(\.png|\.jpg|\.jpeg|\.gif)$/', $url, $m) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* delais 7 jours 168h
|
||||
* travaillé 5 jours 37h
|
||||
* temps tickets 1 jours 7.4h
|
||||
*
|
||||
* personnes 4 (TpP) 3.7h / semaine * prs
|
||||
*
|
||||
* délais moyen ticket (Dmt) 0.75h
|
||||
*
|
||||
* délais max attente pour 1 ticket (Dam) 7jours
|
||||
*
|
||||
* niveaux de charge 4: - faible
|
||||
* - stable
|
||||
* - élevée
|
||||
* - saturation
|
||||
*
|
||||
* états tickets(Et) : - nouveaux (non traités)
|
||||
* - en attente (charge pendante)
|
||||
* - traités
|
||||
*
|
||||
* Algo : linéaire
|
||||
*
|
||||
* TpP : 3.7h
|
||||
* P : 4
|
||||
*
|
||||
* Et(nvx) * Dam
|
||||
*
|
||||
* Nv Charge = (P * TpP) / (Et(nvx) * Dam) - (P * TpP) / (Et(nvx) * DmT) - (Et(att) * DmT)
|
||||
*
|
||||
*
|
||||
* Si nv Charge < 0 => saturation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function computeWorkload(): LoadLevel
|
||||
{
|
||||
$Tnew = View::shared('ticketsNew');
|
||||
$Tprogress = View::shared('ticketsInProgress');
|
||||
$Twaiting = View::shared('ticketsWaiting');
|
||||
|
||||
$worked = config('tickets.workedHoursInAWeek');
|
||||
$workedDays = config('tickets.workedDaysInAWeek');
|
||||
$morningWorkTime = ($worked / $workedDays / 2); // in hours
|
||||
$workingDevsPerWeek = config('tickets.workingDevsPerWeek'); // Devs handlings tickets per week
|
||||
$timePerWeekDev = $workingDevsPerWeek * $morningWorkTime; // Worktime per week to handle tickets
|
||||
|
||||
$avgTimeForTicket = config('tickets.avgTimeForTicket'); // 45min for a ticket
|
||||
|
||||
$todoListDelay = $Tnew * $avgTimeForTicket; // The time needed to handle new ticket (average)
|
||||
$waitingListDelay = $Twaiting * $avgTimeForTicket; // The time needed to handle new ticket (average)
|
||||
$progressListDelay = $Tprogress * $avgTimeForTicket; // The time needed to handle new ticket (average)
|
||||
|
||||
$totalTicketsDelay = $todoListDelay + $progressListDelay + $waitingListDelay;
|
||||
$ETAForAnewTicket = $totalTicketsDelay / $timePerWeekDev; // in weeks
|
||||
|
||||
switch ((int)round($ETAForAnewTicket)) {
|
||||
case LoadLevel::weak()->value:
|
||||
case LoadLevel::stable()->value:
|
||||
case LoadLevel::high()->value:
|
||||
return LoadLevel::make((int)round($ETAForAnewTicket));
|
||||
default:
|
||||
return LoadLevel::overload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,16 +7,6 @@ use App\Models\Message;
|
|||
|
||||
class AddStatusMessages
|
||||
{
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
|
|
|
|||
34
app/Listeners/HandleLoggable.php
Normal file
34
app/Listeners/HandleLoggable.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Events\FileCreated;
|
||||
use App\Events\Loggable;
|
||||
use App\Events\ProjectUpdated;
|
||||
use App\Events\SiteUpdated;
|
||||
use App\Models\Log;
|
||||
|
||||
class HandleLoggable
|
||||
{
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param Loggable $loggable
|
||||
* @return void
|
||||
*/
|
||||
public function handle(Loggable $loggable)
|
||||
{
|
||||
$log = new Log(['entry' => $loggable->message]);
|
||||
$log->user()->associate($loggable->user);
|
||||
if (\get_class($loggable) === FileCreated::class) {
|
||||
$log->parent()->associate($loggable->file->parent);
|
||||
}
|
||||
if (\get_class($loggable) === SiteUpdated::class) {
|
||||
$log->parent()->associate($loggable->site);
|
||||
}
|
||||
if (\get_class($loggable) === ProjectUpdated::class) {
|
||||
$log->parent()->associate($loggable->project);
|
||||
}
|
||||
$log->saveOrFail();
|
||||
}
|
||||
}
|
||||
26
app/Listeners/SendNotificationMailToTicketCreator.php
Normal file
26
app/Listeners/SendNotificationMailToTicketCreator.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use App\Events\NotifyTicketCreator;
|
||||
use App\Mail\TicketUpdate;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendNotificationMailToTicketCreator
|
||||
{
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param NotifyTicketCreator $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(NotifyTicketCreator $event)
|
||||
{
|
||||
$ticket = $event->ticket;
|
||||
Mail::to($ticket->user->email)
|
||||
->send(new TicketUpdate($ticket, $event->corpsMaj));
|
||||
}
|
||||
}
|
||||
|
|
@ -4,13 +4,12 @@ namespace App\Listeners;
|
|||
|
||||
use App\Events\NotifySubscriptions;
|
||||
use App\Mail\TicketUpdate;
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendNotificationMail
|
||||
class SendSubscriptionsNotificationsMail
|
||||
{
|
||||
/**
|
||||
* Handle the event.
|
||||
|
|
@ -20,9 +19,15 @@ class SendNotificationMail
|
|||
*/
|
||||
public function handle(NotifySubscriptions $event)
|
||||
{
|
||||
$ignoreConnectedUserEmail = Auth::user()->email ?? '';
|
||||
$ignoreUsers = $event->ignoreUsers ?? collect();
|
||||
foreach ($event->ticket->subscriptions as $sub) {
|
||||
if (Auth::user() and $sub->email !== Auth::user()->email) {
|
||||
Mail::to($sub->email)->send(new TicketUpdate($event->ticket, $event->corpsMaj));
|
||||
if (
|
||||
$sub->email !== $ignoreConnectedUserEmail and
|
||||
!$ignoreUsers->contains('email', $sub->email)
|
||||
) {
|
||||
Mail::to($sub->email)
|
||||
->send(new TicketUpdate($event->ticket, $event->corpsMaj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,8 @@ use Illuminate\Queue\SerializesModels;
|
|||
|
||||
class NewTicket extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
private $ticket;
|
||||
|
||||
|
|
@ -31,6 +32,7 @@ class NewTicket extends Mailable
|
|||
public function build()
|
||||
{
|
||||
$ticket = $this->ticket;
|
||||
return $this->subject("[SupportWeb] Nouveau ticket n°" . $ticket->id)->view('emails.new-ticket', compact('ticket'));
|
||||
return $this->subject("[SupportWeb] Nouveau ticket n°" . $ticket->id)
|
||||
->view('emails.new-ticket', compact('ticket'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ class TicketUpdate extends Mailable
|
|||
{
|
||||
$ticket = $this->ticket;
|
||||
$corpsMaj = Utils::plainToHtml($this->corpsMaj);
|
||||
return $this->subject("[SupportWeb] Le ticket n°" . $ticket->id . " à été mis à jour")->view('emails.ticket-update', compact('ticket', 'corpsMaj'));
|
||||
return $this->subject("[SupportWeb] Le ticket n°" . $ticket->id . " à été mis à jour")
|
||||
->view('emails.ticket-update', compact('ticket', 'corpsMaj'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
37
app/Mail/UserCreatedMail.php
Normal file
37
app/Mail/UserCreatedMail.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Mail\Mailable;
|
||||
|
||||
class UserCreatedMail extends Mailable
|
||||
{
|
||||
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this
|
||||
->subject(sprintf('%s : Votre compte a été crée', config('app.name')))
|
||||
->view('emails.mail-created')
|
||||
->text('emails.mail-created-plain')
|
||||
->with('user', $this->user)
|
||||
->with('pwd', $this->user->tmpPass);
|
||||
}
|
||||
}
|
||||
135
app/Models/File.php
Normal file
135
app/Models/File.php
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\FileCategory;
|
||||
use App\Enums\FileType;
|
||||
use App\Events\FileCreated;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudType;
|
||||
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
class File extends Model
|
||||
{
|
||||
use CastsEnums;
|
||||
use HasFactory;
|
||||
use IsCrudModel;
|
||||
|
||||
protected $enumCasts = [
|
||||
'category' => FileCategory::class,
|
||||
'type' => FileType::class
|
||||
];
|
||||
|
||||
protected $crudLabelColumn = 'name';
|
||||
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
protected $fillable = ['type', 'uri', 'file', 'name', 'category'];
|
||||
|
||||
public function __construct($attributes = [])
|
||||
{
|
||||
self::$editableProperties = [
|
||||
'name' => [
|
||||
'label' => trans('Nom'),
|
||||
'type' => CrudType::string(),
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view(),
|
||||
CrudAction::update(),
|
||||
CrudAction::create()
|
||||
]
|
||||
],
|
||||
'type' => [
|
||||
'label' => trans('Type'),
|
||||
'type' => CrudType::enum(),
|
||||
'enum' => FileType::class,
|
||||
'required' => true,
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view(),
|
||||
CrudAction::create()
|
||||
]
|
||||
],
|
||||
'category' => [
|
||||
'label' => trans('Categorie'),
|
||||
'type' => CrudType::enum(),
|
||||
'enum' => FileCategory::class,
|
||||
'required' => true,
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view(),
|
||||
CrudAction::update(),
|
||||
CrudAction::create()
|
||||
]
|
||||
],
|
||||
'uri' => [
|
||||
'label' => trans('Lien uri'),
|
||||
'type' => CrudType::uri(),
|
||||
'required' => false,
|
||||
'nullable' => true,
|
||||
'validate' => [
|
||||
'required_without:file',
|
||||
'nullable'
|
||||
],
|
||||
'actions' => [
|
||||
CrudAction::view(),
|
||||
CrudAction::create()
|
||||
]
|
||||
],
|
||||
'file' => [
|
||||
'label' => trans('Fichier'),
|
||||
'type' => CrudType::file(),
|
||||
'required' => false,
|
||||
'nullable' => true,
|
||||
'validate' => [
|
||||
'required_without:uri',
|
||||
'nullable',
|
||||
'mimes:pdf',
|
||||
'max:5120'
|
||||
],
|
||||
'accept' => 'application/pdf',
|
||||
'actions' => [
|
||||
CrudAction::view(),
|
||||
CrudAction::create()
|
||||
]
|
||||
],
|
||||
'created_at' => [
|
||||
'label' => trans('Créé le'),
|
||||
'type' => CrudType::string(),
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view()
|
||||
]
|
||||
]
|
||||
];
|
||||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function (File $file) {
|
||||
event(new FileCreated($file, \auth()->user()));
|
||||
});
|
||||
}
|
||||
|
||||
public function parent()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function parentSite()
|
||||
{
|
||||
return $this->morphTo('parent', 'site');
|
||||
}
|
||||
|
||||
public function parentProject()
|
||||
{
|
||||
return $this->morphTo('parent', 'project');
|
||||
}
|
||||
}
|
||||
83
app/Models/Log.php
Normal file
83
app/Models/Log.php
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudType;
|
||||
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
|
||||
|
||||
class Log extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use IsCrudModel;
|
||||
|
||||
protected $crudLabelColumn = 'id';
|
||||
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
protected $fillable = ['entry'];
|
||||
|
||||
public function __construct($attributes = [])
|
||||
{
|
||||
self::$editableProperties = [
|
||||
'id' => [
|
||||
'label' => trans('ID'),
|
||||
'type' => CrudType::unsignedint(),
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view()
|
||||
]
|
||||
],
|
||||
'entry' => [
|
||||
'label' => trans('Entrée'),
|
||||
'type' => CrudType::text(),
|
||||
'required' => true,
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'created_at' => [
|
||||
'label' => trans('Créé le'),
|
||||
'type' => CrudType::string(),
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view()
|
||||
]
|
||||
]
|
||||
];
|
||||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (Log $log) {
|
||||
$log->user()->associate(auth()->user());
|
||||
});
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function parent()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function parentSite()
|
||||
{
|
||||
return $this->morphTo('parent', 'site');
|
||||
}
|
||||
|
||||
public function parentProject()
|
||||
{
|
||||
return $this->morphTo('parent', 'project');
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\TicketType;
|
||||
use App\Events\NotifySubscriptions;
|
||||
use App\Events\NotifyTicketCreator;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
|
|
@ -16,6 +16,41 @@ class Message extends Model
|
|||
'message'
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function (Message $message) {
|
||||
$ticket = $message->ticket;
|
||||
if ($message->user) {
|
||||
$ticket->load('user');
|
||||
if ($ticket->user !== $message->user) {
|
||||
event(new NotifyTicketCreator(
|
||||
$ticket,
|
||||
trans('Nouvelle réponse de :name :<br> :message sur votre ticket', [
|
||||
'name' => $message->user->name,
|
||||
'message' => $message->message
|
||||
])
|
||||
));
|
||||
}
|
||||
$usersNotified = $ticket->sendBeenMentionnedToUser($message);
|
||||
event(new NotifySubscriptions(
|
||||
$message->ticket,
|
||||
trans('Nouvelle réponse de :name :<br> :message', [
|
||||
'name' => $message->user->name,
|
||||
'message' => $message->message
|
||||
]),
|
||||
$usersNotified
|
||||
));
|
||||
} else {
|
||||
event(new NotifyTicketCreator(
|
||||
$ticket,
|
||||
trans('Votre ticket : :message', [
|
||||
'message' => $message->message
|
||||
])
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function ticket()
|
||||
{
|
||||
return $this->belongsTo(Ticket::class, 'ticket_id');
|
||||
|
|
@ -26,18 +61,18 @@ class Message extends Model
|
|||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
|
||||
public function saveFile(Project $project, Ticket $ticket, UploadedFile $file)
|
||||
public function saveFile(Site $site, Ticket $ticket, UploadedFile $file)
|
||||
{
|
||||
$projectAssetFullPath = Project::ticketAssetsFullPath($project->slug_name);
|
||||
$fileAssetPath = Project::ticketAssetsPath($project->slug_name);
|
||||
$siteAssetFullPath = Site::ticketAssetsFullPath($site->slug_name);
|
||||
$fileAssetPath = Site::ticketAssetsPath($site->slug_name);
|
||||
$fileName = sprintf(
|
||||
'%s-%s-%d-%d.%s',
|
||||
$project->slug_name,
|
||||
$site->slug_name,
|
||||
$ticket->id,
|
||||
rand(),
|
||||
$file->extension()
|
||||
);
|
||||
$file->move(sprintf('%s/messages/%d', $projectAssetFullPath, $ticket->id), $fileName);
|
||||
$file->move(sprintf('%s/messages/%d', $siteAssetFullPath, $ticket->id), $fileName);
|
||||
return sprintf('%s/%d/%s', $fileAssetPath, $ticket->id, $fileName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\CrudAction;
|
||||
use App\Enums\CrudType;
|
||||
use App\Enums\Role;
|
||||
use App\Traits\IsCrudModel;
|
||||
use App\Events\ProjectUpdated;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudType;
|
||||
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
|
||||
|
||||
class Project extends Model
|
||||
{
|
||||
|
|
@ -20,7 +18,10 @@ class Project extends Model
|
|||
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
protected $fillable = ['name'];
|
||||
protected $fillable = [
|
||||
'name', 'status',
|
||||
'description', 'todo', 'sites'
|
||||
];
|
||||
|
||||
public function __construct($attributes = [])
|
||||
{
|
||||
|
|
@ -41,20 +42,66 @@ class Project extends Model
|
|||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'users' => [
|
||||
'label' => trans('Utilisateurs'),
|
||||
'type' => CrudType::belongsToMany(),
|
||||
'belongsToMany' => User::class,
|
||||
'belongsToManyLabel' => 'name',
|
||||
'readonly' => function () {
|
||||
return auth()->user()->role->equals(Role::admin()) ? false : true;
|
||||
},
|
||||
'status' => [
|
||||
'label' => trans('État'),
|
||||
'type' => CrudType::string(),
|
||||
'validate' => [
|
||||
'string',
|
||||
'max:255'
|
||||
],
|
||||
'required' => true,
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'description' => [
|
||||
'label' => trans('Description'),
|
||||
'type' => CrudType::text(),
|
||||
'validate' => [
|
||||
'string'
|
||||
],
|
||||
'required' => true,
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'todo' => [
|
||||
'label' => trans('À Faire'),
|
||||
'type' => CrudType::text(),
|
||||
'validate' => [
|
||||
'string'
|
||||
],
|
||||
'required' => false,
|
||||
'nullable' => true,
|
||||
'actions' => [
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'sites' => [
|
||||
'label' => trans('Sites'),
|
||||
'type' => CrudType::belongsToMany(),
|
||||
'belongsToMany' => Site::class,
|
||||
'belongsToManyLabel' => 'name',
|
||||
'nullable' => true,
|
||||
'readonly' => function () {
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
return !$user->isAdmin();
|
||||
},
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
]
|
||||
]
|
||||
];
|
||||
parent::__construct($attributes);
|
||||
|
|
@ -62,52 +109,23 @@ class Project extends Model
|
|||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function (Project $project) {
|
||||
$path = Project::ticketAssetsFullPath($project->slug_name);
|
||||
if (!File::exists($path)) {
|
||||
File::makeDirectory($path);
|
||||
}
|
||||
});
|
||||
static::updating(function (Project $project) {
|
||||
$oldName = Project::where('id', $project->id)->first()->name;
|
||||
$oldPath = Project::ticketAssetsFullPath($oldName);
|
||||
$newPath = Project::ticketAssetsFullPath($project->slug_name);
|
||||
if (File::exists($oldPath)) {
|
||||
File::move($oldPath, $newPath);
|
||||
} else {
|
||||
File::makeDirectory($newPath);
|
||||
}
|
||||
});
|
||||
static::deleted(function (Project $project) {
|
||||
$path = Project::ticketAssetsFullPath($project->slug_name);
|
||||
if (File::exists($path)) {
|
||||
File::deleteDirectory($path);
|
||||
}
|
||||
static::updated(function (Project $project) {
|
||||
event(new ProjectUpdated($project, \auth()->user()));
|
||||
});
|
||||
}
|
||||
|
||||
public function users()
|
||||
public function sites()
|
||||
{
|
||||
return $this->belongsToMany(User::class);
|
||||
return $this->belongsToMany(Site::class, 'project_site');
|
||||
}
|
||||
|
||||
public function tickets()
|
||||
public function logs()
|
||||
{
|
||||
return $this->hasMany(Ticket::class, 'project_id');
|
||||
return $this->morphMany(Log::class, 'parent');
|
||||
}
|
||||
|
||||
public function getSlugNameAttribute()
|
||||
public function files()
|
||||
{
|
||||
return Str::slug($this->name);
|
||||
}
|
||||
|
||||
public static function ticketAssetsFullPath(string $str)
|
||||
{
|
||||
return public_path(self::ticketAssetsPath($str));
|
||||
}
|
||||
|
||||
public static function ticketAssetsPath(string $str)
|
||||
{
|
||||
return sprintf('fichiers/%s', $str);
|
||||
return $this->morphMany(File::class, 'parent');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
app/Models/ProjetSite.php
Normal file
10
app/Models/ProjetSite.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class ProjetSite extends Pivot
|
||||
{
|
||||
//
|
||||
}
|
||||
200
app/Models/Site.php
Normal file
200
app/Models/Site.php
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Events\SiteUpdated;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudType;
|
||||
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
|
||||
|
||||
class Site extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use IsCrudModel;
|
||||
use SoftDeletes;
|
||||
|
||||
protected $crudLabelColumn = 'name';
|
||||
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
protected $fillable = [
|
||||
'name', 'status',
|
||||
'description', 'todo'
|
||||
];
|
||||
|
||||
public function __construct($attributes = [])
|
||||
{
|
||||
self::$editableProperties = [
|
||||
'name' => [
|
||||
'label' => trans('Nom'),
|
||||
'type' => CrudType::string(),
|
||||
'validate' => [
|
||||
'string',
|
||||
'max:255'
|
||||
],
|
||||
'required' => true,
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'status' => [
|
||||
'label' => trans('État'),
|
||||
'type' => CrudType::string(),
|
||||
'validate' => [
|
||||
'string',
|
||||
'max:255'
|
||||
],
|
||||
'required' => true,
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'description' => [
|
||||
'label' => trans('Description'),
|
||||
'type' => CrudType::text(),
|
||||
'validate' => [
|
||||
'string'
|
||||
],
|
||||
'required' => true,
|
||||
'nullable' => false,
|
||||
'actions' => [
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'todo' => [
|
||||
'label' => trans('À Faire'),
|
||||
'type' => CrudType::text(),
|
||||
'validate' => [
|
||||
'string'
|
||||
],
|
||||
'required' => false,
|
||||
'nullable' => true,
|
||||
'actions' => [
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'users' => [
|
||||
'label' => trans('Utilisateurs'),
|
||||
'type' => CrudType::belongsToMany(),
|
||||
'belongsToMany' => User::class,
|
||||
'belongsToManyLabel' => 'name',
|
||||
'nullable' => true,
|
||||
'readonly' => function () {
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
return !$user->isAdmin();
|
||||
},
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'projects' => [
|
||||
'label' => trans('Projets'),
|
||||
'type' => CrudType::belongsToMany(),
|
||||
'belongsToMany' => Project::class,
|
||||
'belongsToManyLabel' => 'name',
|
||||
'nullable' => true,
|
||||
'readonly' => function () {
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
return !$user->isAdmin();
|
||||
},
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
CrudAction::view(),
|
||||
CrudAction::create(),
|
||||
CrudAction::update()
|
||||
]
|
||||
]
|
||||
];
|
||||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function (Site $site) {
|
||||
$path = Site::ticketAssetsFullPath($site->slug_name);
|
||||
if (!File::exists($path)) {
|
||||
File::makeDirectory($path, 0755, true);
|
||||
}
|
||||
});
|
||||
static::updating(function (Site $site) {
|
||||
$oldName = Site::where('id', $site->id)->first()->name;
|
||||
$oldPath = Site::ticketAssetsFullPath($oldName);
|
||||
$newPath = Site::ticketAssetsFullPath($site->slug_name);
|
||||
if (File::exists($oldPath)) {
|
||||
File::move($oldPath, $newPath);
|
||||
} elseif (!File::exists($newPath)) {
|
||||
File::makeDirectory($newPath, 0755, true);
|
||||
}
|
||||
});
|
||||
static::updated(function (Site $site) {
|
||||
event(new SiteUpdated($site, \auth()->user()));
|
||||
});
|
||||
static::deleted(function (Site $site) {
|
||||
$path = Site::ticketAssetsFullPath($site->slug_name);
|
||||
if (File::exists($path)) {
|
||||
File::deleteDirectory($path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function users()
|
||||
{
|
||||
return $this->belongsToMany(User::class);
|
||||
}
|
||||
|
||||
public function tickets()
|
||||
{
|
||||
return $this->hasMany(Ticket::class, 'site_id');
|
||||
}
|
||||
|
||||
public function projects()
|
||||
{
|
||||
return $this->belongsToMany(Project::class);
|
||||
}
|
||||
|
||||
public function logs()
|
||||
{
|
||||
return $this->morphMany(Log::class, 'parent');
|
||||
}
|
||||
|
||||
public function files()
|
||||
{
|
||||
return $this->morphMany(File::class, 'parent');
|
||||
}
|
||||
|
||||
public function getSlugNameAttribute()
|
||||
{
|
||||
return Str::slug($this->name);
|
||||
}
|
||||
|
||||
public static function ticketAssetsFullPath(string $str)
|
||||
{
|
||||
return public_path(self::ticketAssetsPath($str));
|
||||
}
|
||||
|
||||
public static function ticketAssetsPath(string $str)
|
||||
{
|
||||
return sprintf('fichiers/%s', $str);
|
||||
}
|
||||
}
|
||||
10
app/Models/SiteFile.php
Normal file
10
app/Models/SiteFile.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class SiteFile extends Pivot
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ namespace App\Models;
|
|||
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
class ProjectUser extends Pivot
|
||||
class SiteUser extends Pivot
|
||||
{
|
||||
|
||||
public $timestamps = false;
|
||||
|
|
@ -2,13 +2,18 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Enums\TicketStatus;
|
||||
use App\Enums\TicketType;
|
||||
use App\Events\NotifySubscriptions;
|
||||
use App\Events\TicketStatusChanged;
|
||||
use App\Jobs\SendTicketMentionMail;
|
||||
use App\Mail\TicketMention;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Collection;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
class Ticket extends Model
|
||||
|
|
@ -22,7 +27,7 @@ class Ticket extends Model
|
|||
];
|
||||
|
||||
protected $fillable = [
|
||||
'project_id',
|
||||
'site_id',
|
||||
'type',
|
||||
'status',
|
||||
'text'
|
||||
|
|
@ -48,9 +53,9 @@ class Ticket extends Model
|
|||
});
|
||||
}
|
||||
|
||||
public function project()
|
||||
public function site()
|
||||
{
|
||||
return $this->belongsTo(Project::class);
|
||||
return $this->belongsTo(Site::class);
|
||||
}
|
||||
|
||||
public function user()
|
||||
|
|
@ -68,19 +73,57 @@ class Ticket extends Model
|
|||
return $this->belongsToMany(User::class, 'subscriptions');
|
||||
}
|
||||
|
||||
public static function saveFile(Project $project, Ticket $ticket, TicketType $type, UploadedFile $file)
|
||||
public static function saveFile(Site $site, Ticket $ticket, TicketType $type, UploadedFile $file)
|
||||
{
|
||||
$projectAssetFullPath = Project::ticketAssetsFullPath($project->slug_name);
|
||||
$fileAssetPath = Project::ticketAssetsPath($project->slug_name);
|
||||
$siteAssetFullPath = Site::ticketAssetsFullPath($site->slug_name);
|
||||
$fileAssetPath = Site::ticketAssetsPath($site->slug_name);
|
||||
$fileName = sprintf(
|
||||
'%s-%s-%d-%d.%s',
|
||||
$project->slug_name,
|
||||
$site->slug_name,
|
||||
$type->getDefinition(),
|
||||
$ticket->id,
|
||||
rand(),
|
||||
$file->extension()
|
||||
);
|
||||
$file->move(sprintf('%s/%d', $projectAssetFullPath, $ticket->id), $fileName);
|
||||
$file->move(sprintf('%s/%d', $siteAssetFullPath, $ticket->id), $fileName);
|
||||
return sprintf('%s/%d/%s', $fileAssetPath, $ticket->id, $fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse message @user tags and send emails to concerned users
|
||||
* returns and array with to users notified
|
||||
*
|
||||
* @param Message $message
|
||||
* @return array[User]
|
||||
*/
|
||||
public function sendBeenMentionnedToUser(Message $message): Collection
|
||||
{
|
||||
$ticket = $this;
|
||||
// find @user + send mail si un seul user est trouvé
|
||||
$usersNotified = collect([]);
|
||||
$matches = [];
|
||||
$ticket->load('site');
|
||||
\preg_match_all('/@\w*/', $message->message, $matches);
|
||||
$matches = collect($matches[0] ?? [])->unique();
|
||||
foreach ($matches as $pseudo_usertag) {
|
||||
try {
|
||||
$user = User::where('name', 'LIKE', '%' . ltrim($pseudo_usertag, '@') . '%')
|
||||
->where('email', '!=', $ticket->user->email)
|
||||
->where(function ($q) use ($ticket) {
|
||||
$q->whereHas('sites', function ($q) use ($ticket) {
|
||||
$q->where('id', $ticket->site->id);
|
||||
})->orWhere('role', Role::admin()->value)
|
||||
->orWhere('role', Role::developper()->value);
|
||||
})->firstOrFail();
|
||||
$usersNotified->add($user);
|
||||
SendTicketMentionMail::dispatchAfterResponse(
|
||||
$user,
|
||||
new TicketMention($ticket, $message->message)
|
||||
);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return $usersNotified;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,21 +2,27 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\CrudAction;
|
||||
use App\Enums\CrudType;
|
||||
use App\Enums\Role;
|
||||
use App\Traits\IsCrudModel;
|
||||
use App\Jobs\SendUserCreatedEmail;
|
||||
use App\Notifications\ResetPassword;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudAction;
|
||||
use Kwaadpepper\CrudPolicies\Enums\CrudType;
|
||||
use Kwaadpepper\CrudPolicies\Traits\IsCrudModel;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasFactory;
|
||||
use Notifiable;
|
||||
use CastsEnums;
|
||||
use HasFactory;
|
||||
use IsCrudModel;
|
||||
use Notifiable;
|
||||
use SoftDeletes;
|
||||
|
||||
protected $enumCasts = [
|
||||
'role' => Role::class
|
||||
|
|
@ -26,6 +32,9 @@ class User extends Authenticatable
|
|||
|
||||
protected $crudValueColumn = 'id';
|
||||
|
||||
// stores unhashed pwd
|
||||
public $tmpPass = null;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
|
|
@ -35,6 +44,8 @@ class User extends Authenticatable
|
|||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'role',
|
||||
'sites'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -81,7 +92,19 @@ class User extends Authenticatable
|
|||
'required' => true,
|
||||
'nullable' => false,
|
||||
'validate' => [
|
||||
'max:255'
|
||||
'max:255',
|
||||
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
function ($attribute, $value, $fail) {
|
||||
$q = static::query();
|
||||
if (request()->user) {
|
||||
$q = $q->where('id', '!=', $this->id);
|
||||
}
|
||||
if ($q->where('email', $value)->exists()) {
|
||||
$fail(__(':attribute est déjà utilisé', [
|
||||
'attribute' => $attribute
|
||||
]));
|
||||
}
|
||||
}
|
||||
],
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
|
|
@ -103,13 +126,16 @@ class User extends Authenticatable
|
|||
CrudAction::update(),
|
||||
]
|
||||
],
|
||||
'projects' => [
|
||||
'sites' => [
|
||||
'label' => trans('Projets'),
|
||||
'type' => CrudType::belongsToMany(),
|
||||
'belongsToMany' => Project::class,
|
||||
'belongsToMany' => Site::class,
|
||||
'belongsToManyLabel' => 'name',
|
||||
'nullable' => true,
|
||||
'readonly' => function () {
|
||||
return auth()->user()->role->equals(Role::admin()) ? false : true;
|
||||
/** @var \App\Models\User */
|
||||
$user = auth()->user();
|
||||
return !$user->isAdmin();
|
||||
},
|
||||
'actions' => [
|
||||
CrudAction::viewAny(),
|
||||
|
|
@ -122,6 +148,24 @@ class User extends Authenticatable
|
|||
parent::__construct($attributes);
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (User $user) {
|
||||
if (\is_null($user->password)) {
|
||||
$user->password = Str::random(10);
|
||||
$user->tmpPass = $user->password;
|
||||
}
|
||||
if (Hash::needsRehash($user->password)) {
|
||||
$user->password = Hash::make($user->password);
|
||||
}
|
||||
});
|
||||
static::updating(function (User $user) {
|
||||
if (Hash::needsRehash($user->password)) {
|
||||
$user->password = Hash::make($user->password);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function tickets()
|
||||
{
|
||||
return $this->hasMany(Ticket::class, 'created_by');
|
||||
|
|
@ -137,8 +181,29 @@ class User extends Authenticatable
|
|||
return $this->belongsToMany(Ticket::class, 'subscriptions');
|
||||
}
|
||||
|
||||
public function projects()
|
||||
public function sites()
|
||||
{
|
||||
return $this->belongsToMany(Project::class);
|
||||
return $this->belongsToMany(Site::class);
|
||||
}
|
||||
|
||||
public function sendMailConfirm()
|
||||
{
|
||||
dispatch(new SendUserCreatedEmail($this));
|
||||
}
|
||||
|
||||
public function sendPasswordResetNotification($token)
|
||||
{
|
||||
$this->notify(new ResetPassword($token));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the user an admin or a developper ?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
return ($this->role->equals(Role::developper()) or
|
||||
$this->role->equals(Role::admin()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
30
app/Notifications/ResetPassword.php
Normal file
30
app/Notifications/ResetPassword.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Notifications;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
|
||||
|
||||
class ResetPassword extends ResetPasswordNotification
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
/**
|
||||
* Get the reset password notification mail message for the given URL.
|
||||
*
|
||||
* @param string $url
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
*/
|
||||
protected function buildMailMessage($url)
|
||||
{
|
||||
$message = (new MailMessage())
|
||||
->subject(trans('Reinitialisation de mot de passe'))
|
||||
->line(trans('Vous recevez cet e-mail car nous avons reçu une demande de réinitialisation de mot de passe pour votre compte.'))
|
||||
->action(trans('Réinitialiser le mot de passe'), $url)
|
||||
->line(trans('Ce lien de réinitialisation de mot de passe expirera dans :count minutes. ', ['count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')]))
|
||||
->line(trans('Si vous n\'avez pas demandé de réinitialisation de mot de passe, aucune autre action n\'est requise.'));
|
||||
$message->viewData['greeting'] = 'Bonjour,';
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
38
app/Policies/FilePolicy.php
Normal file
38
app/Policies/FilePolicy.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Policies\Rules\UserRoleRules as URR;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
|
||||
|
||||
class FilePolicy extends RootPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Init policies on call
|
||||
*
|
||||
* @param User $user
|
||||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function setPolicies(User $user, Model $model = null)
|
||||
{
|
||||
/**
|
||||
* This first rule to return true in a policy action authorises the action
|
||||
*/
|
||||
$this->policies = [
|
||||
'file' => [
|
||||
'viewAny' => URR::make('userIsAdmin', $user),
|
||||
'view' => URR::make('userIsAdmin', $user),
|
||||
'update' => URR::make('denyEveryOne'),
|
||||
'create' => URR::make('userIsAdmin', $user),
|
||||
'delete' => URR::make('userIsDev', $user)
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
43
app/Policies/LogPolicy.php
Normal file
43
app/Policies/LogPolicy.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Policies\Rules\LogStaticRules;
|
||||
use App\Policies\Rules\UserRoleRules as URR;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
|
||||
use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
|
||||
|
||||
class LogPolicy extends RootPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Init policies on call
|
||||
*
|
||||
* @param User $user
|
||||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function setPolicies(User $user, Model $model = null)
|
||||
{
|
||||
/**
|
||||
* This first rule to return true in a policy action authorises the action
|
||||
*/
|
||||
$this->policies = [
|
||||
'log' => [
|
||||
'viewAny' => URR::make('userIsAdmin', $user),
|
||||
'view' => URR::make('denyEveryOne'),
|
||||
'update' => StaticRules::and([
|
||||
URR::make('userIsDev', $user),
|
||||
LogStaticRules::make('logIsOwnedByUser', $user, $model)
|
||||
]),
|
||||
'create' => URR::make('userIsAdmin', $user),
|
||||
'delete' => URR::make('denyEveryOne')
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -2,11 +2,37 @@
|
|||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Policies\Rules\UserRoleRules as URR;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
|
||||
|
||||
class ProjectPolicy extends AppPolicies
|
||||
class ProjectPolicy extends RootPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
// Handle delete has tickets
|
||||
/**
|
||||
* Init policies on call
|
||||
*
|
||||
* @param User $user
|
||||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function setPolicies(User $user, Model $model = null)
|
||||
{
|
||||
/**
|
||||
* This first rule to return true in a policy action authorises the action
|
||||
*/
|
||||
$this->policies = [
|
||||
'project' => [
|
||||
'viewAny' => URR::make('userIsAdmin', $user),
|
||||
'view' => URR::make('userIsAdmin', $user),
|
||||
'update' => URR::make('userIsAdmin', $user),
|
||||
'create' => URR::make('userIsAdmin', $user),
|
||||
'delete' => URR::make('userIsAdmin', $user)
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
20
app/Policies/Rules/LogStaticRules.php
Normal file
20
app/Policies/Rules/LogStaticRules.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies\Rules;
|
||||
|
||||
use App\Models\Log;
|
||||
use App\Models\User;
|
||||
use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
|
||||
|
||||
/**
|
||||
* Userlevel Policiy Static Rules
|
||||
*! Rules has to return boolean value
|
||||
*/
|
||||
abstract class LogStaticRules extends StaticRules
|
||||
{
|
||||
|
||||
protected static function logIsOwnedByUser(User $user, Log $log)
|
||||
{
|
||||
return $log->user->id === $user->id;
|
||||
}
|
||||
}
|
||||
20
app/Policies/Rules/SiteStaticRules.php
Normal file
20
app/Policies/Rules/SiteStaticRules.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies\Rules;
|
||||
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
|
||||
|
||||
/**
|
||||
* Userlevel Policiy Static Rules
|
||||
*! Rules has to return boolean value
|
||||
*/
|
||||
abstract class SiteStaticRules extends StaticRules
|
||||
{
|
||||
|
||||
protected static function userSitesHasTicket(User $user, Ticket $ticket)
|
||||
{
|
||||
return $user->sites->contains($ticket->site_id);
|
||||
}
|
||||
}
|
||||
20
app/Policies/Rules/TicketStaticRules.php
Normal file
20
app/Policies/Rules/TicketStaticRules.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies\Rules;
|
||||
|
||||
use App\Enums\TicketStatus;
|
||||
use App\Models\Ticket;
|
||||
use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
|
||||
|
||||
/**
|
||||
* Userlevel Policiy Static Rules
|
||||
*! Rules has to return boolean value
|
||||
*/
|
||||
abstract class TicketStaticRules extends StaticRules
|
||||
{
|
||||
|
||||
protected static function ticketIsNotArchived(Ticket $ticket)
|
||||
{
|
||||
return !$ticket->status->equals(TicketStatus::archived());
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ namespace App\Policies\Rules;
|
|||
|
||||
use App\Enums\Role;
|
||||
use App\Models\User;
|
||||
use App\Lib\PolicySystem\StaticRules;
|
||||
use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
|
||||
|
||||
/**
|
||||
* Userlevel Policiy Static Rules
|
||||
|
|
@ -15,6 +15,21 @@ abstract class UserRoleRules extends StaticRules
|
|||
|
||||
protected static function userIsAdmin(User $user)
|
||||
{
|
||||
return $user->role->equals(Role::admin());
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
protected static function userIsDev(User $user)
|
||||
{
|
||||
return $user->role->equals(Role::developper());
|
||||
}
|
||||
|
||||
protected static function allowEveryOne()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function denyEveryOne()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Policies\Rules;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Lib\PolicySystem\StaticRules;
|
||||
|
||||
/**
|
||||
* Userlevel Policiy Static Rules
|
||||
*! Rules has to return boolean value
|
||||
*/
|
||||
abstract class UserStaticRules extends StaticRules
|
||||
{
|
||||
protected static function usersAreTheSame(User $user, User $userToCheck)
|
||||
{
|
||||
return ($user->id == $userToCheck->id) != 0;
|
||||
}
|
||||
|
||||
protected static function usersAreNotTheSame(User $user, User $userToCheck)
|
||||
{
|
||||
return !self::usersAreTheSame($user, $userToCheck);
|
||||
}
|
||||
|
||||
protected static function everyOne()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function noOne()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,14 +3,15 @@
|
|||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Lib\PolicySystem\RootPolicy;
|
||||
use App\Lib\PolicySystem\StaticRules as Sr;
|
||||
use App\Policies\Rules\UserStaticRules as USR;
|
||||
use App\Policies\Rules\UserRoleRules as URR;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
|
||||
|
||||
class AppPolicies extends RootPolicy
|
||||
class SitePolicy extends RootPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Init policies on call
|
||||
*
|
||||
|
|
@ -18,26 +19,20 @@ class AppPolicies extends RootPolicy
|
|||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function setPolicies(User $user, Model $model = null)
|
||||
{
|
||||
/**
|
||||
* This first rule to return true in a policy action authorises the action
|
||||
*/
|
||||
$this->policies = [
|
||||
'project' => [
|
||||
'site' => [
|
||||
'viewAny' => URR::make('userIsAdmin', $user),
|
||||
'view' => URR::make('userIsAdmin', $user),
|
||||
'update' => URR::make('userIsAdmin', $user),
|
||||
'create' => URR::make('userIsAdmin', $user),
|
||||
'delete' => URR::make('userIsAdmin', $user)
|
||||
],
|
||||
'user' => [
|
||||
'viewAny' => URR::make('userIsAdmin', $user),
|
||||
'view' => URR::make('userIsAdmin', $user),
|
||||
'update' => URR::make('userIsAdmin', $user),
|
||||
'create' => URR::make('userIsAdmin', $user),
|
||||
'delete' => URR::make('userIsAdmin', $user)
|
||||
],
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -2,132 +2,63 @@
|
|||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Enums\Role;
|
||||
use App\Enums\TicketStatus;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use App\Policies\Rules\SiteStaticRules as SSR;
|
||||
use App\Policies\Rules\TicketStaticRules;
|
||||
use App\Policies\Rules\UserRoleRules as URR;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
|
||||
use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules;
|
||||
|
||||
class TicketPolicy
|
||||
class TicketPolicy extends RootPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
* Init policies on call
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
* @param User $user
|
||||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
public function viewAny(User $user)
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function setPolicies(User $user, Model $model = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Models\Ticket $ticket
|
||||
* @return mixed
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
public function view(User $user, Ticket $ticket)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
|
||||
public function create(User $user)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Models\Ticket $ticket
|
||||
* @return mixed
|
||||
*/
|
||||
public function update(User $user, Ticket $ticket)
|
||||
{
|
||||
return Auth::user()->role->equals(Role::admin()) or $user->projects->contains($ticket->project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Models\Ticket $ticket
|
||||
* @return mixed
|
||||
*/
|
||||
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
public function delete(User $user, Ticket $ticket)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Models\Ticket $ticket
|
||||
* @return mixed
|
||||
*/
|
||||
public function restore(User $user, Ticket $ticket)
|
||||
{
|
||||
return Auth::user()->role->equals(Role::admin()) or $user->projects->contains($ticket->project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*
|
||||
* @param \App\Models\User $user
|
||||
* @param \App\Models\Ticket $ticket
|
||||
* @return mixed
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
|
||||
public function forceDelete(User $user, Ticket $ticket)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function changeStatus(User $user)
|
||||
{
|
||||
return $user->role->equals(Role::admin());
|
||||
}
|
||||
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
public function suscribeToTicket(User $user, Ticket $ticket)
|
||||
{
|
||||
return !$ticket->status->equals(TicketStatus::archived());
|
||||
}
|
||||
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
public function createMessage(User $user, Ticket $ticket)
|
||||
{
|
||||
//can create if
|
||||
//role == 1
|
||||
// OR project_id IN user->projects
|
||||
|
||||
if (
|
||||
!(
|
||||
$user->role->equals(Role::admin())
|
||||
or $user->projects->contains($ticket->project_id) )
|
||||
)
|
||||
{
|
||||
return Response::deny(trans('Vous ne pouvez modifier un ticket qui ne vous appartient pas.'));
|
||||
}
|
||||
return $ticket->status->equals(TicketStatus::archived()) ?
|
||||
Response::deny(trans('Vous ne pouvez ajouter un message à un ticket qui est archivé')) : Response::allow();
|
||||
/**
|
||||
* This first rule to return true in a policy action authorises the action
|
||||
*/
|
||||
$this->policies = [
|
||||
'ticket' => [
|
||||
'viewAny' => URR::make('allowEveryOne'),
|
||||
'view' => URR::make('allowEveryOne'),
|
||||
'create' => URR::make('allowEveryOne'),
|
||||
'update' => StaticRules::or([
|
||||
URR::make('userIsAdmin', $user),
|
||||
SSR::make('userSitesHasTicket', $user, $model)
|
||||
]),
|
||||
'changeStatus' => URR::make('userIsAdmin', $user),
|
||||
'delete' => URR::make('denyEveryOne'),
|
||||
'forceDelete' => URR::make('denyEveryOne'),
|
||||
'restore' => StaticRules::or([
|
||||
URR::make('userIsAdmin', $user),
|
||||
SSR::make('userSitesHasTicket', $user, $model)
|
||||
]),
|
||||
'suscribeToTicket' => TicketStaticRules::make('ticketIsNotArchived', $model),
|
||||
'createMessage' => StaticRules::or([
|
||||
StaticRules::or([
|
||||
URR::make('userIsAdmin', $user),
|
||||
SSR::make('userSitesHasTicket', $user, $model)
|
||||
]),
|
||||
StaticRules::and([
|
||||
TicketStaticRules::make('ticketIsNotArchived', $model),
|
||||
SSR::make('userSitesHasTicket', $user, $model)
|
||||
])
|
||||
])
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,37 @@
|
|||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Policies\Rules\UserRoleRules as URR;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\CrudPolicies\Policies\RootPolicy;
|
||||
|
||||
class UserPolicy extends AppPolicies
|
||||
class UserPolicy extends RootPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Init policies on call
|
||||
*
|
||||
* @param User $user
|
||||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function setPolicies(User $user, Model $model = null)
|
||||
{
|
||||
/**
|
||||
* This first rule to return true in a policy action authorises the action
|
||||
*/
|
||||
$this->policies = [
|
||||
'user' => [
|
||||
'viewAny' => URR::make('userIsAdmin', $user),
|
||||
'view' => URR::make('userIsAdmin', $user),
|
||||
'update' => URR::make('userIsAdmin', $user),
|
||||
'create' => URR::make('userIsAdmin', $user),
|
||||
'delete' => URR::make('userIsAdmin', $user)
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Providers;
|
|||
use App\Enums\TicketStatus;
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
|
@ -33,7 +34,14 @@ class AppServiceProvider extends ServiceProvider
|
|||
URL::forceScheme('https');
|
||||
}
|
||||
|
||||
$appInfos = json_decode(File::get(\app_path('../composer.json')));
|
||||
|
||||
View::share('name', $appInfos->name);
|
||||
View::share('version', $appInfos->version);
|
||||
View::share('license', $appInfos->license);
|
||||
|
||||
View::share('ticketsNew', Ticket::where('status', TicketStatus::new())->count());
|
||||
View::share('ticketsWaiting', Ticket::where('status', TicketStatus::waiting())->count());
|
||||
View::share('ticketsInProgress', Ticket::whereNotIn(
|
||||
'status',
|
||||
[
|
||||
|
|
@ -42,6 +50,7 @@ class AppServiceProvider extends ServiceProvider
|
|||
TicketStatus::estimateValidated(),
|
||||
TicketStatus::toPlan(),
|
||||
TicketStatus::planned(),
|
||||
TicketStatus::waiting(),
|
||||
TicketStatus::production(),
|
||||
TicketStatus::archived()
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\Ticket;
|
||||
use App\Policies\ProjectPolicy;
|
||||
use App\Policies\TicketPolicy;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
|
|
|
|||
|
|
@ -2,28 +2,37 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Events\FileCreated;
|
||||
use App\Events\NotifySubscriptions;
|
||||
use App\Events\NotifyTicketCreator;
|
||||
use App\Events\ProjectUpdated;
|
||||
use App\Events\SiteUpdated;
|
||||
use App\Events\TicketStatusChanged;
|
||||
use App\Listeners\AddStatusMessages;
|
||||
use App\Listeners\HandleLoggable;
|
||||
use App\Listeners\SendNotificationMail;
|
||||
use App\Listeners\SendNotificationMailToTicketCreator;
|
||||
use App\Listeners\SendSubscriptionsNotificationsMail;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
||||
/**
|
||||
* The event listener mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listen = [
|
||||
Registered::class => [
|
||||
SendEmailVerificationNotification::class,
|
||||
],
|
||||
'App\Events\TicketStatusChanged' => [
|
||||
'App\Listeners\AddStatusMessages',
|
||||
],
|
||||
'App\Events\NotifySubscriptions' => [
|
||||
'App\Listeners\SendNotificationMail'
|
||||
],
|
||||
Registered::class => [SendEmailVerificationNotification::class],
|
||||
TicketStatusChanged::class => [AddStatusMessages::class],
|
||||
NotifySubscriptions::class => [SendSubscriptionsNotificationsMail::class],
|
||||
NotifyTicketCreator::class => [SendNotificationMailToTicketCreator::class],
|
||||
FileCreated::class => [HandleLoggable::class],
|
||||
ProjectUpdated::class => [HandleLoggable::class],
|
||||
SiteUpdated::class => [HandleLoggable::class]
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\File;
|
||||
use App\Models\Log;
|
||||
use App\Models\Project;
|
||||
use App\Models\Site;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
|
@ -19,16 +23,7 @@ class RouteServiceProvider extends ServiceProvider
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
public const HOME = '/home';
|
||||
|
||||
/**
|
||||
* The controller namespace for the application.
|
||||
*
|
||||
* When present, controller route declarations will automatically be prefixed with this namespace.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
// protected $namespace = 'App\\Http\\Controllers';
|
||||
public const HOME = '/ticket';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
|
|
@ -49,8 +44,14 @@ class RouteServiceProvider extends ServiceProvider
|
|||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web.php'));
|
||||
});
|
||||
Route::model('file', File::class);
|
||||
Route::model('log', Log::class);
|
||||
Route::model('user', User::class);
|
||||
Route::model('site', Site::class);
|
||||
Route::model('project', Project::class);
|
||||
Route::bind('ticket', function ($value) {
|
||||
return Ticket::where('id', $value)->has('site')->firstOrFail();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -60,6 +61,9 @@ class RouteServiceProvider extends ServiceProvider
|
|||
*/
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
RateLimiter::for('credentials', function (Request $request) {
|
||||
return Limit::perMinute(30)->by(optional($request->user())->id ?: $request->ip());
|
||||
});
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,516 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use App\Enums\CrudAction;
|
||||
use App\Enums\CrudType;
|
||||
use App\Exceptions\CrudException;
|
||||
use App\Http\Requests\CrudRequest;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
trait IsCrudModel
|
||||
{
|
||||
|
||||
protected static $editableProperties = [];
|
||||
|
||||
private static $schemaCrud = [
|
||||
'label' => null,
|
||||
'placeholder' => '',
|
||||
'type' => null,
|
||||
'enum' => null,
|
||||
'nullable' => false,
|
||||
'readonly' => false,
|
||||
'disabled' => false,
|
||||
'required' => false,
|
||||
'validate' => [],
|
||||
'rules' => [],
|
||||
'actions' => [],
|
||||
'belongsTo' => null,
|
||||
'belongsToMany' => null,
|
||||
'getAttribute' => null,
|
||||
'setAttribute' => null
|
||||
];
|
||||
|
||||
// OVERLOAD MODEL FUNCTION
|
||||
public function __get($key)
|
||||
{
|
||||
if (
|
||||
isset(static::$editableProperties[$key]['getAttribute']) and
|
||||
is_callable(static::$editableProperties[$key]['getAttribute'])
|
||||
) {
|
||||
return static::$editableProperties[$key]['getAttribute']($this);
|
||||
}
|
||||
return parent::__get($key);
|
||||
}
|
||||
|
||||
// OVERLOAD MODEL FUNCTION
|
||||
public function __set($key, $value)
|
||||
{
|
||||
if (
|
||||
isset($this::$editableProperties[$key]['setAttribute']) and
|
||||
is_callable($this::$editableProperties[$key]['setAttribute'])
|
||||
) {
|
||||
return $this::$editableProperties[$key]['setAttribute']($this, $value);
|
||||
}
|
||||
return parent::__set($key, $value);
|
||||
}
|
||||
|
||||
// OVERLOAD MODEL FUNCTION
|
||||
public function fill(array $attributes)
|
||||
{
|
||||
parent::fill($attributes);
|
||||
foreach ($attributes as $k => $attribute) {
|
||||
if (
|
||||
isset(static::$editableProperties[$k]['setAttribute']) and
|
||||
is_callable(static::$editableProperties[$k]['setAttribute'])
|
||||
) {
|
||||
$this->{$k} = $attribute;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function saveRelations(CrudRequest $request)
|
||||
{
|
||||
$validated = $request->validated();
|
||||
$props = self::getEditableProperties();
|
||||
foreach ($props as $k => $prop) {
|
||||
if ($prop['belongsToMany'] and \is_array($validated[$k])) {
|
||||
$this->{$k}()->sync($validated[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Has this model Has many relations with delete restrict ?
|
||||
*
|
||||
* @return bool|string
|
||||
*/
|
||||
public function gotHasManyRelationWithRestrictOnDelete()
|
||||
{
|
||||
$sm = DB::getDoctrineSchemaManager();
|
||||
foreach (self::getRelationships() as $relation => $type) {
|
||||
if ($type[0] === 'HasMany') {
|
||||
$table = (new $type[1]())->getTable();
|
||||
/** @var \Doctrine\DBAL\Schema\ForeignKeyConstraint $foreignKey */
|
||||
foreach ($sm->listTableForeignKeys($table) as $foreignKey) {
|
||||
// /vendor/doctrine/dbal/src/Schema/ForeignKeyConstraint.php onEvent()
|
||||
if ($foreignKey->onDelete() === 'CASCADE') {
|
||||
// allow only CASCADE action, otherwise it would break the app
|
||||
continue;
|
||||
}
|
||||
return class_basename($type[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the crud label
|
||||
*/
|
||||
public function getCrudLabelAttribute()
|
||||
{
|
||||
return $this->{$this->crudLabelColumn};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the crud value
|
||||
*/
|
||||
public function getCrudValueAttribute()
|
||||
{
|
||||
if (is_null($this->crudValueColumn)) {
|
||||
throw new CrudException(sprintf(
|
||||
'[\'belongsTo\'] model `%s` must have `crudValueColumn` property',
|
||||
get_class($this),
|
||||
CrudAction::class
|
||||
));
|
||||
}
|
||||
return $this->{$this->crudValueColumn};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the crud label column
|
||||
*/
|
||||
public function getCrudLabelColumnAttribute()
|
||||
{
|
||||
if (is_null($this->crudLabelColumn)) {
|
||||
throw new CrudException(sprintf(
|
||||
'[\'belongsTo\'] model `%s` must have `crudLabelColumn` property',
|
||||
get_class($this),
|
||||
CrudAction::class
|
||||
));
|
||||
}
|
||||
return $this->crudLabelColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the crud value column
|
||||
*/
|
||||
public function getCrudValueColumnAttribute()
|
||||
{
|
||||
return $this->crudValueColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model validation rules
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getRules(): array
|
||||
{
|
||||
return collect(static::$editableProperties)->mapWithKeys(function ($item, $key) {
|
||||
$item = \array_merge(self::$schemaCrud, $item);
|
||||
$defaultRules = [];
|
||||
if (!$item['nullable']) {
|
||||
$defaultRules[] = 'required';
|
||||
} elseif ($item['required']) {
|
||||
$defaultRules[] = 'required';
|
||||
} elseif ($item['readonly']) {
|
||||
$defaultRules[] = 'prohibited';
|
||||
}
|
||||
switch ($item['type']) {
|
||||
case CrudType::boolean():
|
||||
$defaultRules[] = 'boolean';
|
||||
break;
|
||||
case CrudType::email():
|
||||
$defaultRules[] = 'email:rfc,dns';
|
||||
break;
|
||||
case CrudType::belongsTo():
|
||||
$defaultRules[] = 'integer';
|
||||
$defaultRules[] = 'min:0';
|
||||
break;
|
||||
case CrudType::int():
|
||||
$defaultRules[] = 'integer';
|
||||
break;
|
||||
case CrudType::belongsToMany():
|
||||
$defaultRules[] = 'array';
|
||||
break;
|
||||
case CrudType::password():
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
$defaultRules[] = "regex:/^(?=.*[A-Z])(?=.*[²&~\"#'\{\(\[\-|`_\\^@\)\°=+\}¨^\$£¤%?,;.:\/!§*<>])(?=.*[0-9].*)(?=.*[a-z]).{8,}$/";
|
||||
break;
|
||||
case CrudType::text():
|
||||
case CrudType::string():
|
||||
default:
|
||||
$defaultRules[] = 'string';
|
||||
break;
|
||||
}
|
||||
return [$key => array_merge($defaultRules, $item['validate'] ?? self::$schemaCrud['validate'])];
|
||||
})->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getModelName(): string
|
||||
{
|
||||
return class_basename(get_class($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Editable Properties
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getEditableProperties(): array
|
||||
{
|
||||
static::assertEditablePropertiesIsCorrect();
|
||||
return static::$editableProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert self::$editableProperties is in a correct format
|
||||
*
|
||||
* 'string' => CrudType::value()
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function assertEditablePropertiesIsCorrect(): void
|
||||
{
|
||||
if (empty(static::$editableProperties)) {
|
||||
throw new CrudException('$editableProperties must be defined in ' . __CLASS__);
|
||||
}
|
||||
foreach (static::$editableProperties as $name => &$prop) {
|
||||
if (!is_string($name)) {
|
||||
throw new CrudException(sprintf(
|
||||
'editableProperties array value must be an array given: %s',
|
||||
print_r($prop, true) // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Found
|
||||
));
|
||||
}
|
||||
if (!is_array($prop)) {
|
||||
throw new CrudException(sprintf(
|
||||
'editableProperties array value must be an array given: %s',
|
||||
print_r($prop, true) // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Found
|
||||
));
|
||||
}
|
||||
$prop = array_merge_recursive_distinct(self::$schemaCrud, $prop);
|
||||
|
||||
// Auto label
|
||||
$prop['label'] = $prop['label'] ?? ucfirst($name);
|
||||
self::assertLabelIscorrect($prop);
|
||||
self::assertPlaceholderIscorrect($prop);
|
||||
self::assertTypeIscorrect($prop);
|
||||
switch ($prop['type']) {
|
||||
// Email auto rule
|
||||
case CrudType::email():
|
||||
$prop['validate'] = is_array($prop['validate']) ? $prop['validate'] : [];
|
||||
if (!array_search('email:rfc,dns', $prop['validate'])) {
|
||||
$prop['validate'][] = 'email:rfc,dns';
|
||||
}
|
||||
break;
|
||||
case CrudType::json():
|
||||
$prop['validate'] = is_array($prop['validate']) ? $prop['validate'] : [];
|
||||
if (!array_search('json', $prop['validate'])) {
|
||||
$prop['validate'][] = 'json';
|
||||
}
|
||||
break;
|
||||
}
|
||||
self::assertNullableIscorrect($prop);
|
||||
self::assertReadonlyIscorrect($prop);
|
||||
self::assertDisabledIscorrect($prop);
|
||||
self::assertValidateIscorrect($prop);
|
||||
self::assertRulesArecorrect($prop);
|
||||
self::assertActionsArecorrect($prop);
|
||||
self::assertBelongsToIsCorrect($prop);
|
||||
self::assertEnumIscorrect($prop);
|
||||
self::assertGetAttributeIscorrect($prop);
|
||||
self::assertSetAttributeIscorrect($prop);
|
||||
self::assertBelongsToManyIsCorrect($prop);
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertLabelIscorrect(array $prop): void
|
||||
{
|
||||
if (!isset($prop['label']) or !is_string($prop['label'])) {
|
||||
throw new CrudException('$editableProperties[\'label\'] array value must be a string');
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertPlaceholderIscorrect(array $prop): void
|
||||
{
|
||||
if (!isset($prop['placeholder']) or !is_string($prop['placeholder'])) {
|
||||
throw new CrudException('$editableProperties[\'placeholder\'] array value must be a string');
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertTypeIscorrect(array $prop): void
|
||||
{
|
||||
if (!isset($prop['type']) or !is_object($prop['type']) or get_class($prop['type']) !== CrudType::class) {
|
||||
throw new CrudException('$editableProperties[\'type\'] value must be a CrudType enum');
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertNullableIscorrect(array $prop): void
|
||||
{
|
||||
if (!isset($prop['nullable']) or !is_bool($prop['nullable'])) {
|
||||
throw new CrudException('$editableProperties[\'nullable\'] array value must be a boolean');
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertReadonlyIscorrect(array $prop): void
|
||||
{
|
||||
if (
|
||||
!isset($prop['readonly']) or
|
||||
(!is_bool($prop['readonly']) and !\is_callable($prop['readonly']))
|
||||
) {
|
||||
throw new CrudException('$editableProperties[\'readonly\'] array value must be a boolean or a function returnning a boolean');
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertDisabledIscorrect(array $prop): void
|
||||
{
|
||||
if (!isset($prop['disabled']) or !is_bool($prop['disabled'])) {
|
||||
throw new CrudException('$editableProperties[\'disabled\'] array value must be a boolean');
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertRequiredIscorrect(array $prop): void
|
||||
{
|
||||
if (!isset($prop['required']) or !is_bool($prop['required'])) {
|
||||
throw new CrudException('$editableProperties[\'required\'] array value must be a boolean');
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertValidateIscorrect(array $prop): void
|
||||
{
|
||||
if (!isset($prop['validate']) or !\is_array($prop['validate'])) {
|
||||
throw new CrudException('$editableProperties[\'validate\'] array value must be an array');
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertGetAttributeIscorrect(array $prop): void
|
||||
{
|
||||
if (!is_null($prop['getAttribute']) and !is_callable($prop['getAttribute'])) {
|
||||
throw new CrudException('$editableProperties[\'getAttribute\'] array value must be callable');
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertSetAttributeIscorrect(array $prop): void
|
||||
{
|
||||
if (!is_null($prop['setAttribute']) and !is_callable($prop['setAttribute'])) {
|
||||
throw new CrudException('$editableProperties[\'setAttribute\'] array value must be callable');
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertRulesArecorrect(array $prop): void
|
||||
{
|
||||
$allowedRules = [
|
||||
'min' => function ($v) {
|
||||
return \is_numeric($v);
|
||||
},
|
||||
'max' => function ($v) {
|
||||
return \is_numeric($v);
|
||||
},
|
||||
'step' => function ($v) {
|
||||
return \is_numeric($v);
|
||||
},
|
||||
];
|
||||
if (!isset($prop['rules']) or !\is_array($prop['rules'])) {
|
||||
throw new CrudException('$editableProperties[\'rules\'] array value must be an array');
|
||||
}
|
||||
if (count(array_diff_key($prop['rules'], $allowedRules))) {
|
||||
throw new CrudException(sprintf(
|
||||
'$editableProperties[\'rules\'] array contains unallowed rule, allowed are : %s',
|
||||
implode(',', array_keys($allowedRules))
|
||||
));
|
||||
}
|
||||
foreach ($allowedRules as $k => $f) {
|
||||
if (array_key_exists($k, $prop['rules']) and !$f($prop['rules'][$k])) {
|
||||
throw new CrudException(sprintf(
|
||||
'$editableProperties[\'rules\'] rule `%s` does not validate with value `%s`',
|
||||
$k,
|
||||
$prop['rules'][$k]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertActionsArecorrect(array $prop): void
|
||||
{
|
||||
if (!isset($prop['actions']) or !\is_array($prop['actions'])) {
|
||||
throw new CrudException('$editableProperties[\'actions\'] array value must be an array');
|
||||
}
|
||||
foreach ($prop['actions'] as $action) {
|
||||
if (!is_object($action) or get_class($action) !== CrudAction::class) {
|
||||
throw new CrudException(sprintf(
|
||||
'$editableProperties[\'actions\'] array value must contain `%s` objects',
|
||||
CrudAction::class
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertEnumIscorrect(array $prop): void
|
||||
{
|
||||
if (
|
||||
$prop['type']->equals(CrudType::enum()) and (is_null($prop['enum']) or
|
||||
!\is_subclass_of($prop['enum'], BaseEnum::class))
|
||||
) {
|
||||
throw new CrudException(sprintf(
|
||||
'$editableProperties[\'enum\'] array value must be an enum `%s`',
|
||||
BaseEnum::class
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertBelongsToIsCorrect(array $prop): void
|
||||
{
|
||||
if (is_null($prop['belongsTo'])) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
$prop['type']->equals(CrudType::belongsTo()) and
|
||||
(!\is_string($prop['belongsTo']) or
|
||||
!\class_exists($prop['belongsTo']) or
|
||||
is_a($prop['belongsTo'], Model::class, true) or
|
||||
!in_array(IsCrudModel::class, class_uses_recursive($prop['belongsTo'])))
|
||||
) {
|
||||
throw new CrudException(sprintf(
|
||||
'$editableProperties[\'belongsTo\'] array value must be a CrudModel given: `%s`',
|
||||
$prop['belongsTo']
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private static function assertBelongsToManyIsCorrect(array $prop): void
|
||||
{
|
||||
if (is_null($prop['belongsToMany'])) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
$prop['type']->equals(CrudType::belongsToMany()) and
|
||||
(!\is_string($prop['belongsToMany']) or
|
||||
!\class_exists($prop['belongsToMany']) or
|
||||
is_a($prop['belongsToMany'], Model::class, true) or
|
||||
!in_array(IsCrudModel::class, class_uses_recursive($prop['belongsToMany'])))
|
||||
) {
|
||||
throw new CrudException(sprintf(
|
||||
'$editableProperties[\'belongsToMany\'] array value must be a CrudModel given: `%s`',
|
||||
$prop['belongsToMany']
|
||||
));
|
||||
}
|
||||
if (
|
||||
$prop['type']->equals(CrudType::belongsToMany()) and
|
||||
(!\is_string($prop['belongsToManyLabel']))
|
||||
) {
|
||||
throw new CrudException(sprintf(
|
||||
'$editableProperties[\'belongsToManyLabel\'] must be string using belongsToMany, given: `%s`',
|
||||
$prop['belongsToMany']
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get eloquent relationships
|
||||
*
|
||||
* @url https://laracasts.com/discuss/channels/eloquent/get-all-model-relationships
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function getRelationships()
|
||||
{
|
||||
$instance = new static();
|
||||
|
||||
// Get public methods declared without parameters and non inherited
|
||||
$class = get_class($instance);
|
||||
$allMethods = (new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC);
|
||||
$methods = array_filter(
|
||||
$allMethods,
|
||||
function ($method) use ($class) {
|
||||
return $method->class === $class
|
||||
&& !$method->getParameters() // relationships have no parameters
|
||||
&& $method->getName() !== 'getRelationships' // prevent infinite recursion
|
||||
&& $method->getName() !== 'gotHasManyRelationWithRestrictOnDelete'; // prevent infinite recursion
|
||||
}
|
||||
);
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
$relations = [];
|
||||
foreach ($methods as $method) {
|
||||
try {
|
||||
$methodName = $method->getName();
|
||||
$methodReturn = $instance->$methodName();
|
||||
if (!$methodReturn instanceof Relation) {
|
||||
continue;
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$type = (new \ReflectionClass($methodReturn))->getShortName();
|
||||
$model = get_class($methodReturn->getRelated());
|
||||
$relations[$methodName] = [$type, $model];
|
||||
}
|
||||
|
||||
DB::rollBack();
|
||||
|
||||
return $relations;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"name": "laravel/laravel",
|
||||
"name": "ldcweb/supportweb",
|
||||
"version": "1.0.1",
|
||||
"type": "project",
|
||||
"description": "The Laravel Framework.",
|
||||
"keywords": [
|
||||
|
|
@ -10,13 +11,16 @@
|
|||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
"doctrine/dbal": "^3.1",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"fideloper/proxy": "^4.4",
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"kwaadpepper/crud-policies": "^0.1.9",
|
||||
"kwaadpepper/enum": "^0.1.4",
|
||||
"laravel-lang/lang": "~8.0",
|
||||
"laravel/framework": "^8.12",
|
||||
"laravel/tinker": "^2.5",
|
||||
"laravel/ui": "^3.1"
|
||||
"laravel/ui": "^3.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"facade/ignition": "^2.5",
|
||||
|
|
|
|||
8048
composer.lock
generated
Normal file
8048
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -39,7 +39,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'debug' => (bool) env('APP_DEBUG', false),
|
||||
'debug' => (bool)env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
@ -171,7 +171,6 @@ return [
|
|||
*/
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\AuthServiceProvider::class,
|
||||
// App\Providers\BroadcastServiceProvider::class,
|
||||
App\Providers\EventServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
App\Providers\TranslationServiceProvider::class
|
||||
|
|
@ -233,6 +232,8 @@ return [
|
|||
'CrudAction' => App\Enums\CrudAction::class,
|
||||
'CrudType' => App\Enums\CrudType::class,
|
||||
'Role' => App\Enums\Role::class,
|
||||
'FileType' => App\Enums\FileType::class,
|
||||
'LoadLevel' => App\Enums\LoadLevel::class,
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
|||
|
|
@ -69,12 +69,7 @@ return [
|
|||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => App\Models\User::class,
|
||||
],
|
||||
|
||||
// 'users' => [
|
||||
// 'driver' => 'database',
|
||||
// 'table' => 'users',
|
||||
// ],
|
||||
]
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -60,9 +60,7 @@ return [
|
|||
env('MEMCACHED_USERNAME'),
|
||||
env('MEMCACHED_PASSWORD'),
|
||||
],
|
||||
'options' => [
|
||||
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
|
||||
],
|
||||
'options' => [],
|
||||
'servers' => [
|
||||
[
|
||||
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
|
||||
|
|
@ -99,6 +97,6 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache'),
|
||||
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache'),
|
||||
|
||||
];
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ return [
|
|||
|
||||
'options' => [
|
||||
'cluster' => env('REDIS_CLUSTER', 'redis'),
|
||||
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
|
||||
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_database_'),
|
||||
],
|
||||
|
||||
'default' => [
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ return [
|
|||
'public' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app/public'),
|
||||
'url' => env('APP_URL').'/storage',
|
||||
'url' => env('APP_URL') . '/storage',
|
||||
'visibility' => 'public',
|
||||
],
|
||||
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ return [
|
|||
|
||||
'cookie' => env(
|
||||
'SESSION_COOKIE',
|
||||
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
|
||||
Str::slug(env('APP_NAME', 'laravel'), '_') . '_session'
|
||||
),
|
||||
|
||||
/*
|
||||
|
|
|
|||
8
config/tickets.php
Normal file
8
config/tickets.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'workedHoursInAWeek' => 37,
|
||||
'workedDaysInAWeek' => 5,
|
||||
'workingDevsPerWeek' => 2,
|
||||
'avgTimeForTicket' => 0.95, // 0.95 heure
|
||||
];
|
||||
|
|
@ -15,20 +15,17 @@ return [
|
|||
* of your proxy (e.g. if using ELB or similar).
|
||||
*
|
||||
*/
|
||||
'proxies' => '*', // [<ip addresses>,], '*', '<ip addresses>,'
|
||||
'proxies' => '*',
|
||||
|
||||
/*
|
||||
* To trust one or more specific proxies that connect
|
||||
* directly to your server, use an array or a string separated by comma of IP addresses:
|
||||
*/
|
||||
// 'proxies' => ['192.168.1.1'],
|
||||
// 'proxies' => '192.168.1.1, 192.168.1.2',
|
||||
|
||||
/*
|
||||
* Or, to trust all proxies that connect
|
||||
* directly to your server, use a "*"
|
||||
*/
|
||||
// 'proxies' => '*',
|
||||
|
||||
/*
|
||||
* Which headers to use to detect proxy related data (For, Host, Proto, Port)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue