parent
5862d05f35
commit
df9b64a4e2
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\Actions\Schools;
|
||||
|
||||
use App\Exceptions\AuditionAdminException;
|
||||
use App\Models\School;
|
||||
use App\Models\User;
|
||||
|
||||
use function auditionLog;
|
||||
use function is_null;
|
||||
|
||||
class SetHeadDirector
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function __invoke(User $user, School $school): void
|
||||
{
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class AuditionAdminException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Actions\Schools\SetHeadDirector;
|
||||
use App\Exceptions\AuditionAdminException;
|
||||
use App\Models\AuditLogEntry;
|
||||
use App\Models\School;
|
||||
use App\Models\SchoolEmailDomain;
|
||||
|
|
@ -15,7 +17,7 @@ use function request;
|
|||
|
||||
class SchoolController extends Controller
|
||||
{
|
||||
public function store(Request $request): RedirectResponse
|
||||
public function store(Request $request, SetHeadDirector $headSetter): RedirectResponse
|
||||
{
|
||||
if ($request->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');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
use App\Actions\Schools\SetHeadDirector;
|
||||
use App\Exceptions\AuditionAdminException;
|
||||
use App\Models\School;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
$this->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',
|
||||
]);
|
||||
});
|
||||
Loading…
Reference in New Issue