Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…into inviting-to-quizzes-frontend
  • Loading branch information
AmonDeShir committed Dec 15, 2024
2 parents 6277280 + 1c83273 commit b7dfe63
Show file tree
Hide file tree
Showing 27 changed files with 381 additions and 36 deletions.
27 changes: 27 additions & 0 deletions app/Actions/CreateAdminAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace App\Actions;

use App\Models\School;
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Hash;

class CreateAdminAction
{
public function execute(array $adminData): User
{
$school = School::query()->where(["is_admin_school" => true])->firstOrFail();

$user = new User($adminData);
$user->password = Hash::make($adminData["password"]);
$user->school()->associate($school);
$user->save();
$user->syncRoles("admin");
event(new Registered($user));

return $user;
}
}
16 changes: 16 additions & 0 deletions app/Actions/ForceChangePasswordAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace App\Actions;

use App\Models\User;

class ForceChangePasswordAction
{
public function execute(User $user): void
{
$user->force_password_change = true;
$user->save();
}
}
23 changes: 7 additions & 16 deletions app/Http/Controllers/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

namespace App\Http\Controllers;

use App\Actions\CreateAdminAction;
use App\Actions\ForceChangePasswordAction;
use App\Helpers\SortHelper;
use App\Http\Requests\AdminRequest;
use App\Http\Requests\Auth\RegisterAdminRequest;
use App\Http\Resources\UserResource;
use App\Models\School;
use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Hash;
Expand All @@ -32,23 +32,14 @@ public function index(SortHelper $sorter): Response
]);
}

public function store(RegisterAdminRequest $request): RedirectResponse
public function store(RegisterAdminRequest $request, ForceChangePasswordAction $forceChangePasswordAction, CreateAdminAction $createAdminAction): RedirectResponse
{
$school = School::query()->where(["is_admin_school" => true])->firstOrFail();
$userExists = User::query()->where("email", $request->email)->exists();

if (!$userExists) {
$user = new User($request->validated());
$user->password = Hash::make($request->password);
$user->school()->associate($school);
$user->save();
$user->syncRoles("admin");
event(new Registered($user));
}
$user = $createAdminAction->execute($request->validated());
$forceChangePasswordAction->execute($user);

return redirect()
->route("admin.admins.index")
->with("success", "Administrator został utworzony pomyślnie.");
->with("status", "Administrator został utworzony pomyślnie.");
}

public function update(AdminRequest $request, User $user): RedirectResponse
Expand All @@ -69,7 +60,7 @@ public function update(AdminRequest $request, User $user): RedirectResponse

return redirect()
->route("admin.admins.index")
->with("success", "Administrator zaktualizowany pomyślnie.");
->with("status", "Administrator zaktualizowany pomyślnie.");
}

public function destroy(User $user): RedirectResponse
Expand Down
4 changes: 4 additions & 0 deletions app/Http/Controllers/ContestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ public function create(Request $request): RedirectResponse|Response
->with(["userQuestions.question.answers", "quiz"])
->get();

$assignedQuizzes = $user->assignedQuizzes->pluck("id");

$quizzes = Quiz::query()
->whereNotNull("locked_at")
->where("is_public", true)
->orWhereIn("id", $assignedQuizzes)
->with("questions.answers")
->get();

Expand Down
3 changes: 3 additions & 0 deletions app/Http/Controllers/ProfileUserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use App\Http\Requests\Auth\ProfileUserPasswordResetRequest;
use App\Http\Resources\UserResource;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Contracts\Hashing\Hasher;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
Expand Down Expand Up @@ -34,6 +35,8 @@ public function update(ProfileUserPasswordResetRequest $request, Hasher $hasher)
$user->password = $hasher->make($validated["password"]);
$user->save();

event(new PasswordReset($user));

return redirect()->back()
->with("status", "Zaktualizowano hasło");
}
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/QuizQuestionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ public function clone(QuizCloneService $service, Question $question, Quiz $quiz)

