190) { return 'Please enter a shorter email address.'; } $parts = explode('@', $email); if (count($parts) !== 2) { return 'Please enter a valid email address.'; } [$local, $domain] = $parts; if ($local === '' || $domain === '') { return 'Please enter a valid email address.'; } if (is_disposable_email_domain($domain)) { return 'Please use your real email address. Temporary or disposable inboxes are not allowed.'; } if (!has_valid_email_dns($domain)) { return 'Please use an email with a real mail domain.'; } return null; } function clean_text_input($value, int $max_length = 255): string { $value = trim((string) $value); $value = strip_tags($value); $value = preg_replace('/[-]/u', '', $value) ?? ''; if (function_exists('mb_substr')) { return mb_substr($value, 0, $max_length); } return substr($value, 0, $max_length); } function normalize_timezone_input($value): ?string { $timezone = clean_text_input($value, 100); if ($timezone === '') { return null; } return in_array($timezone, timezone_identifiers_list(), true) ? $timezone : null; } // --- Helper function to fetch webinar details --- function get_webinar_details($id) { if (empty($id)) return null; try { $stmt = db()->prepare("SELECT id, title, description, scheduled_at, presenter FROM webinars WHERE id = ?"); $stmt->execute([$id]); return $stmt->fetch(PDO::FETCH_ASSOC); } catch (PDOException $e) { error_log("Database error fetching webinar ID $id: " . $e->getMessage()); return null; } } // --- Only allow POST requests --- if ($_SERVER["REQUEST_METHOD"] !== "POST") { echo json_encode(['success' => false, 'error' => 'Invalid request method.']); exit; } $webinar_id = filter_input(INPUT_POST, 'webinar_id', FILTER_VALIDATE_INT) ?: 1; $webinar = get_webinar_details($webinar_id); if (!$webinar) { http_response_code(404); echo json_encode(['success' => false, 'error' => 'Webinar not found.']); exit; } // --- DATA CAPTURE --- $email_input = (string) filter_input(INPUT_POST, 'email', FILTER_UNSAFE_RAW); $email = normalize_email_address($email_input); $email_error = validate_registration_email($email); $first_name = clean_text_input(filter_input(INPUT_POST, 'first_name', FILTER_UNSAFE_RAW), 120); $last_name = clean_text_input(filter_input(INPUT_POST, 'last_name', FILTER_UNSAFE_RAW), 120); $company = clean_text_input(filter_input(INPUT_POST, 'company', FILTER_UNSAFE_RAW), 180); $how_did_you_hear = clean_text_input(filter_input(INPUT_POST, 'how_did_you_hear', FILTER_UNSAFE_RAW), 120); $timezone = normalize_timezone_input(filter_input(INPUT_POST, 'timezone', FILTER_UNSAFE_RAW)); $allowed_sources = [ 'Social Media', 'LinkedIn', 'Reddit', 'Threads', 'Advertisement', 'ChatGPT', 'Flatlogic Community Discord', 'Other', ]; // --- VALIDATION --- if (!$first_name || !$last_name || $email === '' || $how_did_you_hear === '') { echo json_encode(['success' => false, 'error' => 'Please fill out all required fields.']); exit; } if ($email_error !== null) { echo json_encode(['success' => false, 'error' => $email_error]); exit; } if (!in_array($how_did_you_hear, $allowed_sources, true)) { echo json_encode(['success' => false, 'error' => 'Please choose how you heard about this webinar from the list.']); exit; } try { // --- CHECK IF ALREADY REGISTERED OR SOFT-DELETED -- - $stmt = db()->prepare("SELECT id, deleted_at FROM attendees WHERE webinar_id = ? AND email = ?"); $stmt->execute([$webinar_id, $email]); $existing_user = $stmt->fetch(PDO::FETCH_ASSOC); $send_email = false; if ($existing_user) { if ($existing_user['deleted_at'] !== null) { // --- USER IS SOFT-DELETED, SO REACTIVATE AND UPDATE --- $sql = "UPDATE attendees SET first_name = ?, last_name = ?, company = ?, how_did_you_hear = ?, timezone = ?, deleted_at = NULL, created_at = NOW(), consented = 1 WHERE id = ?"; $stmt = db()->prepare($sql); $stmt->execute([$first_name, $last_name, $company, $how_did_you_hear, $timezone, $existing_user['id']]); $send_email = true; } else { // --- USER IS ACTIVE, SO REJECT --- echo json_encode(['success' => false, 'error' => 'You are already registered for this webinar.']); exit; } } else { // --- REGISTER NEW USER --- $password_hash = password_hash($email . time(), PASSWORD_DEFAULT); $sql = "INSERT INTO attendees (webinar_id, first_name, last_name, email, company, how_did_you_hear, password, timezone, consented) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1)"; $stmt = db()->prepare($sql); $stmt->execute([$webinar_id, $first_name, $last_name, $email, $company, $how_did_you_hear, $password_hash, $timezone]); $send_email = true; } $schedule = webinar_schedule_data($webinar); $event_title = $schedule['title']; $event_description = $schedule['calendar_description']; $start_time_utc = webinar_utc_stamp($schedule['start']); $end_time_utc = webinar_utc_stamp($schedule['end']); $google_link = 'https://www.google.com/calendar/render?action=TEMPLATE' . '&text=' . urlencode($event_title) . '&dates=' . $start_time_utc . '/' . $end_time_utc . '&details=' . urlencode($event_description) . '&location=' . urlencode('Online') . '&ctz=UTC'; if ($send_email) { // --- SEND CONFIRMATION EMAIL --- $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST']; $logo_url = $protocol . '://' . $host . '/assets/pasted-20251030-095744-1b7c02ab.png'; $subject = "Confirmation: You're Registered for {$event_title}"; $safe_first_name = htmlspecialchars($first_name, ENT_QUOTES, 'UTF-8'); $safe_title = htmlspecialchars($event_title, ENT_QUOTES, 'UTF-8'); $safe_date_long = htmlspecialchars($schedule['date_long'], ENT_QUOTES, 'UTF-8'); $safe_berlin_label = htmlspecialchars($schedule['berlin_label'], ENT_QUOTES, 'UTF-8'); $safe_new_york_label = htmlspecialchars($schedule['new_york_label'], ENT_QUOTES, 'UTF-8'); $safe_los_angeles_label = htmlspecialchars($schedule['los_angeles_label'], ENT_QUOTES, 'UTF-8'); $body_html = << Webinar Registration Confirmation
AppWizzy Logo

