WizdomWeb/app/Controllers/VerificationController.php

178 lines
6.4 KiB
PHP

<?php
/**
* File: VerificationController.php
* Version: 1.10
* Path: /app/Controllers/VerificationController.php
* Purpose: Handles email verification for newsletter and contact messages, including code expiration and attempt logging.
* Now wired to use EmailService for all post-verification messaging, including unified contact+newsletter handling.
* Project: Wizdom Networks Website
*/
namespace WizdomNetworks\WizeWeb\Controllers;
use WizdomNetworks\WizeWeb\Core\View;
use WizdomNetworks\WizeWeb\Utilities\Database;
use WizdomNetworks\WizeWeb\Utilities\Logger;
use WizdomNetworks\WizeWeb\Services\EmailService;
class VerificationController
{
private EmailService $emailService;
public function __construct()
{
$this->emailService = new EmailService();
}
/**
* Handles email verification for newsletter and contact submissions using a unique code.
*
* - If the code matches an unverified record: marks it as verified and sends confirmations.
* - If already verified: shows a message.
* - If expired: prompts user to resend.
* - If invalid: redirects user to restart the process.
*
* @param string $code The verification code from the URL path.
* @return void
*/
public function verify(string $code): void
{
try {
if (empty($code)) {
Logger::error("Email verification attempted without a code.");
View::render('pages/verify_failed', [
'reason' => 'No verification code provided.',
'redirect' => true
]);
return;
}
$db = Database::getConnection();
$subscriber = null;
$table = null;
$type = null;
// Attempt to locate the subscriber record by code in either table
$stmt = $db->prepare("SELECT * FROM subscribers WHERE verification_code = ?");
$stmt->execute([$code]);
$subscriber = $stmt->fetch();
if ($subscriber) {
$table = 'subscribers';
$type = 'newsletter';
} else {
$stmt = $db->prepare("SELECT * FROM contact_messages WHERE verification_code = ?");
$stmt->execute([$code]);
$subscriber = $stmt->fetch();
if ($subscriber) {
$table = 'contact_messages';
$type = 'contact';
}
}
// If no record was found at all
if (!$subscriber) {
Logger::error("Invalid verification code attempted: $code");
View::render('pages/verify_failed', [
'reason' => 'That link is invalid. You may need to start a new submission.',
'redirect' => true
]);
return;
}
// Handle expired code case
if (!empty($subscriber['verification_expires_at']) && strtotime($subscriber['verification_expires_at']) < time()) {
Logger::info("Verification link expired: $code");
View::render('pages/verify_failed', [
'reason' => 'Your verification link has expired. Please request a new one.',
'type' => $type ?? 'unknown'
]);
return;
}
// Log the verification attempt regardless of outcome
$safeType = in_array($type, ['contact', 'newsletter', 'contact+newsletter'], true) ? $type : 'unknown';
$logAttempt = $db->prepare("
INSERT INTO verification_attempts (email, type, attempted_at, ip_address, user_agent)
VALUES (?, ?, NOW(), ?, ?)
");
$logAttempt->execute([
$subscriber['email'] ?? '[unknown]',
$safeType,
$_SERVER['REMOTE_ADDR'] ?? 'unknown',
$_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
]);
// If already verified
if ((int) $subscriber['is_verified'] === 1) {
View::render('pages/verify_success', [
'type' => $type ?? 'unknown',
'message' => 'This submission has already been verified.'
]);
return;
}
// Mark the submission as verified
$update = $db->prepare("UPDATE $table SET is_verified = 1, verification_code = NULL WHERE id = ?");
$update->execute([$subscriber['id']]);
Logger::info("Subscriber verified: ID {$subscriber['id']} via $type");
// Handle post-verification logic for contact submissions
if ($type === 'contact') {
$stmt = $db->prepare("
SELECT first_name, last_name, subject, message, pending_newsletter_opt_in
FROM contact_messages WHERE id = ?
");
$stmt->execute([$subscriber['id']]);
$details = $stmt->fetch();
$emailData = [
'email' => $subscriber['email'],
'first_name' => $details['first_name'] ?? '',
'last_name' => $details['last_name'] ?? '',
'subject' => $details['subject'] ?? '',
'message' => $details['message'] ?? '',
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
];
// If opted in to newsletter from contact form
if (!empty($details['pending_newsletter_opt_in'])) {
$this->emailService->sendContactAndNewsletterWelcome($emailData);
$db->prepare("UPDATE contact_messages SET pending_newsletter_opt_in = 0 WHERE id = ?")
->execute([$subscriber['id']]);
$db->prepare("
INSERT INTO subscribers (email, is_verified, created_at)
VALUES (?, 1, NOW())
ON DUPLICATE KEY UPDATE is_verified = 1
")->execute([$subscriber['email']]);
$type = 'contact+newsletter'; // Refined to reflect both intents
} else {
$this->emailService->sendConfirmationToUser($emailData);
}
$this->emailService->sendSalesNotification($emailData);
}
// Final success render
View::render('pages/verify_success', [
'type' => $type ?? 'unknown',
'message' => null
]);
} catch (\Throwable $e) {
Logger::error("Verification exception: " . $e->getMessage());
View::render('pages/verify_failed', [
'reason' => 'An error occurred during verification.',
'redirect' => true
]);
}
}
}