return redirect()
->back()
->with("success", "Pytanie zostało skopiowane");
->with("status", "Pytanie zostało skopiowane");
}
}
25 changes: 25 additions & 0 deletions app/Http/Middleware/EnsureUserPasswordWasChanged.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureUserPasswordWasChanged
{
public function handle(Request $request, Closure $next): Response|RedirectResponse
{
$user = $request->user();

if ($user->force_password_change) {
return redirect(route("profile"))
->with("status", "Proszę zmienić hasło.");
}

return $next($request);
}
}
2 changes: 1 addition & 1 deletion app/Http/Requests/Auth/RegisterAdminRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public function authorize(): bool
public function rules(): array
{
return [
"email" => ["required", "string", "email:rfc,dns", "max:255"],
"email" => ["required", "string", "unique:users", "email:rfc,dns", "max:255"],
"firstname" => ["required", "string", "max:255"],
"surname" => ["required", "string", "max:255"],
"password" => ["required", "string", "min:8", "max:255"],
Expand Down
5 changes: 5 additions & 0 deletions app/Http/Requests/QuizRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ public function prepareForValidation(): void
if ($this->has("scheduledAt")) {
$this->merge(["scheduled_at" => $this->input("scheduledAt")]);
}

if ($this->has("isPublic")) {
$this->merge(["is_public" => $this->input("isPublic")]);
}
}

/**
Expand All @@ -31,6 +35,7 @@ public function rules(): array
"scheduled_at" => ["date", "after:now"],
"duration" => ["numeric", "min:1", "max:2147483647"],
"description" => ["string", "nullable"],
"is_public" => ["boolean"],
];
}
}
5 changes: 5 additions & 0 deletions app/Http/Requests/UpdateQuizRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public function prepareForValidation(): void
if ($this->has("scheduledAt")) {
$this->merge(["scheduled_at" => $this->input("scheduledAt")]);
}

if ($this->has("isPublic")) {
$this->merge(["is_public" => $this->input("isPublic")]);
}
}

/**
Expand All @@ -30,6 +34,7 @@ public function rules(): array
return [
"title" => ["required", "string", "max:255"],
"scheduled_at" => ["date", "after:now"],
"is_public" => ["boolean"],
"duration" => ["integer", "min:1", "max:2147483647"],
"description" => ["string", "nullable"],
"questions" => ["array"],
Expand Down
1 change: 1 addition & 0 deletions app/Http/Resources/QuizResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public function toArray($request): array
"scheduledAt" => $this->scheduled_at,
"duration" => $this->duration,
"state" => $this->state,
"isPublic" => $this->is_public,
"canBeLocked" => $this->canBeLocked,
"canBeUnlocked" => $this->canBeUnlocked,
"questions" => $this->is_local ? [] : QuestionResource::collection($this->questions),
Expand Down
1 change: 1 addition & 0 deletions app/Http/Resources/UserResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public function toArray(Request $request): array
"email" => $this->email,
"school" => SchoolResource::make($this->school),
"isAnonymized" => $this->is_anonymized,
"forcePasswordChange" => $this->force_password_change,
"isAdmin" => $this->hasRole("admin"),
"isSuperAdmin" => $this->hasRole("super_admin"),
"createdAt" => $this->created_at,
Expand Down
19 changes: 19 additions & 0 deletions app/Listeners/CheckPasswordWasForceChanged.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace App\Listeners;

use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Contracts\Queue\ShouldQueue;

class CheckPasswordWasForceChanged implements ShouldQueue
{
public function handle(PasswordReset $event): void
{
if ($event->user->force_password_change) {
$event->user->force_password_change = false;
$event->user->save();
}
}
}
4 changes: 3 additions & 1 deletion app/Models/Quiz.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@
* @property ?Carbon $ranking_published_at
* @property ?Carbon $locked_at
* @property bool $is_local
* @property bool $is_public
* @property ?int $duration
* @property ?string $description
* @property bool $isLocked
* @property bool $isPublished
* @property bool $canBeLocked
* @property bool $canBeUnlocked
* @property string $state
* @property ?string $description
* @property bool $isRankingPublished
* @property ?Carbon $closeAt
* @property Collection<Question> $questions
Expand All @@ -46,6 +47,7 @@ class Quiz extends Model
"duration",
"ranking_published_at",
"description",
"is_public",
];
protected $guarded = [];

Expand Down
5 changes: 4 additions & 1 deletion app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
Expand All @@ -30,6 +31,7 @@
* @property Carbon $updated_at
* @property School $school
* @property boolean $is_anonymized
* @property boolean $force_password_change
* @property Collection<UserQuiz> $userQuizzes
* @property Collection<Quiz> $assignedQuizzes
*/
Expand All @@ -45,6 +47,7 @@ class User extends Authenticatable implements MustVerifyEmail, CanResetPassword
"email",
"school_id",
"is_anonymized",
"force_password_change",
];
protected $hidden = [
"remember_token",
Expand All @@ -70,7 +73,7 @@ public function userQuizzes(): HasMany
return $this->hasMany(UserQuiz::class);
}

