From c471a97a23a4cb011a7f1793782c143f49c3b9a9 Mon Sep 17 00:00:00 2001 From: James Collins Date: Thu, 11 May 2023 16:49:12 +1000 Subject: [PATCH] add event users --- app/Conductors/Conductor.php | 2 +- app/Conductors/EventConductor.php | 8 +- app/Http/Controllers/Api/EventController.php | 111 +++++++++++++++--- app/Http/Controllers/Api/UserController.php | 31 ++++- app/Models/Event.php | 14 +++ app/Models/EventUsers.php | 44 +++++++ app/Models/User.php | 20 ++-- ..._05_11_033621_create_event_users_table.php | 36 ++++++ routes/api.php | 6 + 9 files changed, 242 insertions(+), 30 deletions(-) create mode 100644 app/Models/EventUsers.php create mode 100644 database/migrations/2023_05_11_033621_create_event_users_table.php diff --git a/app/Conductors/Conductor.php b/app/Conductors/Conductor.php index 593172b..889dc68 100644 --- a/app/Conductors/Conductor.php +++ b/app/Conductors/Conductor.php @@ -301,7 +301,7 @@ class Conductor $transformedCollection = collect(); foreach ($collection as $item) { - if ($conductor->viewable($item)) { + if ($conductor->viewable($item) === true) { $transformedCollection->push($conductor->transformModel($item)); } } diff --git a/app/Conductors/EventConductor.php b/app/Conductors/EventConductor.php index 43a5c5d..a2b482a 100644 --- a/app/Conductors/EventConductor.php +++ b/app/Conductors/EventConductor.php @@ -106,8 +106,12 @@ class EventConductor extends Conductor */ public function includeAttachments(Model $model) { - return $model->attachments()->get()->map(function ($attachment) { - return MediaConductor::includeModel(request(), 'attachments', $attachment->media); + $user = auth()->user(); + + return $model->attachments()->get()->map(function ($attachment) use ($user) { + if ($attachment->private === false || ($user !== null && ($user->hasPermission('admin/events') === true || $attachment->users->contains($user) === true))) { + return MediaConductor::includeModel(request(), 'attachments', $attachment->media); + } }); } diff --git a/app/Http/Controllers/Api/EventController.php b/app/Http/Controllers/Api/EventController.php index 9145278..52be0b2 100644 --- a/app/Http/Controllers/Api/EventController.php +++ b/app/Http/Controllers/Api/EventController.php @@ -6,8 +6,10 @@ use App\Enum\HttpResponseCodes; use App\Models\Event; use App\Conductors\EventConductor; use App\Conductors\MediaConductor; +use App\Conductors\UserConductor; use App\Http\Requests\EventRequest; use App\Models\Media; +use App\Models\User; use Illuminate\Http\Request; class EventController extends ApiController @@ -18,7 +20,7 @@ class EventController extends ApiController public function __construct() { $this->middleware('auth:sanctum') - ->only(['store','update','destroy']); + ->only(['store','update','destroy', 'userAdd', 'userUpdate', 'userDelete']); } /** @@ -111,11 +113,8 @@ class EventController extends ApiController * Get a list of attachments related to this model. * * @param Request $request The user request. - * @param Article $article The article model. - * @return JsonResponse Returns the article attachments. - * @throws InvalidFormatException - * @throws BindingResolutionException - * @throws InvalidCastException + * @param Event $event The event model. + * @return JsonResponse Returns the event attachments. */ public function getAttachments(Request $request, Event $event) { @@ -134,15 +133,13 @@ class EventController extends ApiController * Store an attachment related to this model. * * @param Request $request The user request. - * @param Article $article The article model. + * @param Event $event The event model. * @return JsonResponse The response. - * @throws BindingResolutionException - * @throws MassAssignmentException */ public function storeAttachment(Request $request, Event $event) { if (EventConductor::updatable($event) === true) { - if ($request->has("medium") && Media::find($request->medium)) { + if ($request->has("medium") === true && Media::find($request->medium) !== null) { $event->attachments()->create(['media_id' => $request->medium]); return $this->respondCreated(); } @@ -157,10 +154,8 @@ class EventController extends ApiController * Update/replace attachments related to this model. * * @param Request $request The user request. - * @param Article $article The related model. + * @param Event $event The related model. * @return JsonResponse - * @throws BindingResolutionException - * @throws MassAssignmentException */ public function updateAttachments(Request $request, Event $event) { @@ -175,7 +170,7 @@ class EventController extends ApiController // Delete attachments that are not in $mediaIds foreach ($attachments as $attachment) { - if (!in_array($attachment->media_id, $mediaIds)) { + if (in_array($attachment->media_id, $mediaIds) === false) { $attachment->delete(); } } @@ -185,13 +180,13 @@ class EventController extends ApiController $found = false; foreach ($attachments as $attachment) { - if ($attachment->media_id == $mediaId) { + if ($attachment->media_id === $mediaId) { $found = true; break; } } - if (!$found) { + if ($found === false) { $event->attachments()->create(['media_id' => $mediaId]); } } @@ -204,11 +199,11 @@ class EventController extends ApiController /** * Delete a specific related attachment. + * * @param Request $request The user request. - * @param Article $article The model. + * @param Event $event The model. * @param Media $medium The attachment medium. * @return JsonResponse - * @throws BindingResolutionException */ public function deleteAttachment(Request $request, Event $event, Media $medium) { @@ -224,7 +219,7 @@ class EventController extends ApiController } } - if ($deleted) { + if ($deleted === true) { // Attachment was deleted successfully return $this->respondNoContent(); } else { @@ -235,4 +230,82 @@ class EventController extends ApiController return $this->respondForbidden(); } + + public function userList(Request $request, Event $event) + { + $authUser = $request->user(); + $eventUsers = $event->users; + + if ($authUser !== null) { + $isAdmin = $authUser->hasPermission('admin/events'); + $isEventUser = $eventUsers->contains($authUser->id); + + if ($isAdmin === true || $isEventUser === true) { + if ($isAdmin === false) { + $eventUsers = $eventUsers->filter(function ($user) use ($authUser) { + return $user->id === $authUser->id; + }); + } + + return $this->respondAsResource(UserConductor::collection($request, $eventUsers), ['isCollection' => true, 'resourceName' => 'users']); + } + + return $this->respondNotFound(); + } + + return $this->respondForbidden(); + } + + public function userAdd(Request $request, Event $event) + { + $authUser = $request->user(); + if ($authUser !== null && $authUser->hasPermission('admin/events') === true) { + if ($request->has("users") === true) { + $eventUsers = $event->users()->pluck('user_id')->toArray(); // Get the current users in the event + $requestedUsers = $request->input("users"); // Get the requested users + + $usersToAdd = array_diff($requestedUsers, $eventUsers); // Users to add + $usersToRemove = array_diff($eventUsers, $requestedUsers); // Users to remove + + // Add missing users + foreach ($usersToAdd as $userToAdd) { + if (User::find($userToAdd) !== null) { + $event->users()->attach($userToAdd); + } + } + + // Remove extra users + foreach ($usersToRemove as $userToRemove) { + $event->users()->detach($userToRemove); + } + + return $this->respondNoContent(); + }//end if + + return $this->respondWithErrors(['users' => 'The user list was not found']); + }//end if + + return $this->respondForbidden(); + } + + public function userUpdate(Request $request, Event $event) + { + // only admin/events permitted + } + + public function userDelete(Request $request, Event $event, User $user) + { + $authUser = $request->user(); + if ($authUser !== null && $authUser->hasPermission('admin/events') === true) { + $eventUsers = $event->users; + if ($eventUsers->find($user->id) !== null) { + $eventUsers->detach($user->id); + return $this->respondNoContent(); + } else { + return $this->respondNotFound(); + } + } + + return $this->respondForbidden(); + } } diff --git a/app/Http/Controllers/Api/UserController.php b/app/Http/Controllers/Api/UserController.php index fdecf14..4b30169 100644 --- a/app/Http/Controllers/Api/UserController.php +++ b/app/Http/Controllers/Api/UserController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Api; +use App\Conductors\EventConductor; use App\Enum\HttpResponseCodes; use App\Http\Requests\UserRequest; use App\Http\Requests\UserForgotPasswordRequest; @@ -20,6 +21,8 @@ use App\Models\UserCode; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use App\Conductors\UserConductor; +use Illuminate\Http\JsonResponse; +use Illuminate\Contracts\Container\BindingResolutionException; class UserController extends ApiController { @@ -37,7 +40,8 @@ class UserController extends ApiController 'forgotPassword', 'resetPassword', 'verifyEmail', - 'resendVerifyEmailCode' + 'resendVerifyEmailCode', + 'eventList', ]); } @@ -330,4 +334,29 @@ class UserController extends ApiController return $this->respondNotFound(); } + + /** + * Return a JSON event list of a user. + * + * @param Request $request The http request. + * @param User $user The specified user. + * @return JsonResponse + */ + public function eventList(Request $request, User $user) + { + if ($request->user() !== null && ($request->user() === $user || $request->user()->hasPermission('admin/events') === true)) { + $collection = $user->events; + $total = $collection->count(); + + $collection = EventConductor::collection($request, $collection); + return $this->respondAsResource( + $collection, + ['isCollection' => true, + 'appendData' => ['total' => $total] + ] + ); + } else { + return $this->respondForbidden(); + } + } } diff --git a/app/Models/Event.php b/app/Models/Event.php index 77d3822..ebd791c 100644 --- a/app/Models/Event.php +++ b/app/Models/Event.php @@ -5,6 +5,8 @@ namespace App\Models; use App\Traits\Uuids; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Eloquent\Relations\MorphMany; class Event extends Model { @@ -36,9 +38,21 @@ class Event extends Model /** * Get all of the article's attachments. + * + * @return MorphMany */ public function attachments() { return $this->morphMany('App\Models\Attachment', 'attachable'); } + + /** + * Get all the associated users. + * + * @return BelongsToMany + */ + public function users() + { + return $this->belongsToMany(User::class, 'event_user', 'event_id', 'user_id'); + } } diff --git a/app/Models/EventUsers.php b/app/Models/EventUsers.php new file mode 100644 index 0000000..f1ea84b --- /dev/null +++ b/app/Models/EventUsers.php @@ -0,0 +1,44 @@ + + */ + protected $fillable = [ + 'event_id', + 'user_id', + ]; + + + /** + * Get the event for this attachment. + * + * @return BelongsTo + */ + public function event() + { + return $this->belongsTo(Event::class); + } + + /** + * Get the user for this attachment. + * + * @return BelongsTo + */ + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 9ec6998..e915254 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,6 +6,7 @@ namespace App\Models; use App\Traits\Uuids; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; @@ -76,11 +77,6 @@ class User extends Authenticatable implements Auditable ]; - // public function getPermissionsAttribute() { - // return $this->permissions()->pluck('permission')->toArray(); - // } - - /** * Get the list of files of the user * @@ -120,7 +116,7 @@ class User extends Authenticatable implements Auditable */ public function givePermission($permissions) { - if (!is_array($permissions)) { + if (is_array($permissions) === false) { $permissions = [$permissions]; } @@ -145,7 +141,7 @@ class User extends Authenticatable implements Auditable */ public function revokePermission($permissions) { - if (!is_array($permissions)) { + if (is_array($permissions) === false) { $permissions = [$permissions]; } @@ -193,4 +189,14 @@ class User extends Authenticatable implements Auditable { return $this->hasMany(UserLogins::class); } + + /** + * Get the events associated with the user. + * + * @return BelongsToMany + */ + public function events() + { + return $this->belongsToMany(Event::class, 'event_user', 'user_id', 'event_id'); + } } diff --git a/database/migrations/2023_05_11_033621_create_event_users_table.php b/database/migrations/2023_05_11_033621_create_event_users_table.php new file mode 100644 index 0000000..4405cc3 --- /dev/null +++ b/database/migrations/2023_05_11_033621_create_event_users_table.php @@ -0,0 +1,36 @@ +id(); + $table->uuid('event_id'); + $table->uuid('user_id'); + $table->timestamps(); + + $table->foreign('event_id')->references('id')->on('event')->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('user')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('event_users'); + } +}; diff --git a/routes/api.php b/routes/api.php index 4d2a0ab..50c9fdc 100644 --- a/routes/api.php +++ b/routes/api.php @@ -34,6 +34,7 @@ Route::post('/users/forgotPassword', [UserController::class, 'forgotPassword']); Route::post('/users/resetPassword', [UserController::class, 'resetPassword']); Route::post('/users/resendVerifyEmailCode', [UserController::class, 'resendVerifyEmailCode']); Route::post('/users/verifyEmail', [UserController::class, 'verifyEmail']); +Route::get('/users/{user}/events', [UserController::class, 'eventList']); Route::apiResource('media', MediaController::class); Route::get('media/{medium}/download', [MediaController::class, 'download']); @@ -44,6 +45,11 @@ Route::apiAttachmentResource('articles', ArticleController::class); Route::apiResource('events', EventController::class); Route::apiAttachmentResource('events', EventController::class); +Route::get('/events/{event}/users', [EventController::class, 'userList']); +Route::post('/events/{event}/users', [EventController::class, 'userAdd']); +Route::match(['put', 'patch'], '/events/{event}/users', [EventController::class, 'userUpdate']); +Route::delete('/events/{event}/users/{user}', [EventController::class, 'userDelete']); + Route::post('/contact', [ContactController::class, 'send']); Route::apiResource('/shortlinks', ShortlinkController::class);