Files
alrahma_sunday_school_api/app/Http/Controllers/old/RegisterController.php
T
2026-03-08 16:33:24 -04:00

360 lines
15 KiB
PHP

<?php
namespace App\Controllers\View;
use CodeIgniter\Controller;
use App\Models\ConfigurationModel;
use App\Models\UserModel;
use App\Models\AuthorizedUserModel;
use App\Models\RoleModel;
use App\Models\ParentModel;
use App\Models\UserRoleModel;
use App\Services\EmailService;
use App\Services\SchoolIdService;
use App\Services\PhoneFormatterService;
class RegisterController extends Controller
{
protected $db;
protected $configModel;
protected $semester;
protected $schoolYear;
protected $userRoleModel;
protected $roleModel;
protected $userModel;
protected $parentModel;
public function __construct()
{
// Load the database service
$this->db = \Config\Database::connect();
// Check if the database connection is established
if (!$this->db->connect()) {
log_message('error', 'Database connection failed.');
throw new \Exception('Database connection failed.');
} else {
log_message('info', 'Database connection successful.');
}
$this->configModel = new ConfigurationModel();
$this->userRoleModel = new UserRoleModel();
$this->roleModel = new RoleModel();
$this->userModel = new UserModel();
$this->parentModel = new ParentModel();
$this->semester = $this->configModel->getConfig('semester');
$this->schoolYear = $this->configModel->getConfig('school_year');
}
public function index()
{
log_message('debug', 'CAPTCHA session value: ' . session()->get('captcha_answer'));
// Only generate CAPTCHA if not already set (first visit or after submission)
if (!session()->has('captcha_answer')) {
$length = rand(4, 8);
$captchaText = $this->generateCaptchaText($length);
session()->set('captcha_answer', $captchaText);
}
$data['captchaQuestion'] = session()->get('captcha_answer');
// ✅ NO need to check policy_accepted here
return view('user/register', $data);
}
private function generateCaptchaText($length)
{
$characters = 'ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjkmnopqrstuvwxyz0123456789';
$captchaText = '';
for ($i = 0; $i < $length; $i++) {
$captchaText .= $characters[rand(0, strlen($characters) - 1)];
}
return $captchaText;
}
public function success()
{
// Start session
$session = session();
// Check if the session has 'user_email'
if (!$session->has('user_email')) {
// If not, redirect to the registration page
return redirect()->to('/register');
}
// Get 'user_email' from session
$email = $session->get('user_email');
// Pass the 'email' variable to the success view
return view('success', ['email' => $email]);
}
public function register()
{
helper(['url', 'form']);
log_message('debug', 'CAPTCHA session value: ' . session()->get('captcha_answer'));
/* ───────────── 1. Load services ───────────── */
$schoolIdService = new SchoolIdService();
/* ───────────── 2. Sanitize & raw data ───────────── */
$post = $this->formatUserInput($this->request); // safe / formatted
$rawPost = $this->request->getPost(); // raw (for flags etc.)
$captcha = $rawPost['captcha'] ?? '';
/* ───────────── 3. Validation rules ───────────── */
$rules = [
'firstname' => 'required|regex_match[/^[a-zA-Z\s-]+$/]|min_length[2]|max_length[30]',
'lastname' => 'required|regex_match[/^[a-zA-Z\s-]+$/]|min_length[2]|max_length[30]',
'gender' => 'required|in_list[Male,Female]',
'email' => 'required|valid_email|max_length[50]',
'confirm_email' => 'required|matches[email]',
'cellphone' => 'required|regex_match[/^[\d\s\-\(\)\.]+$/]|min_length[10]|max_length[20]',
'address_street' => 'permit_empty|regex_match[/^[a-zA-Z0-9\s\-]+$/]|max_length[150]',
'apt' => 'permit_empty|regex_match[/^[a-zA-Z0-9\s-]+$/]|max_length[10]',
'city' => 'required|alpha_space|min_length[2]|max_length[30]',
'state' => 'required|in_list[CT,ME,MA,NH,NY,RI,VT]',
'zip' => 'required|regex_match[/^\d{5}$/]',
'accept_school_policy' => 'required',
'captcha' => 'required|alpha_numeric|min_length[4]|max_length[10]',
];
// 1. Check if "I am a parent" checkbox is ticked
$isParent = isset($rawPost['is_parent']) && $rawPost['is_parent'] == '1';
// 2. If parent, handle second parent logic
if ($isParent) {
$noSecondParent = isset($rawPost['no_second_parent_info']) && $rawPost['no_second_parent_info'] == '1';
$secondParentProvided = !empty($rawPost['second_firstname']) ||
!empty($rawPost['second_lastname']) ||
!empty($rawPost['second_gender']) ||
!empty($rawPost['second_email']) ||
!empty($rawPost['second_cellphone']);
// 3. Error: must provide second parent or check "no second parent"
if (!$noSecondParent && !$secondParentProvided) {
return redirect()->back()->withInput()->with('errors', [
'second_parent_info' => 'As a parent, please provide second parent information or check "No second parent info".'
]);
}
// 4. Add second parent field rules only if "no second parent" is NOT checked
if (!$noSecondParent) {
$rules += [
'second_firstname' => 'required|regex_match[/^[a-zA-Z\s\-]+$/]|min_length[2]|max_length[30]',
'second_lastname' => 'required|regex_match[/^[a-zA-Z\s\-]+$/]|min_length[2]|max_length[30]',
'second_gender' => 'required|in_list[Male,Female]',
'second_email' => 'required|valid_email|max_length[50]',
'second_cellphone' => 'required|regex_match[/^[\d\s\-\(\)\.]+$/]|min_length[10]|max_length[20]',
];
}
}
log_message('debug', '🧪 Starting validation check...');
if (!$this->validate($rules)) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
/* ───────────── 4. CAPTCHA check ───────────── */
if ($captcha !== session()->get('captcha_answer')) {
return redirect()->back()
->withInput()
->with('errors', ['captcha' => '']) // keeps input marked as invalid
->with('top_error', 'Incorrect CAPTCHA answer. Please try again.');
}
/* ───────────── 5. Unique e-mail ───────────── */
// Step 1: Check if a user with this email exists
$existingUser = $this->userModel->where('email', $post['email'])->first();
if ($existingUser) {
// Step 2: Check if the user has a token (i.e., not verified yet)
if (!empty($existingUser['token']) && $existingUser['is_verified'] == 0) {
// User exists and is unverified
return redirect()->back()->withInput()->with('error',
'This email address is already registered and is pending activation. Please check your email to activate your account.');
} else {
// User exists and is already active or has no token
return redirect()->back()->withInput()->with('error',
'The email address you entered is already in use. Please try a different one.');
}
}
/* ───────────── 6. Determine role ───────────── */
$roleName = !empty($rawPost['is_parent']) ? 'parent' : 'guest';
$role = $this->roleModel->where('name', $roleName)->first();
if (!$role) {
return redirect()->back()->withInput()->with('errors', ['role' => 'Role not found']);
}
/* ───────────── 7. Build & insert user ───────────── */
$token = bin2hex(random_bytes(48));
$userData = [
'firstname' => $post['firstname'],
'lastname' => $post['lastname'],
'gender' => $post['gender'],
'email' => $post['email'],
'cellphone' => $post['cellphone'],
'address_street' => $post['address_street'] ?? null,
'apt' => $post['apt'] ?? null,
'city' => $post['city'],
'state' => $post['state'],
'zip' => $post['zip'],
'token' => $token,
'is_verified'=> 0,
'accept_school_policy' => (int) $post['accept_school_policy'],
'status' => 'Inactive',
'school_id' => $schoolIdService->generateUserSchoolId(),
'semester' => $this->semester,
'school_year'=> $this->schoolYear,
];
$this->db->transStart();
if (!$this->userModel->insert($userData)) {
$this->db->transRollback();
return redirect()->back()->withInput()->with('errors', $this->userModel->errors());
}
$firstParentId = $this->userModel->getInsertID();
$this->userRoleModel->insert([
'user_id' => $firstParentId,
'role_id' => $role['id'],
'created_at' => utc_now(),
]);
/* ───────────── 8. Optional second-parent insert ───────────── */
if ($isParent) {
if (empty($rawPost['no_second_parent_info'])) {
$this->parentModel->insert([
'secondparent_firstname' => $post['second_firstname'],
'secondparent_lastname' => $post['second_lastname'],
'secondparent_gender' => $post['second_gender'],
'secondparent_email' => $post['second_email'],
'secondparent_phone' => $post['second_cellphone'],
'firstparent_id' => $firstParentId,
'semester' => $this->semester,
'school_year' => $this->schoolYear,
]);
}
}
$this->db->transComplete();
if (!$this->db->transStatus()) {
return redirect()->back()->withInput()->with('error', 'Registration failed, please try again.');
}
/* ───────────── 9. Confirmation e-mail ───────────── */
$recipientName = $post['firstname'] . ' ' . $post['lastname'];
$activationLink = site_url('/user/confirm/' . $token);
$orgName = 'Al Rahma Sunday School';
$contactInfo = 'alrahma.isgl@gmail.com';
$logoUrl = 'https://alrahmaisgl.org/assets/images/alrahma_logo.png';
$emailData = [
'recipientName' => $recipientName,
'activationLink' => $activationLink,
'orgName' => $orgName,
'contactInfo' => $contactInfo,
'logoUrl' => $logoUrl,
'subject' => 'Email Confirmation'
];
if ($isParent) {
$html = view('emails/welcome_parent', $emailData);
} else {
$html = view('emails/welcome_staff', $emailData);
}
(new EmailService())->send($post['email'], 'Email Confirmation', $html, 'general');
/* ───────────── 10. Redirect to success ───────────── */
session()->set('user_email', $post['email']);
session()->remove('captcha_answer'); // Clear CAPTCHA after success
return redirect()->to('/register/success');
}
private function formatName(string $name): string
{
$name = trim($name);
return ucwords(strtolower($name), " -");
}
private function formatEmail(?string $email): string
{
$email = strtolower(trim($email));
/*
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Please enter a valid email address.');
}*/
return $email;
}
private function formatAddress(string $address): string
{
return ucwords(strtolower(trim($address)));
}
private function formatUserInput($request): array
{
$formatter = new PhoneFormatterService();
$post = (array) $request->getPost();
// tiny helper: safe getter + trim + cast to string
$g = static function (string $key) use ($post): string {
return trim((string) ($post[$key] ?? ''));
};
$data = [
'firstname' => $this->formatName($g('firstname')),
'lastname' => $this->formatName($g('lastname')),
'email' => $this->formatEmail($g('email')),
'gender' => $g('gender'),
'city' => $this->formatName($g('city')),
'cellphone' => $formatter->formatPhoneNumber($g('cellphone')), // ✅ safe
'address_street' => $this->formatAddress($g('address_street')),
'apt' => strtoupper($g('apt')),
'state' => strtoupper($g('state')),
'zip' => $g('zip'),
'accept_school_policy' => (int) ($post['accept_school_policy'] ?? 0),
];
// if user says "no second parent info", skip entirely
$noSecondInfo = !empty($post['no_second_parent_info']);
// collect (safely) and see if anything was provided
$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 = $formatter->formatPhoneNumber($g('second_cellphone')); // ✅ safe
$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;
}
}