public function assignedQuizzes()
public function assignedQuizzes(): BelongsToMany
{
return $this->belongsToMany(Quiz::class, "quiz_assignments");
}
Expand Down
4 changes: 2 additions & 2 deletions app/Policies/QuizPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function delete(User $user, Quiz $quiz): bool

public function submit(User $user, Quiz $quiz): bool
{
return $quiz->isLocked && !$quiz->is_local;
return $quiz->isLocked && !$quiz->is_local && ($quiz->is_public || $quiz->assignedUsers()->where("user_id", $user->id)->exists());
}

public function lock(User $user, Quiz $quiz): bool
Expand All @@ -42,7 +42,7 @@ public function unlock(User $user, Quiz $quiz): bool

public function assign(User $user, Quiz $quiz): bool
{
return $quiz->isLocked && !$quiz->isPublished && !$quiz->hasUserQuizzesFrom($user);
return $quiz->isLocked && !$quiz->isPublished && $quiz->is_public && !$quiz->hasUserQuizzesFrom($user);
}

public function viewAdminRanking(User $user, Quiz $quiz): Response
Expand Down
8 changes: 8 additions & 0 deletions database/factories/QuizFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public function definition(): array
{
return [
"title" => fake()->name(),
"is_public" => true,
"duration" => fake()->numberBetween(60, 120),
];
}
Expand Down Expand Up @@ -47,6 +48,13 @@ public function withRanking(): static
]);
}

public function private(): static
{
return $this->state(fn(array $attributes): array => [
"is_public" => false,
]);
}

public function local(): static
{
return $this->state(fn(array $attributes): array => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public function up(): void
$table->timestamp("locked_at")->nullable();
$table->string("title");
$table->text("description")->nullable();
$table->boolean("is_public")->default(false);
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class() extends Migration {
public function up(): void
{
Schema::table("users", function (Blueprint $table): void {
$table->boolean("force_password_change")->default(false);
});
}

public function down(): void
{
Schema::table("users", function (Blueprint $table): void {
$table->dropColumn("force_password_change");
});
}
};
1 change: 1 addition & 0 deletions resources/js/Types/Quiz.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface Quiz {
description?: string
isUserAssigned: boolean
isRankingPublished: boolean
isPublic: boolean
questions: Question[]
}

Expand Down
1 change: 1 addition & 0 deletions resources/js/Types/User.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ interface User {
email: string
school: School
isAnonymized: boolean
forcePasswordChange: boolean
isAdmin: boolean
isSuperAdmin: boolean
createdAt: string
Expand Down
Loading

0 comments on commit b7dfe63

Please sign in to comment.