354 lines
14 KiB
PHP
354 lines
14 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) {
|
|
return redirect()->back()->withInput()->with('error',
|
|
'This email address is already registered. Please check your email or log in.');
|
|
}
|
|
|
|
/* ───────────── 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));
|
|
$tokenHash = hash('sha256', $token);
|
|
$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' => $tokenHash,
|
|
'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;
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|