diff --git a/app/Console/Commands/CreateUser.php b/app/Console/Commands/CreateUser.php
index 29bd2fd..404d0c9 100644
--- a/app/Console/Commands/CreateUser.php
+++ b/app/Console/Commands/CreateUser.php
@@ -2,8 +2,10 @@
namespace App\Console\Commands;
+use App\Models\Project;
use App\Models\User;
use Illuminate\Console\Command;
+use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Hash;
class CreateUser extends Command
@@ -47,7 +49,16 @@ class CreateUser extends Command
if ($this->confirm('Is the user an admin?')) {
$admin = true;
} else {
- $projectId = $this->ask("What's the id of the project (s)he's working on?");
+ $project = null;
+ try {
+ $project = Project::where(
+ 'id',
+ $this->ask("What's the id of the project (s)he's working on?")
+ )->firstOrFail();
+ } catch (ModelNotFoundException $e) {
+ $this->error('The project does not exists');
+ return -1;
+ }
}
$user = new User();
@@ -56,11 +67,12 @@ class CreateUser extends Command
if ($admin) {
$user->role = 1;
} else {
- $user->project_id = $projectId;
+ $user->project_id = $project->id;
}
$user->password = Hash::make($pwd);
$user->save();
$this->info('User created.');
+ return 0;
}
}
diff --git a/app/Enums/TicketStatus.php b/app/Enums/TicketStatus.php
new file mode 100644
index 0000000..75f5df7
--- /dev/null
+++ b/app/Enums/TicketStatus.php
@@ -0,0 +1,34 @@
+ 1,
+ 'inprogress' => 2,
+ 'processed' => 3,
+ 'archived' => 4,
+ ];
+ }
+
+ protected static function labels(): array
+ {
+ return [
+ 'new' => trans('Nouveau'),
+ 'inprogress' => trans('En cours'),
+ 'processed' => trans('Traité'),
+ 'archived' => trans('Archivé'),
+ ];
+ }
+}
diff --git a/app/Enums/TicketType.php b/app/Enums/TicketType.php
new file mode 100644
index 0000000..c6ca449
--- /dev/null
+++ b/app/Enums/TicketType.php
@@ -0,0 +1,28 @@
+ 0,
+ 'evolution' => 1,
+ ];
+ }
+
+ protected static function labels(): array
+ {
+ return [
+ 'bug' => trans('Bug'),
+ 'evolution' => trans('Évolution'),
+ ];
+ }
+}
diff --git a/app/Events/NotifySubscriptions.php b/app/Events/NotifySubscriptions.php
index e9a15ec..1f60396 100644
--- a/app/Events/NotifySubscriptions.php
+++ b/app/Events/NotifySubscriptions.php
@@ -2,22 +2,29 @@
namespace App\Events;
+use App\Models\Ticket;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class NotifySubscriptions
{
- use Dispatchable, InteractsWithSockets, SerializesModels;
+ use Dispatchable;
+ use InteractsWithSockets;
+ use SerializesModels;
- public $ticket, $corpsMaj;
+ /** @var Ticket */
+ public $ticket;
+
+ /** @var string */
+ public $corpsMaj;
/**
* Create a new event instance.
*
* @return void
*/
- public function __construct($ticket, $corpsMaj)
+ public function __construct(Ticket $ticket, string $corpsMaj)
{
$this->ticket = $ticket;
$this->corpsMaj = $corpsMaj;
diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php
index 2b39ed5..ef0910d 100644
--- a/app/Http/Controllers/ProjectController.php
+++ b/app/Http/Controllers/ProjectController.php
@@ -9,6 +9,11 @@ use App\Models\Ticket;
class ProjectController extends Controller
{
+ public function __construct()
+ {
+ $this->authorizeResource(Project::class, 'project');
+ }
+
public function index()
{
$projects = Project::all();
diff --git a/app/Http/Controllers/TicketController.php b/app/Http/Controllers/TicketController.php
index 3ffed0c..f169250 100644
--- a/app/Http/Controllers/TicketController.php
+++ b/app/Http/Controllers/TicketController.php
@@ -2,234 +2,160 @@
namespace App\Http\Controllers;
+use App\Enums\TicketStatus;
+use App\Enums\TicketType;
use App\Events\NotifySubscriptions;
use App\Events\TicketStatusChanged;
+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\Mail\NewTicket;
use App\Models\Message;
use App\Models\Project;
use App\Models\Ticket;
use App\Models\User;
-use Illuminate\Http\Request;
+use Exception;
use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
class TicketController extends Controller
{
- private $status = [1 => "Nouveau", 2 => "En cours", 3 => "Traité", 4 => "Archivé"];
-
- public function index(Request $request)
+ public function __construct()
{
- if (Auth::check()) {
- $tickets = Ticket::where('status', '<>', 4);
+ $this->authorizeResource(Ticket::class, 'ticket');
+ }
- $sortway = $request->sort_way == 'asc' ? 'asc' : null;
- $sortway = $request->sort_way == 'desc' ? 'desc' : null;
+ public function index(SortTickets $request)
+ {
+ $tickets = Ticket::query();
- if ($request->sort_col and $sortway) {
- switch ($request->sort_col) {
- case 'id':
- $tickets = $tickets->orderBy('id', $sortway);
- break;
- case 'project':
- $tickets = $tickets->orderBy('project_id', $sortway);
- break;
- case 'created_at':
- $tickets = $tickets->orderBy('created_at', $sortway);
- break;
- case 'created_by':
- $tickets = $tickets->orderBy('created_by', $sortway);
- break;
- case 'type':
- $tickets = $tickets->orderBy('type', $sortway);
- break;
- case 'status':
- $tickets = $tickets->orderBy('status', $sortway);
- break;
- }
- }
-
- $tickets = $tickets->paginate();
-
- $status = $this->status;
- return view('home', compact('tickets', 'status'));
+ if ($request->status) {
+ $tickets = $tickets->where('status', TicketStatus::archived());
} else {
- return view('home');
+ $tickets = $tickets->where('status', '<>', TicketStatus::archived());
}
+
+ if ($request->sort_col and $request->sort_way) {
+ switch ($request->sort_col) {
+ case 'id':
+ $tickets = $tickets->orderBy('id', $request->sort_way);
+ break;
+ case 'project':
+ $tickets = $tickets->orderBy('project_id', $request->sort_way);
+ break;
+ case 'created_at':
+ $tickets = $tickets->orderBy('created_at', $request->sort_way);
+ break;
+ case 'created_by':
+ $tickets = $tickets->orderBy('created_by', $request->sort_way);
+ break;
+ case 'type':
+ $tickets = $tickets->orderBy('type', $request->sort_way);
+ break;
+ case 'status':
+ $tickets = $tickets->orderBy('status', $request->sort_way);
+ break;
+ }
+ }
+
+ $tickets = $tickets->paginate();
+
+ return view('ticket.index', compact('tickets'));
}
- public function show($id) {
- $ticket = Ticket::find($id);
- $status = $this->status;
+ public function show(Ticket $ticket)
+ {
$messages = $ticket->messages()->orderBy('created_at', 'DESC')->get();
- $isSubscribed = false;
- if (!$ticket)
- abort(404);
- if ($ticket->subscriptions()->where('user_id', Auth::user()->id)->count() !== 0)
- $isSubscribed = true;
- return view('ticket.show', compact('ticket', 'status', 'messages', 'isSubscribed'));
+ $isSubscribed = (bool)$ticket->subscriptions()->where('user_id', Auth::user()->id)->count();
+ return view('ticket.show', compact('ticket', 'messages', 'isSubscribed'));
}
- public function create() {
+ public function create()
+ {
$projects = Project::all();
+ if (!count($projects)) {
+ return redirect()->route('project.create')
+ ->with('warning', 'Vous devez tout d\'abord créer un projet pour y ajouter un ticket');
+ }
return view('ticket.create', compact('projects'));
}
- public function store(Request $request) {
- $request->validate([
- 'text' => 'required|string',
- 'file_ticket' => 'mimes:jpeg,jpg,png,pdf|max:2048',
- 'file_ticket2' => 'mimes:jpeg,jpg,png,pdf|max:2048',
- ]);
+ public function store(StoreTicket $request)
+ {
+ $project = Project::findOrFail($request->project_id);
- if (Auth::user()->role === 1) {
- $projectId = $request->project_id;
- } else {
- $projectId = Auth::user()->project_id;
+ $ticket = new Ticket($request->validated());
+
+ if ($request->file_ticket) {
+ $ticket->file = Ticket::saveFile($project, $ticket, $ticket->type, $request->file_ticket);
+ }
+ if ($request->file_ticket2) {
+ $ticket->file2 = Ticket::saveFile($project, $ticket, $ticket->type, $request->file_ticket2);
}
- $type = $request->type;
- $text = $request->text;
- $fileTicket = $request->file_ticket;
- $fileTicket2 = $request->file_ticket2;
- $ticket = new Ticket();
- $ticket->project_id = $projectId;
- $ticket->type = $type;
- $ticket->text = $text;
- $ticket->created_by = Auth::user()->id;
+ $ticket->user()->associate(auth()->user());
$ticket->save();
- if ($fileTicket !== null) {
- $projectSlug = Str::slug(Project::find($projectId)->name);
- $fileName = $projectSlug . "-" . ($type === "0" ? "bug" : "evo") . "-" . $ticket->id . "." . $fileTicket->extension();
- $fileTicket->move(public_path('fichiers/' . $projectSlug . "/" . $ticket->id . "/"), $fileName);
- $filePath = 'fichiers/' . $projectSlug . "/" . $ticket->id . "/" . $fileName;
+ if (config('mail.MAIL_ADMIN')) {
+ Mail::to(config('mail.MAIL_ADMIN'))->send(new NewTicket($ticket));
+ } else {
+ throw new Exception('Missing MAIL_ADMIN in .env');
}
- if ($fileTicket2 !== null) {
- $projectSlug = Str::slug(Project::find($projectId)->name);
- $fileName2 = $projectSlug . "-" . ($type === "0" ? "bug" : "evo") . "-" . $ticket->id . "-2." . $fileTicket2->extension();
- $fileTicket2->move(public_path('fichiers/' . $projectSlug . "/" . $ticket->id . "/"), $fileName2);
- $filePath2 = 'fichiers/' . $projectSlug . "/" . $ticket->id . "/" . $fileName2;
- }
-
-
- if ($fileTicket !== null)
- $ticket->file = $filePath;
-
- if ($fileTicket2 !== null)
- $ticket->file2 = $filePath2;
-
- if ($fileTicket !== null || $fileTicket2 !== null)
- $ticket->save();
-
-
- Mail::to(config('mail.MAIL_ADMIN'))->send(new NewTicket($ticket));
return redirect()->route('home')->with('success', 'Ticket enregistré !');
}
- public function edit($id) {
- $ticket = Ticket::findOrFail($id);
- if ($ticket->created_by === Auth::user()->id || Auth::user()->role === 1) {
- $projects = Project::all();
- $status = $this->status;
- return view('ticket.edit', compact('ticket', 'projects', 'status'));
- }
- return redirect()->back();
+ // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
+ public function edit(Ticket $ticket)
+ {
+ $projects = Project::all();
+ return view('ticket.edit', compact('ticket', 'projects'));
}
- public function update(Request $request, $id) {
- $request->validate([
- 'text' => 'required|string',
- 'file_ticket' => 'mimes:jpeg,jpg,png,pdf|max:2048',
- 'file_ticket2' => 'mimes:jpeg,jpg,png,pdf|max:2048',
- ]);
+ public function update(UpdateTicket $request, Ticket $ticket)
+ {
+ $project = Project::findOrFail($request->project_id);
+ $ticket->fill($request->validated());
- if (Auth::user()->role === 1) {
- $projectId = $request->project_id;
- $status = $request->status;
- } else {
- $projectId = Auth::user()->project_id;
+ if ($request->file_ticket) {
+ if (File::exists($ticket->file)) {
+ File::delete($ticket->file);
+ }
+ $ticket->file = Ticket::saveFile($project, $ticket, $ticket->type, $request->file_ticket);
}
- $type = $request->type;
- $text = $request->text;
- $fileTicket = $request->file_ticket;
- $fileTicket2 = $request->file_ticket2;
-
- $ticket = Ticket::findOrFail($id);
-
- if ($fileTicket !== null) {
- if ($ticket->file !== null && file_exists(public_path() . $ticket->file))
- unlink(public_path() . $ticket->file);
- $projectSlug = Str::slug(Project::find($projectId)->name);
- $fileName = $projectSlug . "-" . ($type === "0" ? "bug" : "evo") . "-" . $ticket->id . "." . $fileTicket->extension();
- $fileTicket->move(public_path('fichiers/' . $projectSlug . "/" . $ticket->id . "/"), $fileName);
- $filePath = 'fichiers/' . $projectSlug . "/" . $ticket->id . "/" . $fileName;
- }
- if ($fileTicket2 !== null) {
- if ($ticket->file2 !== null && file_exists(public_path() . $ticket->file2))
- unlink(public_path() . $ticket->file2);
- $projectSlug = Str::slug(Project::find($projectId)->name);
- $fileName2 = $projectSlug . "-" . ($type === "0" ? "bug" : "evo") . "-" . $ticket->id . "." . $fileTicket2->extension();
- $fileTicket2->move(public_path('fichiers/' . $projectSlug . "/" . $ticket->id . "/"), $fileName2);
- $filePath2 = 'fichiers/' . $projectSlug . "/" . $ticket->id . "/" . $fileName2;
- }
-
- $ticket->project_id = $projectId;
- $ticket->type = $type;
- $ticket->text = $text;
- if ($fileTicket !== null)
- $ticket->file = $filePath;
- if ($fileTicket2 !== null)
- $ticket->file2 = $filePath2;
- if (Auth::user()->role === 1) {
- $ticket->status = $status;
- event(new TicketStatusChanged($ticket, $this->status));
+ 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->save();
- return redirect()->route('edit-ticket', $ticket->id)->with('success', 'Modifications enregistrées !');
+ return redirect()->route('ticket.edit', $ticket->id)->with('success', 'Modifications enregistrées !');
}
- public function changeStatus(Request $request, $id) {
- $ticket = Ticket::find($id);
- if (!$ticket)
- abort(404);
- $ticket->status = $request->status;
+ public function changeStatus(ChangeTicketStatus $request, Ticket $ticket)
+ {
+ $ticket->fill($request->validated());
$ticket->save();
- event(new TicketStatusChanged($ticket, $this->status));
- event(new NotifySubscriptions($ticket, "Le ticket à changé de status : " . $this->status[$ticket->status]));
-
return back();
}
- public function createMessage(Request $request) {
- $request->validate([
- 'message' => 'required',
- ]);
-
- if ($request->file_message !== null) {
- $request->validate([
- 'file_message' => 'mimes:jpeg,jpg,png,pdf|max:2048',
- ]);
- }
-
- $ticketId = $request->ticket_id;
+ public function createMessage(CreateMessage $request, Ticket $ticket)
+ {
$fileMessage = $request->file_message;
- $ticket = Ticket::find($ticketId);
-
- if (Auth::user()->role !== 1 && (!$ticket || Auth::user()->id !== $ticket->created_by))
- return back()->withErrors("Les données entrées sont incorrects.");
-
- $message = new Message();
- $message->ticket_id = $ticketId;
- $message->user_id = Auth::user()->id;
- $message->message = $request->message;
+ $message = new Message($request->validated());
+ $message->user()->associate(auth()->user());
+ $message->ticket()->associate($ticket);
$message->save();
- if ($request->file_message !== null) {
+ if ($request->file_message) {
$projectSlug = Str::slug($ticket->project->name);
$fileMessage = $request->file_message;
$fileName = $projectSlug . '-ticket-' . $ticket->id . '-message-' . $message->id . '.' . $fileMessage->extension();
@@ -239,13 +165,19 @@ class TicketController extends Controller
$message->save();
}
- event(new NotifySubscriptions($ticket, "Nouvelle réponse de " . $message->user->name . " :
" . $message->message));
-
return back();
}
- public function subscribe(Request $request, $id) {
- User::find(Auth::user()->id)->subscriptions()->toggle($id);
+ public function setToArchive(Ticket $ticket)
+ {
+ $ticket->status = TicketStatus::archived();
+ $ticket->saveOrFail();
+ return redirect()->route('ticket.index');
+ }
+
+ public function subscribe(Ticket $ticket)
+ {
+ User::find(Auth::user()->id)->subscriptions()->toggle($ticket->id);
return back();
}
}
diff --git a/app/Http/Requests/ChangeTicketStatus.php b/app/Http/Requests/ChangeTicketStatus.php
new file mode 100644
index 0000000..4d681d0
--- /dev/null
+++ b/app/Http/Requests/ChangeTicketStatus.php
@@ -0,0 +1,31 @@
+ [
+ 'required',
+ // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
+ function ($attribute, $value, $fail) {
+ if (!in_array($value, TicketStatus::toValues())) {
+ $fail(__('Veuillez choisir entre :values', [
+ 'value' => implode(',', TicketStatus::toLabels())
+ ]));
+ }
+ }
+ ],
+ ];
+ }
+}
diff --git a/app/Http/Requests/CreateMessage.php b/app/Http/Requests/CreateMessage.php
new file mode 100644
index 0000000..7834188
--- /dev/null
+++ b/app/Http/Requests/CreateMessage.php
@@ -0,0 +1,21 @@
+ 'required|min:10',
+ 'file_message' => 'sometimes|mimes:jpeg,jpg,png,pdf|max:2048'
+ ];
+ }
+}
diff --git a/app/Http/Requests/SortTickets.php b/app/Http/Requests/SortTickets.php
new file mode 100644
index 0000000..3112e19
--- /dev/null
+++ b/app/Http/Requests/SortTickets.php
@@ -0,0 +1,42 @@
+check();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'sort_way' => 'sometimes|in:asc,desc',
+ 'status' => [
+ 'sometimes',
+ // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
+ function ($attribute, $value, $fail) {
+ if (!in_array($value, TicketStatus::toValues())) {
+ $fail(__('Veuillez choisir entre :values', [
+ 'value' => implode(',', TicketStatus::toLabels())
+ ]));
+ }
+ }
+ ],
+ ];
+ }
+}
diff --git a/app/Http/Requests/StoreTicket.php b/app/Http/Requests/StoreTicket.php
new file mode 100644
index 0000000..78370a9
--- /dev/null
+++ b/app/Http/Requests/StoreTicket.php
@@ -0,0 +1,63 @@
+user()->project_id) {
+ $this->merge([
+ 'project_id' => auth()->user()->project_id,
+ 'status' => TicketStatus::new()->value,
+ ]);
+ }
+ return auth()->check();
+ }
+
+ /**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array
+ */
+ public function rules()
+ {
+ return [
+ 'project_id' => 'required|exists:projects,id',
+ 'type' => [
+ 'required',
+ // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
+ function ($attribute, $value, $fail) {
+ if (!in_array($value, TicketType::toValues())) {
+ $fail(__('Veuillez choisir entre :values', [
+ 'value' => implode(',', TicketType::toLabels())
+ ]));
+ }
+ }
+ ],
+ 'status' => [
+ 'required',
+ // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
+ function ($attribute, $value, $fail) {
+ if (!in_array($value, TicketStatus::toValues())) {
+ $fail(__('Veuillez choisir entre :values', [
+ 'value' => implode(',', TicketStatus::toLabels())
+ ]));
+ }
+ }
+ ],
+ 'text' => 'required|string|min:40',
+ 'file_ticket' => 'mimes:jpeg,jpg,png,pdf|max:2048',
+ 'file_ticket2' => 'mimes:jpeg,jpg,png,pdf|max:2048',
+ ];
+ }
+}
diff --git a/app/Http/Requests/UpdateTicket.php b/app/Http/Requests/UpdateTicket.php
new file mode 100644
index 0000000..167da51
--- /dev/null
+++ b/app/Http/Requests/UpdateTicket.php
@@ -0,0 +1,21 @@
+user()->project_id) {
+ $this->merge([
+ 'project_id' => auth()->user()->project_id,
+ 'status' => TicketStatus::new()->value,
+ ]);
+ }
+ return auth()->check() or
+ auth()->user()->role === 1 or
+ request()->route()->ticket->created_by === auth()->user()->id;
+ }
+}
diff --git a/app/Listeners/AddStatusMessages.php b/app/Listeners/AddStatusMessages.php
index 1985c35..1c05a72 100644
--- a/app/Listeners/AddStatusMessages.php
+++ b/app/Listeners/AddStatusMessages.php
@@ -27,7 +27,7 @@ class AddStatusMessages
{
$message = new Message();
$message->ticket_id = $event->ticket->id;
- $message->message = "Le status est passé à : " . $event->status[$event->ticket->status];
+ $message->message = "Le status est passé à : " . $event->ticket->status->label;
$message->save();
}
}
diff --git a/app/Listeners/SendNotificationMail.php b/app/Listeners/SendNotificationMail.php
index 55fb583..178df4a 100644
--- a/app/Listeners/SendNotificationMail.php
+++ b/app/Listeners/SendNotificationMail.php
@@ -12,16 +12,6 @@ use Illuminate\Support\Facades\Mail;
class SendNotificationMail
{
- /**
- * Create the event listener.
- *
- * @return void
- */
- public function __construct()
- {
- //
- }
-
/**
* Handle the event.
*
@@ -30,8 +20,10 @@ class SendNotificationMail
*/
public function handle(NotifySubscriptions $event)
{
- foreach($event->ticket->subscriptions as $sub)
- if ($sub->email !== Auth::user()->email)
+ foreach ($event->ticket->subscriptions as $sub) {
+ if ($sub->email !== Auth::user()->email) {
Mail::to($sub->email)->send(new TicketUpdate($event->ticket, $event->corpsMaj));
+ }
+ }
}
}
diff --git a/app/Models/Message.php b/app/Models/Message.php
index 471f7b1..dfbe163 100644
--- a/app/Models/Message.php
+++ b/app/Models/Message.php
@@ -2,18 +2,55 @@
namespace App\Models;
+use App\Enums\TicketType;
+use App\Events\NotifySubscriptions;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
+use Illuminate\Http\UploadedFile;
class Message extends Model
{
use HasFactory;
- public function ticket() {
- return $this->belongsTo('App\Models\Ticket', 'ticket_id');
+ protected $fillable = [
+ 'message'
+ ];
+
+ protected static function booted()
+ {
+ static::created(function (Message $message) {
+ event(new NotifySubscriptions(
+ $message->ticket,
+ trans('Nouvelle réponse de :name :
:message', [
+ 'name' => $message->user->name,
+ 'message' => $message->message
+ ])
+ ));
+ });
}
- public function user() {
- return $this->belongsTo('App\Models\User', 'user_id');
+ public function ticket()
+ {
+ return $this->belongsTo(Ticket::class, 'ticket_id');
+ }
+
+ public function user()
+ {
+ return $this->belongsTo(User::class, 'user_id');
+ }
+
+ public function saveFile(Project $project, Ticket $ticket, UploadedFile $file)
+ {
+ $projectAssetFullPath = Project::ticketAssetsFullPath($project->slug_name);
+ $fileAssetPath = Project::ticketAssetsPath($project->slug_name);
+ $fileName = sprintf(
+ '%s-%s-%d-%d.%s',
+ $project->slug_name,
+ $ticket->id,
+ rand(),
+ $file->extension()
+ );
+ $file->move(sprintf('%s/messages/%d', $projectAssetFullPath, $ticket->id), $fileName);
+ return sprintf('%s/%d/%s', $fileAssetPath, $ticket->id, $fileName);
}
}
diff --git a/app/Models/Project.php b/app/Models/Project.php
index 5e7aea4..f1498f8 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -16,15 +16,15 @@ class Project extends Model
protected static function booted()
{
static::created(function (Project $project) {
- $path = Project::ticketAssetsPath($project->slug_name);
+ $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::ticketAssetsPath($oldName);
- $newPath = Project::ticketAssetsPath($project->slug_name);
+ $oldPath = Project::ticketAssetsFullPath($oldName);
+ $newPath = Project::ticketAssetsFullPath($project->slug_name);
if (File::exists($oldPath)) {
File::move($oldPath, $newPath);
} else {
@@ -32,7 +32,7 @@ class Project extends Model
}
});
static::deleted(function (Project $project) {
- $path = Project::ticketAssetsPath($project->slug_name);
+ $path = Project::ticketAssetsFullPath($project->slug_name);
if (File::exists($path)) {
File::deleteDirectory($path);
}
@@ -41,7 +41,7 @@ class Project extends Model
public function tickets()
{
- return $this->hasMany('App\Models\Ticket', 'project_id');
+ return $this->hasMany(Ticket::class, 'project_id');
}
public function getSlugNameAttribute()
@@ -49,8 +49,13 @@ class Project extends Model
return Str::slug($this->name);
}
- protected static function ticketAssetsPath(string $str)
+ public static function ticketAssetsFullPath(string $str)
{
- return public_path(sprintf('fichiers/%s', $str));
+ return public_path(self::ticketAssetsPath($str));
+ }
+
+ public static function ticketAssetsPath(string $str)
+ {
+ return sprintf('fichiers/%s', $str);
}
}
diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php
index ff43a4d..45c8282 100644
--- a/app/Models/Ticket.php
+++ b/app/Models/Ticket.php
@@ -2,26 +2,79 @@
namespace App\Models;
+use App\Enums\TicketStatus;
+use App\Enums\TicketType;
+use App\Events\NotifySubscriptions;
+use App\Events\TicketStatusChanged;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
+use Illuminate\Http\UploadedFile;
+use Kwaadpepper\Enum\Traits\CastsEnums;
class Ticket extends Model
{
use HasFactory;
+ use CastsEnums;
- public function project() {
- return $this->belongsTo('App\Models\Project', 'project_id');
+ protected $enumCasts = [
+ 'type' => TicketType::class,
+ 'status' => TicketStatus::class
+ ];
+
+ protected $fillable = [
+ 'project_id',
+ 'type',
+ 'status',
+ 'text'
+ ];
+
+ protected static function booted()
+ {
+ static::updating(function (Ticket $ticket) {
+ $oldStatus = Ticket::where('id', $ticket->id)->first()->status;
+ if (!$ticket->status->equals($oldStatus)) {
+ event(new TicketStatusChanged($ticket, $oldStatus));
+ event(new NotifySubscriptions($ticket, sprintf(
+ 'Le ticket à changé de status : %s',
+ $ticket->status->label
+ )));
+ }
+ });
}
- public function user() {
- return $this->belongsTo('App\Models\User', 'created_by');
+ public function project()
+ {
+ return $this->belongsTo(Project::class);
}
- public function messages() {
- return $this->hasMany('App\Models\Message', 'ticket_id');
+ public function user()
+ {
+ return $this->belongsTo(User::class, 'created_by');
}
- public function subscriptions() {
- return $this->belongsToMany('App\Models\User', 'subscriptions');
+ public function messages()
+ {
+ return $this->hasMany(Message::class, 'ticket_id');
+ }
+
+ public function subscriptions()
+ {
+ return $this->belongsToMany(User::class, 'subscriptions');
+ }
+
+ public static function saveFile(Project $project, Ticket $ticket, TicketType $type, UploadedFile $file)
+ {
+ $projectAssetFullPath = Project::ticketAssetsFullPath($project->slug_name);
+ $fileAssetPath = Project::ticketAssetsPath($project->slug_name);
+ $fileName = sprintf(
+ '%s-%s-%d-%d.%s',
+ $project->slug_name,
+ $type->getDefinition(),
+ $ticket->id,
+ rand(),
+ $file->extension()
+ );
+ $file->move(sprintf('%s/%d', $projectAssetFullPath, $ticket->id), $fileName);
+ return sprintf('%s/%d/%s', $fileAssetPath, $ticket->id, $fileName);
}
}
diff --git a/app/Models/User.php b/app/Models/User.php
index e1034d2..1bb0266 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -2,14 +2,14 @@
namespace App\Models;
-use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
- use HasFactory, Notifiable;
+ use HasFactory;
+ use Notifiable;
/**
* The attributes that are mass assignable.
@@ -41,15 +41,23 @@ class User extends Authenticatable
'email_verified_at' => 'datetime',
];
- public function tickets() {
- return $this->hasMany("App\Models\Ticket", 'created_by');
+ public function tickets()
+ {
+ return $this->hasMany(Ticket::class, 'created_by');
}
- public function messages() {
- return $this->hasMany('App\Models\Messages', 'user_id');
+ public function messages()
+ {
+ return $this->hasMany(Messages::class, 'user_id');
}
- public function subscriptions() {
- return $this->belongsToMany('App\Models\Ticket', 'subscriptions');
+ public function subscriptions()
+ {
+ return $this->belongsToMany(Ticket::class, 'subscriptions');
+ }
+
+ public function project()
+ {
+ return $this->belongsTo(Project::class);
}
}
diff --git a/app/Policies/ProjectPolicy.php b/app/Policies/ProjectPolicy.php
new file mode 100644
index 0000000..9513d25
--- /dev/null
+++ b/app/Policies/ProjectPolicy.php
@@ -0,0 +1,99 @@
+role === 1;
+ }
+
+ /**
+ * Determine whether the user can view the model.
+ *
+ * @param \App\Models\User $user
+ * @param \App\Models\Project $project
+ * @return mixed
+ */
+ //phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
+ public function view(User $user, Project $project)
+ {
+ return $user->role === 1;
+ }
+
+ /**
+ * Determine whether the user can create models.
+ *
+ * @param \App\Models\User $user
+ * @return mixed
+ */
+ public function create(User $user)
+ {
+ return $user->role === 1;
+ }
+
+ /**
+ * Determine whether the user can update the model.
+ *
+ * @param \App\Models\User $user
+ * @param \App\Models\Project $project
+ * @return mixed
+ */
+ //phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
+ public function update(User $user, Project $project)
+ {
+ return $user->role === 1;
+ }
+
+ /**
+ * Determine whether the user can delete the model.
+ *
+ * @param \App\Models\User $user
+ * @param \App\Models\Project $project
+ * @return mixed
+ */
+ //phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
+ public function delete(User $user, Project $project)
+ {
+ return $user->role === 1;
+ }
+
+ /**
+ * Determine whether the user can restore the model.
+ *
+ * @param \App\Models\User $user
+ * @param \App\Models\Project $project
+ * @return mixed
+ */
+ //phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
+ public function restore(User $user, Project $project)
+ {
+ return $user->role === 1;
+ }
+
+ /**
+ * Determine whether the user can permanently delete the model.
+ *
+ * @param \App\Models\User $user
+ * @param \App\Models\Project $project
+ * @return mixed
+ */
+ //phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
+ public function forceDelete(User $user, Project $project)
+ {
+ return $user->role === 1;
+ }
+}
diff --git a/app/Policies/TicketPolicy.php b/app/Policies/TicketPolicy.php
new file mode 100644
index 0000000..de00932
--- /dev/null
+++ b/app/Policies/TicketPolicy.php
@@ -0,0 +1,123 @@
+role === 1 or $ticket->created_by == $user->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 === 1 or $ticket->created_by == $user->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 === 1;
+ }
+
+ // 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)
+ {
+ if ($user->role !== 1 and $ticket->created_by !== $user->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();
+ }
+}
diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php
index ce74491..3a82fd0 100644
--- a/app/Providers/AuthServiceProvider.php
+++ b/app/Providers/AuthServiceProvider.php
@@ -2,18 +2,22 @@
namespace App\Providers;
+use App\Models\Ticket;
+use App\Policies\ProjectPolicy;
+use App\Policies\TicketPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
-use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
+
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
- // 'App\Models\Model' => 'App\Policies\ModelPolicy',
+ Project::class => ProjectPolicy::class,
+ Ticket::class => TicketPolicy::class
];
/**
diff --git a/config/app.php b/config/app.php
index 8bfe3b7..c697c9a 100644
--- a/config/app.php
+++ b/config/app.php
@@ -226,7 +226,8 @@ return [
'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
-
+ 'TicketType' => App\Enums\TicketType::class,
+ 'TicketStatus' => App\Enums\TicketStatus::class
],
];
diff --git a/resources/css/app.css b/resources/css/app.css
deleted file mode 100644
index e69de29..0000000
diff --git a/resources/sass/app.scss b/resources/sass/app.scss
index 3193ffa..fcf3e2d 100644
--- a/resources/sass/app.scss
+++ b/resources/sass/app.scss
@@ -6,3 +6,13 @@
// Bootstrap
@import '~bootstrap/scss/bootstrap';
+
+a.col_sort {
+ &:link,:visited,:hover,:active {
+ text-decoration: none;
+ }
+}
+
+.container-xxl {
+ max-width: 1340px !important;
+}
diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php
index e4f00e3..65d5188 100644
--- a/resources/views/home.blade.php
+++ b/resources/views/home.blade.php
@@ -1,114 +1,8 @@
@extends('layout.app')
-@section('tickets', 'active')
-
@section('content')
| - # - @include('modules.sorter', ['colname' => 'id']) - | -- Projet - @include('modules.sorter', ['colname' => 'project']) - | -- Type - @include('modules.sorter', ['colname' => 'type']) | -Fichier | -Fichier 2 | -- Date de création - @include('modules.sorter', ['colname' => 'created_at']) | -- Créé par - @include('modules.sorter', ['colname' => 'created_by']) | -- Status - @include('modules.sorter', ['colname' => 'status']) - | -Contenu | - @if (Auth::user()->role === 1) -- @endif - - - @foreach ($tickets as $ticket) - @if (Auth::user()->project_id === $ticket->project->id || Auth::user()->role === 1) - |
|---|---|---|---|---|---|---|---|---|---|
| - {{ $ticket->id }} - | -{{ $ticket->project->name }} | -- @if ($ticket->type === 0) - Bug - @else - Évolution - @endif - | -- @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') }} | -{{ $ticket->user->name }} | -{{ $status[$ticket->status] }} | -- {{-- Modal du ticket --}} - - - - - - | -- @if (Auth::user()->role === 1) - Modifier - @endif - | -
Pour consulter vos tickets, merci de vous connecter : Connexion
- @endauth