add projet

This commit is contained in:
root
2026-03-05 12:29:37 -05:00
parent 8d1eef8ba8
commit 23b7db1107
9109 changed files with 1106501 additions and 73 deletions
+357
View File
@@ -0,0 +1,357 @@
<?php
namespace App\Http\Controllers\Api;
use App\Models\Configuration;
use App\Models\ParentModel;
use App\Models\Role;
use App\Models\User;
use App\Models\UserRole;
use App\Services\EmailService;
use App\Services\PhoneFormatterService;
use App\Services\SchoolIdService;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Symfony\Component\HttpFoundation\Response;
class RegisterController extends BaseApiController
{
protected Configuration $config;
protected User $user;
protected UserRole $userRole;
protected Role $role;
protected Parent $parent;
protected SchoolIdService $schoolIdService;
protected EmailService $emailService;
protected PhoneFormatterService $phoneFormatter;
protected string $semester;
protected string $schoolYear;
public function __construct()
{
parent::__construct();
$this->config = model(Configuration::class);
$this->user = model(User::class);
$this->userRole = model(UserRole::class);
$this->role = model(Role::class);
$this->parent = model(ParentModel::class);
$this->schoolIdService = app(SchoolIdService::class);
$this->emailService = app(EmailService::class);
$this->phoneFormatter = app(PhoneFormatterService::class);
$this->semester = (string) ($this->config->getConfig('semester') ?? '');
$this->schoolYear = (string) ($this->config->getConfig('school_year') ?? '');
}
public function index(): JsonResponse
{
$session = session();
log_message('debug', 'CAPTCHA session value: ' . ($session->get('captcha_answer') ?? ''));
if (!$session->has('captcha_answer')) {
$length = random_int(4, 8);
$session->put('captcha_answer', $this->generateCaptchaText($length));
}
return $this->respondSuccess([
'captcha_question' => $session->get('captcha_answer'),
], 'Captcha generated');
}
public function registrationSuccess(): JsonResponse
{
$session = session();
if (!$session->has('user_email')) {
return $this->respondError('No registration session found.', Response::HTTP_BAD_REQUEST);
}
return $this->respondSuccess([
'email' => $session->get('user_email'),
], 'Registration success data');
}
public function register(): JsonResponse
{
$payload = $this->payloadData();
log_message('debug', 'CAPTCHA session value: ' . (session()->get('captcha_answer') ?? ''));
$formatted = $this->formatUserInput($payload);
$validationPayload = array_merge($payload, $formatted);
$captcha = (string) ($payload['captcha'] ?? '');
$isParent = !empty($payload['is_parent']) && (string) $payload['is_parent'] === '1';
$noSecondParentInfo = !empty($payload['no_second_parent_info']) && (string) $payload['no_second_parent_info'] === '1';
$rules = [
'firstname' => ['required', 'regex:/^[a-zA-Z\s-]+$/', 'min:2', 'max:30'],
'lastname' => ['required', 'regex:/^[a-zA-Z\s-]+$/', 'min:2', 'max:30'],
'gender' => ['required', Rule::in(['Male', 'Female'])],
'email' => ['required', 'email', 'max:50'],
'confirm_email' => ['required', 'same:email'],
'cellphone' => ['required', 'regex:/^[\d\s\-\(\)\.]+$/', 'min:10', 'max:20'],
'address_street' => ['nullable', 'regex:/^[a-zA-Z0-9\s\-]+$/', 'max:150'],
'apt' => ['nullable', 'regex:/^[a-zA-Z0-9\s-]+$/', 'max:10'],
'city' => ['required', 'regex:/^[a-zA-Z\s]+$/', 'min:2', 'max:30'],
'state' => ['required', Rule::in(['CT', 'ME', 'MA', 'NH', 'NY', 'RI', 'VT'])],
'zip' => ['required', 'regex:/^\d{5}$/'],
'accept_school_policy' => ['required', 'accepted'],
'captcha' => ['required', 'alpha_num', 'min:4', 'max:10'],
];
if ($isParent && !$noSecondParentInfo) {
$rules = array_merge($rules, [
'second_firstname' => ['required', 'regex:/^[a-zA-Z\s-]+$/', 'min:2', 'max:30'],
'second_lastname' => ['required', 'regex:/^[a-zA-Z\s-]+$/', 'min:2', 'max:30'],
'second_gender' => ['required', Rule::in(['Male', 'Female'])],
'second_email' => ['required', 'email', 'max:50'],
'second_cellphone' => ['required', 'regex:/^[\d\s\-\(\)\.]+$/', 'min:10', 'max:20'],
]);
}
$errors = $this->validateRequest($validationPayload, $rules);
if (!empty($errors)) {
return $this->respondValidationError($errors);
}
if ($isParent && !$noSecondParentInfo && !$this->hasSecondParentData($formatted)) {
return $this->respondValidationError([
'second_parent_info' => ['As a parent, please provide second parent information or check "No second parent info".'],
]);
}
if ($captcha !== (string) session()->get('captcha_answer')) {
return $this->respondValidationError([
'captcha' => ['Incorrect CAPTCHA answer. Please try again.'],
], 'Incorrect CAPTCHA answer.');
}
$email = $formatted['email'] ?? '';
$existingUser = $this->user->newQuery()->where('email', $email)->first();
if ($existingUser) {
$isVerified = (int) ($existingUser->is_verified ?? 0);
$token = $existingUser->token ?? null;
if (!empty($token) && $isVerified === 0) {
return $this->respondError(
'This email address is already registered and is pending activation. Please check your email to activate your account.',
Response::HTTP_CONFLICT
);
}
return $this->respondError(
'The email address you entered is already in use. Please try a different one.',
Response::HTTP_CONFLICT
);
}
$roleName = $isParent ? 'parent' : 'guest';
$role = $this->role->newQuery()->where('name', $roleName)->first();
if (!$role) {
return $this->respondError('Role not found', Response::HTTP_BAD_REQUEST);
}
$token = bin2hex(random_bytes(48));
$userData = [
'firstname' => $formatted['firstname'] ?? null,
'lastname' => $formatted['lastname'] ?? null,
'gender' => $formatted['gender'] ?? null,
'email' => $email,
'cellphone' => $formatted['cellphone'] ?? null,
'address_street' => $formatted['address_street'] ?? null,
'apt' => $formatted['apt'] ?? null,
'city' => $formatted['city'] ?? null,
'state' => $formatted['state'] ?? null,
'zip' => $formatted['zip'] ?? null,
'token' => $token,
'is_verified' => 0,
'accept_school_policy' => (int) ($formatted['accept_school_policy'] ?? 0),
'status' => 'Inactive',
'school_id' => $this->schoolIdService->generate(),
'semester' => $this->semester,
'school_year' => $this->schoolYear,
];
DB::beginTransaction();
try {
$user = $this->user->newQuery()->create($userData);
$firstParentId = $user->id ?? null;
if (!$firstParentId) {
DB::rollBack();
return $this->respondError('Registration failed, please try again.', Response::HTTP_INTERNAL_SERVER_ERROR);
}
$this->userRole->newQuery()->create([
'user_id' => $firstParentId,
'role_id' => $role->id,
'semester' => $this->semester,
'school_year'=> $this->schoolYear,
'created_at' => utc_now(),
]);
if ($isParent && !$noSecondParentInfo && $this->hasSecondParentData($formatted)) {
$this->parent->newQuery()->create([
'secondparent_firstname' => $formatted['second_firstname'] ?? null,
'secondparent_lastname' => $formatted['second_lastname'] ?? null,
'secondparent_gender' => $formatted['second_gender'] ?? null,
'secondparent_email' => $formatted['second_email'] ?? null,
'secondparent_phone' => $formatted['second_cellphone'] ?? null,
'firstparent_id' => $firstParentId,
'semester' => $this->semester,
'school_year' => $this->schoolYear,
]);
}
DB::commit();
} catch (\Throwable $e) {
DB::rollBack();
log_message('error', 'Registration failed: ' . $e->getMessage());
return $this->respondError('Registration failed, please try again.', Response::HTTP_INTERNAL_SERVER_ERROR);
}
$recipientName = trim(($formatted['firstname'] ?? '') . ' ' . ($formatted['lastname'] ?? ''));
$emailData = [
'recipientName' => $recipientName,
'activationLink'=> url('/user/confirm/' . $token),
'orgName' => 'Al Rahma Sunday School',
'contactInfo' => 'alrahma.isgl@gmail.com',
'logoUrl' => 'https://alrahmaisgl.org/assets/images/alrahma_logo.png',
];
$html = $this->buildEmailBody($emailData, $isParent);
$this->emailService->send($email, 'Email Confirmation', $html, 'general');
session()->put('user_email', $email);
session()->forget('captcha_answer');
return $this->respondSuccess([
'email' => $email,
], 'Registration successful');
}
private function generateCaptchaText(int $length): string
{
$characters = 'ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjkmnopqrstuvwxyz0123456789';
$captchaText = '';
for ($i = 0; $i < $length; $i++) {
$captchaText .= $characters[random_int(0, strlen($characters) - 1)];
}
return $captchaText;
}
private function formatUserInput(array $post): array
{
$g = static function (string $key) use ($post): string {
return trim((string) ($post[$key] ?? ''));
};
$data = [
'firstname' => $this->formatName($g('firstname')),
'lastname' => $this->formatName($g('lastname')),
'gender' => $g('gender'),
'email' => $this->formatEmail($g('email')),
'confirm_email' => $this->formatEmail($g('confirm_email')),
'cellphone' => $this->phoneFormatter->formatPhoneNumber($g('cellphone')),
'address_street' => $this->nullIfEmpty($this->formatAddress($g('address_street'))),
'apt' => $this->nullIfEmpty(strtoupper($g('apt'))),
'city' => $this->formatName($g('city')),
'state' => strtoupper($g('state')),
'zip' => $g('zip'),
'accept_school_policy' => (int) ($post['accept_school_policy'] ?? 0),
];
$noSecondInfo = !empty($post['no_second_parent_info']);
$second_firstname = $this->formatName($g('second_firstname'));
$second_lastname = $this->formatName($g('second_lastname'));
$second_gender = $g('second_gender');
$second_email = $this->formatEmail($g('second_email'));
$second_cellphone = $this->phoneFormatter->formatPhoneNumber($g('second_cellphone'));
$secondProvided = !$noSecondInfo && (
$second_firstname !== '' ||
$second_lastname !== '' ||
$second_gender !== '' ||
$second_email !== '' ||
preg_replace('/\D/', '', $second_cellphone) !== ''
);
if ($secondProvided) {
$data['second_firstname'] = $second_firstname;
$data['second_lastname'] = $second_lastname;
$data['second_gender'] = $second_gender;
$data['second_email'] = $second_email;
$data['second_cellphone'] = $second_cellphone;
}
return $data;
}
private function formatName(string $name): string
{
$name = trim($name);
return ucwords(strtolower($name), " -");
}
private function formatEmail(?string $email): string
{
return strtolower(trim((string) $email));
}
private function formatAddress(?string $address): string
{
$address = trim((string) $address);
if ($address === '') {
return '';
}
return ucwords(strtolower($address));
}
private function nullIfEmpty(?string $value): ?string
{
if ($value === null) {
return null;
}
$value = trim((string) $value);
return $value === '' ? null : $value;
}
private function hasSecondParentData(array $data): bool
{
return !empty($data['second_firstname'])
|| !empty($data['second_lastname'])
|| !empty($data['second_gender'])
|| !empty($data['second_email'])
|| (!empty($data['second_cellphone']) && preg_replace('/\D/', '', $data['second_cellphone']) !== '');
}
private function buildEmailBody(array $data, bool $isParent): string
{
$audience = $isParent ? 'Parent' : 'Staff Member';
return <<<HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Email Confirmation</title>
</head>
<body>
<p>Dear {$data['recipientName']},</p>
<p>Thank you for registering with {$data['orgName']} as a {$audience}. Please confirm your email address by clicking the link below:</p>
<p><a href="{$data['activationLink']}" target="_blank">Activate My Account</a></p>
<p>If the link above does not work, copy and paste this URL into your browser:</p>
<p>{$data['activationLink']}</p>
<p>For questions, reach us at {$data['contactInfo']}.</p>
<p>Thank you,<br>{$data['orgName']}</p>
</body>
</html>
HTML;
}
}