From 75da3f91b379a8e64907faf482b59147167e2dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Tue, 24 May 2022 17:23:36 +0200 Subject: [PATCH] =?UTF-8?q?Tickets=20Assign=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/TicketController.php | 87 +++++++++++++++--- .../Requests/AssignTicketToUserRequest.php | 42 +++++++++ app/Models/Ticket.php | 11 +++ app/Models/User.php | 89 +++++++++++++++++-- app/Notifications/ResetPassword.php | 3 +- app/Policies/Rules/TicketStaticRules.php | 19 +++- app/Policies/TicketPolicy.php | 10 ++- ...22_05_24_104346_assign_user_to_tickets.php | 38 ++++++++ resources/views/ticket/index.blade.php | 39 +++++--- resources/views/ticket/show.blade.php | 11 +++ routes/web.php | 3 + 11 files changed, 318 insertions(+), 34 deletions(-) create mode 100644 app/Http/Requests/AssignTicketToUserRequest.php create mode 100644 database/migrations/2022_05_24_104346_assign_user_to_tickets.php diff --git a/app/Http/Controllers/TicketController.php b/app/Http/Controllers/TicketController.php index 3cfa550..6a1ac4f 100644 --- a/app/Http/Controllers/TicketController.php +++ b/app/Http/Controllers/TicketController.php @@ -6,6 +6,7 @@ use App\Enums\TicketStatus; use App\Enums\TicketType; use App\Events\NotifySubscriptions; use App\Events\TicketStatusChanged; +use App\Http\Requests\AssignTicketToUserRequest; use App\Http\Requests\ChangeTicketStatus; use App\Http\Requests\CreateMessage; use App\Http\Requests\SortTickets; @@ -20,7 +21,6 @@ use App\Models\Ticket; use App\Models\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Session; @@ -47,7 +47,7 @@ class TicketController extends Controller { // * Handle Ajax if ($request->ajax()) { - $tickets = Ticket::with('user'); + $tickets = Ticket::with(['user', 'assignedUser']); if ($request->s) { $tickets = $tickets->where('text', 'LIKE', "%$request->s%") ->orWhere('id', $request->s) @@ -372,7 +372,7 @@ class TicketController extends Controller { /** @var \App\Models\User */ $user = auth()->user(); - User::find(Auth::user()->id)->subscriptions()->toggle($ticket->id); + $user->subscriptions()->toggle($ticket->id); if ($user->subscriptions()->where('ticket_id', $ticket->id)->exists()) { return back() ->with('success', trans('Vous avez été inscrit')); @@ -388,7 +388,7 @@ class TicketController extends Controller */ public function reopen(Ticket $ticket) { - $message = trans(":ticketName à réouvert le ticket n°:ticketNumber", [ + $message = trans(':ticketName à réouvert le ticket n°:ticketNumber', [ 'ticketName' => $ticket->user->name, 'ticketNumber' => $ticket->id, ]); @@ -397,10 +397,64 @@ class TicketController extends Controller $ticket->saveQuietly(); event(new TicketStatusChanged($ticket, $oldStatus, null, $message)); event(new NotifySubscriptions($ticket, $message, collect([$ticket->user]))); - User::find(Auth::user()->id)->subscriptions()->toggle($ticket->id); + + /** @var \App\Models\User */ + $user = auth()->user(); + $user->subscriptions()->toggle($ticket->id); + return back()->with('warning', trans('Votre ticket à été réouvert.')); } + /** + * Assign ticket to user ticket + * + * @param AssignTicketToUserRequest $request + * @param Ticket $ticket + * @return \Illuminate\Http\RedirectResponse + */ + public function assignTo(AssignTicketToUserRequest $request, Ticket $ticket) + { + /** @var \App\Models\User */ + $user = auth()->user(); + + /** @var \App\Models\User|null */ + if ($assignedUser = User::whereId($request->assign)->first()) { + $message = trans(':admin à assigné le ticket n°:ticketNumber à :assignedAdmin', [ + 'admin' => $user->name, + 'ticketNumber' => $ticket->id, + 'assignedAdmin' => $assignedUser->name, + ]); + $ticket->assignedUser()->associate($assignedUser); + } else { + $message = trans( + ':admin modifié l\'assignation du ticket n°:ticketNumber, il n\'est plus assigné à quelqu\'un.', + [ + 'admin' => $user->name, + 'ticketNumber' => $ticket->id, + ] + ); + $ticket->assignedUser()->dissociate(); + } + $ticket->saveQuietly(); + + + /** @var \App\Enums\TicketStatus */ + $oldStatus = $ticket->status; + event(new TicketStatusChanged($ticket, $oldStatus, null, $message)); + event(new NotifySubscriptions($ticket, $message, collect([$ticket->user]))); + + /** @var \App\Models\User */ + $user = auth()->user(); + $user->subscriptions()->toggle($ticket->id); + + if ($assignedUser) { + return back()->with('info', trans('Le ticket à été assigné à :assignedUser', [ + 'assignedUser' => $assignedUser->name + ])); + } + return back()->with('info', trans('Le ticket n\'est plus assigné à personne')); + } + // * PRIVATES /** @@ -413,11 +467,15 @@ class TicketController extends Controller private function filterTickets(Builder $tickets, Request $request): Builder { if ($request->rst) { + Session::remove('tickets.ass'); Session::remove('tickets.st'); Session::remove('tickets.pr'); Session::remove('tickets.sort_col'); Session::remove('tickets.sort_way'); } + if ($request->assr) { + Session::remove('tickets.ass'); + } if ($request->prs) { Session::remove('tickets.pr'); } @@ -436,14 +494,23 @@ class TicketController extends Controller if ($request->st) { Session::put('tickets.st', $request->st); } + if ($request->ass) { + Session::put('tickets.ass', $request->ass); + } Session::save(); if (Session::has('tickets.pr')) { - $tickets = $tickets->where('site_id', Session::get('tickets.pr')); + $tickets->where('site_id', Session::get('tickets.pr')); + } + + if (Session::has('tickets.ass')) { + $tickets->whereHas('assignedUser', function ($q) { + $q->whereId(Session::get('tickets.ass')); + }); } if (Session::has('tickets.st')) { - $tickets = $tickets->where('status', Session::get('tickets.st')); + $tickets->where('status', Session::get('tickets.st')); } if ( @@ -451,15 +518,15 @@ class TicketController extends Controller Session::has('tickets.sort_way') and in_array(Session::get('tickets.sort_col'), [ 'id', 'site_id', 'created_at', 'created_by', - 'type', 'status' + 'type', 'status', 'assigned_to' ]) ) { - $tickets = $tickets->orderBy( + $tickets->orderBy( Session::get('tickets.sort_col'), Session::get('tickets.sort_way') ); } else { - $tickets = $tickets->orderBy('id', 'DESC'); + $tickets->orderBy('id', 'DESC'); } return $tickets; } diff --git a/app/Http/Requests/AssignTicketToUserRequest.php b/app/Http/Requests/AssignTicketToUserRequest.php new file mode 100644 index 0000000..6caaa12 --- /dev/null +++ b/app/Http/Requests/AssignTicketToUserRequest.php @@ -0,0 +1,42 @@ +user(); + return auth()->check() and $user->isAdmin(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules() + { + return [ + 'assign' => [ + 'nullable', 'exists:users,id', + // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed + function ($attribute, $value, $fail) { + /** @var \App\Models\User $user */ + if ($user = User::whereId($value)->first() and !$user->isAdmin()) { + $fail(trans('Cet utilisateur ne peut se voir assigner un ticket')); + } + } + ] + ]; + } +} diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php index b1957e0..372b5a4 100644 --- a/app/Models/Ticket.php +++ b/app/Models/Ticket.php @@ -110,6 +110,17 @@ class Ticket extends Model return $this->belongsTo(User::class, 'created_by'); } + /** + * BelongsTo User relation + * The user that is in charge of the ticket + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function assignedUser() + { + return $this->belongsTo(User::class, 'assigned_to'); + } + /** * HasMany Message relation * diff --git a/app/Models/User.php b/app/Models/User.php index 0ae64c5..b78b5d3 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -5,10 +5,12 @@ namespace App\Models; use App\Enums\Role; use App\Jobs\SendUserCreatedEmail; use App\Notifications\ResetPassword; +use Illuminate\Database\Eloquent\Collection; 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\Cache; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; use Kwaadpepper\CrudPolicies\Enums\CrudAction; @@ -24,17 +26,29 @@ class User extends Authenticatable use Notifiable; use SoftDeletes; + /** + * The attributes that should be cast to Enums. + * + * @var array + */ protected $enumCasts = [ 'role' => Role::class ]; + /** + * Crud Label + * + * @var string + */ protected $crudLabelColumn = 'name'; + /** + * Crud Value + * + * @var string + */ protected $crudValueColumn = 'id'; - // stores unhashed pwd - public $tmpPass = null; - /** * The attributes that are mass assignable. * @@ -67,7 +81,12 @@ class User extends Authenticatable 'email_verified_at' => 'datetime', ]; - public function __construct($attributes = []) + /** + * User + * + * @param array $attributes + */ + public function __construct(array $attributes = []) { self::$editableProperties = [ 'name' => [ @@ -148,6 +167,11 @@ class User extends Authenticatable parent::__construct($attributes); } + /** + * Perform any actions required after the model boots. + * + * @return void + */ protected static function booted() { static::creating(function (User $user) { @@ -158,46 +182,89 @@ class User extends Authenticatable if (Hash::needsRehash($user->password)) { $user->password = Hash::make($user->password); } + Cache::flush('AdminUsers'); }); static::updating(function (User $user) { if (Hash::needsRehash($user->password)) { $user->password = Hash::make($user->password); } + Cache::flush('AdminUsers'); }); } + // * RELATIONSHIPS + + /** + * HasMany Ticket relation + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ public function tickets() { return $this->hasMany(Ticket::class, 'created_by'); } + /** + * HasMany Message relation + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ public function messages() { - return $this->hasMany(Messages::class, 'user_id'); + return $this->hasMany(Message::class, 'user_id'); } + /** + * BelongsToMany Ticket relation + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ public function subscriptions() { return $this->belongsToMany(Ticket::class, 'subscriptions'); } + /** + * BelongsToMany Site relation + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ public function sites() { return $this->belongsToMany(Site::class); } + /** + * HasMany Schedule relation + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ public function schedules() { return $this->hasMany(Schedule::class); } + // * METHODS + + /** + * Send User creation confirm mail + * + * @return void + */ public function sendMailConfirm() { dispatch(new SendUserCreatedEmail($this)); } + /** + * Send User reset password mail + * + * @param mixed $token + * @return void + */ public function sendPasswordResetNotification($token) { + /** @var string $token */ $this->notify(new ResetPassword($token)); } @@ -211,4 +278,16 @@ class User extends Authenticatable return ($this->role->equals(Role::developper()) or $this->role->equals(Role::admin())); } + + /** + * Get All Admin users + * + * @return Collection + */ + public static function adminUsers(): Collection + { + return Cache::rememberForever('AdminUsers', function () { + return self::whereIn('role', [Role::admin(), Role::developper()])->get(); + }); + } } diff --git a/app/Notifications/ResetPassword.php b/app/Notifications/ResetPassword.php index afee7cb..14412de 100644 --- a/app/Notifications/ResetPassword.php +++ b/app/Notifications/ResetPassword.php @@ -13,7 +13,7 @@ class ResetPassword extends ResetPasswordNotification /** * Get the reset password notification mail message for the given URL. * - * @param string $url + * @param mixed $url * @return \Illuminate\Notifications\Messages\MailMessage */ protected function buildMailMessage($url) @@ -22,6 +22,7 @@ class ResetPassword extends ResetPasswordNotification ->subject(trans('Reinitialisation de mot de passe')) //phpcs:ignore Generic.Files.LineLength.TooLong ->line(trans('Vous recevez cet e-mail car nous avons reçu une demande de réinitialisation de mot de passe pour votre compte.')) + /** @var string $url */ ->action(trans('Réinitialiser le mot de passe'), $url) //phpcs:ignore Generic.Files.LineLength.TooLong ->line(trans('Ce lien de réinitialisation de mot de passe expirera dans :count minutes. ', ['count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')])) diff --git a/app/Policies/Rules/TicketStaticRules.php b/app/Policies/Rules/TicketStaticRules.php index bafd3f0..f9cf42a 100644 --- a/app/Policies/Rules/TicketStaticRules.php +++ b/app/Policies/Rules/TicketStaticRules.php @@ -12,7 +12,24 @@ use Kwaadpepper\CrudPolicies\Policies\Rules\StaticRules; */ abstract class TicketStaticRules extends StaticRules { - protected static function ticketIsNotArchived(Ticket $ticket) + /** + * Ticket is not in production + * + * @param Ticket $ticket + * @return boolean + */ + protected static function ticketIsNotInProduction(Ticket $ticket): bool + { + return !$ticket->status->equals(TicketStatus::production()); + } + + /** + * Ticket is not archived + * + * @param Ticket $ticket + * @return boolean + */ + protected static function ticketIsNotArchived(Ticket $ticket): bool { return !$ticket->status->equals(TicketStatus::archived()); } diff --git a/app/Policies/TicketPolicy.php b/app/Policies/TicketPolicy.php index 8396cf1..5dbdb0c 100644 --- a/app/Policies/TicketPolicy.php +++ b/app/Policies/TicketPolicy.php @@ -18,11 +18,10 @@ class TicketPolicy extends RootPolicy /** * Init policies on call * - * @param User $user + * @param User $user * @param Model $model * @return void */ - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed public function setPolicies(User $user, Model $model = null) { /** @@ -51,7 +50,12 @@ class TicketPolicy extends RootPolicy URR::make('userIsAdmin', $user), TicketStaticRules::make('ticketIsNotArchived', $model), ]), - 'reopen' => URR::make('userIsClient', $user) + 'reopen' => URR::make('userIsClient', $user), + 'assignTo' => StaticRules::and([ + URR::make('userIsAdmin', $user), + TicketStaticRules::make('ticketIsNotInProduction', $model), + TicketStaticRules::make('ticketIsNotArchived', $model) + ]) ] ]; } diff --git a/database/migrations/2022_05_24_104346_assign_user_to_tickets.php b/database/migrations/2022_05_24_104346_assign_user_to_tickets.php new file mode 100644 index 0000000..542f53b --- /dev/null +++ b/database/migrations/2022_05_24_104346_assign_user_to_tickets.php @@ -0,0 +1,38 @@ +foreignId('assigned_to') + ->comment('La personne a qui est assigné le ticket.') + ->nullable() + ->after('created_by') + ->constrained('users') + ->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('tickets', function (Blueprint $table) { + $table->dropForeign(['assigned_to']); + $table->dropColumn('assigned_to'); + }); + } +} diff --git a/resources/views/ticket/index.blade.php b/resources/views/ticket/index.blade.php index e6203ce..b234cb9 100644 --- a/resources/views/ticket/index.blade.php +++ b/resources/views/ticket/index.blade.php @@ -42,6 +42,16 @@ $userPr = Auth::user()->sites ?? collect([]);
+
+
+ +
+
@@ -61,20 +71,21 @@ $userPr = Auth::user()->sites ?? collect([]); #  @include('modules.sorter', ['colname' => 'id']) - - {{__('Projet') }}  - @include('modules.sorter', ['colname' => 'site_id']) - {{__('Type') }}  @include('modules.sorter', ['colname' => 'type']) - {{__('Fichier') }} - {{__('Fichier 2') }} + {{__('Fichiers') }} - {{__('Date de création') }}  + {{__('Crée le') }}  @include('modules.sorter', ['colname' => 'created_at']) + @if(Auth::user()->isAdmin()) + + {{__('Assigné à') }}  + @include('modules.sorter', ['colname' => 'assigned_to']) + + @endif {{__('Créé par') }}  @include('modules.sorter', ['colname' => 'created_by']) @@ -119,12 +130,11 @@ $userPr = Auth::user()->sites ?? collect([]); @endphp - {{ $ticket->id }} - @if ($ticket->is_internal) - 🔒 - @endif +
+ {{ $ticket->id }} @if($ticket->is_internal)🔒@endif + {{ $ticket->site->name }} +
- {{ $ticket->site->name }} {{ $ticket->type->label }} @@ -132,13 +142,14 @@ $userPr = Auth::user()->sites ?? collect([]); @if ($ticket->file !== null) Fichier @endif - - @if ($ticket->file2 !== null) Fichier 2 @endif {{ Carbon\Carbon::parse($ticket->created_at)->format('d/m/Y à H:i') }} + @if(Auth::user()->isAdmin()) + {{ $ticket->assignedUser ? $ticket->assignedUser->name : __('Personne') }} + @endif {{ $ticket->user ? $ticket->user->name : __('Utilisateur supprimé') }} {{ $ticket->status->label }} diff --git a/resources/views/ticket/show.blade.php b/resources/views/ticket/show.blade.php index 74797a9..eb1b7bd 100644 --- a/resources/views/ticket/show.blade.php +++ b/resources/views/ticket/show.blade.php @@ -22,6 +22,17 @@
@endcan + @can('assignTo', $ticket) +
+ @csrf + +
+ @endcan @can('changeStatus', $ticket)
@csrf diff --git a/routes/web.php b/routes/web.php index 66e80ed..b7fded0 100644 --- a/routes/web.php +++ b/routes/web.php @@ -86,6 +86,9 @@ Route::middleware(['auth'])->group(function () { // * Ticket Route::prefix('/ticket')->name('ticket.')->group(function () { + Route::post('/{ticket}/assignTo', [TicketController::class, 'assignTo']) + ->middleware('can:assignTo,ticket') + ->name('assignTo'); Route::post('/{ticket}/reopen', [TicketController::class, 'reopen']) ->middleware('can:reopen,ticket') ->name('reopen');