Auditionadmin 64 #71
|
|
@ -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;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Actions\Schools\SetHeadDirector;
|
||||||
|
use App\Exceptions\AuditionAdminException;
|
||||||
use App\Models\AuditLogEntry;
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\School;
|
use App\Models\School;
|
||||||
use App\Models\SchoolEmailDomain;
|
use App\Models\SchoolEmailDomain;
|
||||||
|
|
@ -15,7 +17,7 @@ use function request;
|
||||||
|
|
||||||
class SchoolController extends Controller
|
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)) {
|
if ($request->user()->cannot('create', School::class)) {
|
||||||
abort(403);
|
abort(403);
|
||||||
|
|
@ -70,14 +72,12 @@ class SchoolController extends Controller
|
||||||
'schools' => [$school->id],
|
'schools' => [$school->id],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
auth()->user()->refresh();
|
||||||
auth()->user()->addFlag('head_director'); // If user is creating a school, they are initially the head
|
try {
|
||||||
AuditLogEntry::create([
|
$headSetter->setHeadDirector(auth()->user());
|
||||||
'user' => auth()->user()->email,
|
} catch (AuditionAdminException $e) {
|
||||||
'ip_address' => request()->ip(),
|
redirect(route('schools.show', $school))->with('error', 'Could not set as head director');
|
||||||
'message' => 'Marked '.auth()->user()->full_name().' as head director for '.$school->name,
|
}
|
||||||
'affected' => ['schools' => [$school->id], 'users' => [auth()->user()->id]],
|
|
||||||
]);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,13 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||||
$this->load('flags');
|
$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)
|
public function scoresForEntry($entry)
|
||||||
{
|
{
|
||||||
return $this->scoreSheets->where('entry_id', '=', $entry)->first()?->subscores;
|
return $this->scoreSheets->where('entry_id', '=', $entry)->first()?->subscores;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
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\AllowForOlympicScoring;
|
||||||
use App\Actions\Tabulation\CalculateEntryScore;
|
use App\Actions\Tabulation\CalculateEntryScore;
|
||||||
use App\Actions\Tabulation\CalculateScoreSheetTotal;
|
use App\Actions\Tabulation\CalculateScoreSheetTotal;
|
||||||
|
|
@ -32,7 +35,6 @@ use App\Services\DoublerService;
|
||||||
use App\Services\DrawService;
|
use App\Services\DrawService;
|
||||||
use App\Services\EntryService;
|
use App\Services\EntryService;
|
||||||
use App\Services\ScoreService;
|
use App\Services\ScoreService;
|
||||||
use App\Services\StudentService;
|
|
||||||
use App\Services\UserService;
|
use App\Services\UserService;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
@ -52,6 +54,10 @@ class AppServiceProvider extends ServiceProvider
|
||||||
$this->app->singleton(ScoreService::class, ScoreService::class);
|
$this->app->singleton(ScoreService::class, ScoreService::class);
|
||||||
$this->app->singleton(UserService::class, UserService::class);
|
$this->app->singleton(UserService::class, UserService::class);
|
||||||
$this->app->singleton(DoublerService::class, DoublerService::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\Actions\Tabulation\EnterScore;
|
||||||
use App\Exceptions\ScoreEntryException;
|
use App\Exceptions\ScoreEntryException;
|
||||||
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\Entry;
|
use App\Models\Entry;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Settings;
|
use App\Settings;
|
||||||
|
|
@ -35,11 +36,22 @@ function auditionSetting($key)
|
||||||
return Settings::get($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
|
* @throws ScoreEntryException
|
||||||
*/
|
*/
|
||||||
function enterScore(User $user, Entry $entry, array $scores): \App\Models\ScoreSheet
|
function enterScore(User $user, Entry $entry, array $scores): \App\Models\ScoreSheet
|
||||||
{
|
{
|
||||||
$scoreEntry = App::make(EnterScore::class);
|
$scoreEntry = App::make(EnterScore::class);
|
||||||
|
|
||||||
return $scoreEntry($user, $entry, $scores);
|
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