39074-vm/register.php
2026-03-25 16:26:16 +00:00

300 lines
12 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
session_start();
require_once 'db/config.php';
require_once 'mail/MailService.php';
require_once 'includes/webinar_schedule.php';
header('Content-Type: application/json');
function normalize_email_address($email) {
return strtolower(trim((string) $email));
}
function has_valid_email_dns($domain) {
if ($domain === '') {
return false;
}
return checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A') || checkdnsrr($domain, 'AAAA');
}
function is_disposable_email_domain($domain) {
static $blocked_domains = [
'10minutemail.com',
'dispostable.com',
'emailondeck.com',
'fakeinbox.com',
'guerrillamail.com',
'maildrop.cc',
'mailinator.com',
'mailnesia.com',
'mintemail.com',
'sharklasers.com',
'tempmail.com',
'temp-mail.org',
'trashmail.com',
'yopmail.com',
'example.com',
'example.net',
'example.org',
];
return in_array($domain, $blocked_domains, true);
}
function validate_registration_email($email) {
$email = normalize_email_address($email);
if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
return 'Please enter a valid email address.';
}
if (strlen($email) > 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 = <<<HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webinar Registration Confirmation</title>
</head>
<body style="font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f4f4f4; color: #333; margin: 0; padding: 0;">
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #f4f4f4;">
<tr>
<td align="center">
<table width="600" border="0" cellspacing="0" cellpadding="0" style="background-color: #ffffff; margin: 20px auto; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);">
<tr>
<td align="center" style="padding: 40px 20px; background: linear-gradient(135deg, #1a237e 0%, #673ab7 100%); border-radius: 10px 10px 0 0;">
<img src="{$logo_url}" alt="AppWizzy Logo" style="height: 60px; margin-bottom: 20px;">
<h1 style="color: #ffffff; font-size: 28px; margin: 0; line-height: 1.2;">You're Registered!</h1>
</td>
</tr>
<tr>
<td style="padding: 40px 30px;">
<h2 style="font-size: 22px; color: #1a237e;">Hello {$safe_first_name},</h2>
<p style="font-size: 16px; line-height: 1.6;">Thank you for registering for the <strong>{$safe_title}</strong> webinar.</p>
<p style="font-size: 16px; line-height: 1.6;">We're excited to have you join us for this professional vibe-coding session.</p>
<div style="background-color: #f9f9f9; border-left: 4px solid #673ab7; padding: 15px 20px; margin: 20px 0;">
<p style="margin: 0; font-size: 16px;"><strong>Webinar Details:</strong></p>
<p style="margin: 10px 0 0; font-size: 16px;">{$safe_date_long} | <strong>{$safe_berlin_label}</strong> | {$safe_new_york_label} | {$safe_los_angeles_label}</p>
</div>
<p style="font-size: 16px; line-height: 1.6;">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.</p>
<p style="font-size: 16px; line-height: 1.6;"><strong>Your personal webinar access link will be emailed to you 1 day before the event.</strong></p>
<p style="text-align: center; margin-top: 30px;">
<a href="{$google_link}" style="background: linear-gradient(135deg, #3f51b5 0%, #9c27b0 100%); color: #ffffff; padding: 12px 25px; text-decoration: none; border-radius: 8px; font-weight: bold;">Add to Calendar</a>
</p>
</td>
</tr>
<tr>
<td align="center" style="padding: 20px; background-color: #f4f4f4; border-top: 1px solid #dddddd;">
<p style="margin: 0; color: #777;">&copy; 2025 AppWizzy. All rights reserved.</p>
<p style="margin: 5px 0 0; color: #777;">You can <a href="{$protocol}://{$host}/" style="color: #3f51b5;">visit our website</a> for more information.</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
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 . '<br><small><strong>Professional Vibe-Coding Webinar</strong></small>',
'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.']);
}