You're Registered!

Hello {$safe_first_name},

Thank you for registering for the {$safe_title} webinar.

We're excited to have you join us for this professional vibe-coding session.

Webinar Details:

{$safe_date_long} | {$safe_berlin_label} | {$safe_new_york_label} | {$safe_los_angeles_label}

You'll learn the fastest way to go from an idea to a working app you own, running on your server, with your database, using real frameworks.

Your personal webinar access link will be emailed to you 1 day before the event.

Add to Calendar

© 2025 AppWizzy. All rights reserved.

You can visit our website for more information.

HTML; MailService::sendMail($email, $subject, $body_html); } $ics_content = implode("\r\n", [ 'BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//Flatlogic//' . $event_title . '//EN', 'BEGIN:VEVENT', 'URL:' . 'http://' . $_SERVER['HTTP_HOST'], 'DTSTART:' . $start_time_utc, 'DTEND:' . $end_time_utc, 'SUMMARY:' . $event_title, 'DESCRIPTION:' . str_replace(PHP_EOL, '\\n', $event_description), 'LOCATION:Online', 'END:VEVENT', 'END:VCALENDAR' ]); $outlook_link = 'data:text/calendar;charset=utf-8,' . rawurlencode($ics_content); echo json_encode([ 'success' => true, 'webinar_title' => $event_title . '
Professional Vibe-Coding Webinar', 'google_link' => $google_link, 'outlook_link' => $outlook_link, 'schedule_line' => $schedule['timezone_line'] ]); } catch (Exception $e) { http_response_code(500); echo json_encode(['success' => false, 'error' => 'An unexpected server error occurred. Please try again.']); }