From df9b64a4e2321b1241a4b5a16b798fce6fbf6d7a Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sat, 10 Aug 2024 18:09:32 -0500 Subject: [PATCH] When a user creates a school, make them the head Work on #64 --- app/Actions/Schools/SetHeadDirector.php | 40 +++++++++++++++ app/Exceptions/AuditionAdminException.php | 10 ++++ app/Http/Controllers/SchoolController.php | 18 +++---- app/Models/User.php | 7 +++ app/Providers/AppServiceProvider.php | 8 ++- app/helpers.php | 12 +++++ tests/Feature/Actions/SetHeadDirectorTest.php | 51 +++++++++++++++++++ 7 files changed, 136 insertions(+), 10 deletions(-) create mode 100644 app/Actions/Schools/SetHeadDirector.php create mode 100644 app/Exceptions/AuditionAdminException.php create mode 100644 tests/Feature/Actions/SetHeadDirectorTest.php diff --git a/app/Actions/Schools/SetHeadDirector.php b/app/Actions/Schools/SetHeadDirector.php new file mode 100644 index 0000000..75377e7 --- /dev/null +++ b/app/Actions/Schools/SetHeadDirector.php @@ -0,0 +1,40 @@ +setHeadDirector($user, $school); + } + + /** + * @throws AuditionAdminException + */ + public function setHeadDirector(User $user): void + { + if (is_null($user->school_id)) { + throw new AuditionAdminException('User is not associated with a school'); + } + foreach ($user->school->directors as $director) { + $director->removeFlag('head_director'); + } + $user->addFlag('head_director'); + + $logMessage = 'Set '.$user->full_name().' as head director at '.$user->school->name; + $logAffected = ['users' => [$user->id], 'schools' => [$user->school_id]]; + auditionLog($logMessage, $logAffected); + } +} diff --git a/app/Exceptions/AuditionAdminException.php b/app/Exceptions/AuditionAdminException.php new file mode 100644 index 0000000..7f6ebba --- /dev/null +++ b/app/Exceptions/AuditionAdminException.php @@ -0,0 +1,10 @@ +user()->cannot('create', School::class)) { abort(403); @@ -70,14 +72,12 @@ class SchoolController extends Controller 'schools' => [$school->id], ], ]); - - auth()->user()->addFlag('head_director'); // If user is creating a school, they are initially the head - AuditLogEntry::create([ - 'user' => auth()->user()->email, - 'ip_address' => request()->ip(), - 'message' => 'Marked '.auth()->user()->full_name().' as head director for '.$school->name, - 'affected' => ['schools' => [$school->id], 'users' => [auth()->user()->id]], - ]); + auth()->user()->refresh(); + try { + $headSetter->setHeadDirector(auth()->user()); + } catch (AuditionAdminException $e) { + redirect(route('schools.show', $school))->with('error', 'Could not set as head director'); + } } diff --git a/app/Models/User.php b/app/Models/User.php index adbbb4f..21b0722 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -187,6 +187,13 @@ class User extends Authenticatable implements MustVerifyEmail $this->load('flags'); } + public function removeFlag($flag): void + { + // remove related userFlag where flag_name = $flag + $this->flags()->where('flag_name', $flag)->delete(); + $this->load('flags'); + } + public function scoresForEntry($entry) { return $this->scoreSheets->where('entry_id', '=', $entry)->first()?->subscores; diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 6b048c4..f1e8467 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,9 @@ namespace App\Providers; +use App\Actions\Entries\CreateEntry; +use App\Actions\Entries\UpdateEntry; +use App\Actions\Schools\SetHeadDirector; use App\Actions\Tabulation\AllowForOlympicScoring; use App\Actions\Tabulation\CalculateEntryScore; use App\Actions\Tabulation\CalculateScoreSheetTotal; @@ -32,7 +35,6 @@ use App\Services\DoublerService; use App\Services\DrawService; use App\Services\EntryService; use App\Services\ScoreService; -use App\Services\StudentService; use App\Services\UserService; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\ServiceProvider; @@ -52,6 +54,10 @@ class AppServiceProvider extends ServiceProvider $this->app->singleton(ScoreService::class, ScoreService::class); $this->app->singleton(UserService::class, UserService::class); $this->app->singleton(DoublerService::class, DoublerService::class); + $this->app->singleton(CreateEntry::class, CreateEntry::class); + $this->app->singleton(UpdateEntry::class, UpdateEntry::class); + $this->app->singleton(SetHeadDirector::class, SetHeadDirector::class); + } /** diff --git a/app/helpers.php b/app/helpers.php index 09e1327..ae6332d 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -2,6 +2,7 @@ use App\Actions\Tabulation\EnterScore; use App\Exceptions\ScoreEntryException; +use App\Models\AuditLogEntry; use App\Models\Entry; use App\Models\User; use App\Settings; @@ -35,11 +36,22 @@ function auditionSetting($key) return Settings::get($key); } +function auditionLog(string $message, array $affected) +{ + AuditLogEntry::create([ + 'user' => auth()->user()->email ?? 'no user', + 'ip_address' => request()->ip(), + 'message' => $message, + 'affected' => $affected, + ]); +} + /** * @throws ScoreEntryException */ function enterScore(User $user, Entry $entry, array $scores): \App\Models\ScoreSheet { $scoreEntry = App::make(EnterScore::class); + return $scoreEntry($user, $entry, $scores); } diff --git a/tests/Feature/Actions/SetHeadDirectorTest.php b/tests/Feature/Actions/SetHeadDirectorTest.php new file mode 100644 index 0000000..3f5fd97 --- /dev/null +++ b/tests/Feature/Actions/SetHeadDirectorTest.php @@ -0,0 +1,51 @@ +setter = app(SetHeadDirector::class); +}); + +it('sets a head director flag for a user with a school', function () { + // Arrange + $school = School::factory()->create(); + $user = User::factory()->create(['school_id' => $school->id]); + $this->setter->setHeadDirector($user); + $this->assertDatabaseHas('user_flags', [ + 'user_id' => $user->id, + 'flag_name' => 'head_director', + ]); +}); +it('throws an error if the user has no school', function () { + // Arrange + $user = User::factory()->create(); + // Act & Assert + $this->setter->setHeadDirector($user); +})->throws(AuditionAdminException::class, 'User is not associated with a school'); +it('removes the head director flag from any other users as the school', function () { + // Arrange + $school = School::factory()->create(); + $oldHead = User::factory()->create(['school_id' => $school->id]); + $newHead = User::factory()->create(['school_id' => $school->id]); + $oldHead->addFlag('head_director'); + $this->assertDatabaseHas('user_flags', [ + 'user_id' => $oldHead->id, + 'flag_name' => 'head_director', + ]); + // Act + $this->setter->setHeadDirector($newHead); + $this->assertDatabaseHas('user_flags', [ + 'user_id' => $newHead->id, + 'flag_name' => 'head_director', + ]); + $this->assertDatabaseMissing('user_flags', [ + 'user_id' => $oldHead->id, + 'flag_name' => 'head_director', + ]); +});