187 lines
7.8 KiB
PHP
187 lines
7.8 KiB
PHP
<?php
|
|
/**
|
|
* File: ContactController.php
|
|
* Version: 2.16
|
|
* Path: /app/Controllers/ContactController.php
|
|
* Purpose: Handles contact form submission, abuse checks, and verification logic.
|
|
* Defers contact/sales email sending until verification.
|
|
* Tracks newsletter opt-in flag for unified post-verification messaging.
|
|
* Project: Wizdom Networks Website
|
|
*/
|
|
|
|
namespace WizdomNetworks\WizeWeb\Controllers;
|
|
|
|
use WizdomNetworks\WizeWeb\Core\View;
|
|
use WizdomNetworks\WizeWeb\Utilities\Logger;
|
|
use WizdomNetworks\WizeWeb\Utilities\Validator;
|
|
use WizdomNetworks\WizeWeb\Utilities\Sanitizer;
|
|
use WizdomNetworks\WizeWeb\Utilities\Database;
|
|
use WizdomNetworks\WizeWeb\Utilities\SessionHelper;
|
|
use WizdomNetworks\WizeWeb\Utilities\SubmissionCheck;
|
|
use WizdomNetworks\WizeWeb\Utilities\ErrorHandler;
|
|
use WizdomNetworks\WizeWeb\Utilities\Response;
|
|
use WizdomNetworks\WizeWeb\Services\EmailService;
|
|
use WizdomNetworks\WizeWeb\Services\VerificationService;
|
|
use WizdomNetworks\WizeWeb\Models\ContactModel;
|
|
use Exception;
|
|
|
|
class ContactController
|
|
{
|
|
private EmailService $emailService;
|
|
private VerificationService $verificationService;
|
|
|
|
/**
|
|
* Initializes email and verification service dependencies.
|
|
*/
|
|
public function __construct()
|
|
{
|
|
$this->emailService = new EmailService();
|
|
$this->verificationService = new VerificationService();
|
|
}
|
|
|
|
/**
|
|
* Renders the landing page containing the contact form.
|
|
*/
|
|
public function index(): void
|
|
{
|
|
View::render('pages/landing');
|
|
}
|
|
|
|
/**
|
|
* Handles form submission: validates, logs, checks abuse, stores, and triggers verification.
|
|
* If user opted in to the newsletter, flags for follow-up after verification.
|
|
*/
|
|
public function submit(): void
|
|
{
|
|
Logger::info("Executing controller: ContactController::submit");
|
|
|
|
try {
|
|
$formData = [
|
|
'first_name' => Sanitizer::sanitizeString($_POST['first_name'] ?? ''),
|
|
'last_name' => Sanitizer::sanitizeString($_POST['last_name'] ?? ''),
|
|
'email' => Sanitizer::sanitizeString($_POST['email'] ?? ''),
|
|
'phone' => Sanitizer::sanitizeString($_POST['phone'] ?? ''),
|
|
'subject' => Sanitizer::sanitizeString($_POST['subject'] ?? ''),
|
|
'message' => Sanitizer::sanitizeString($_POST['message'] ?? ''),
|
|
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
|
|
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
|
|
'pending_newsletter_opt_in' => isset($_POST['subscribe_newsletter']) && $_POST['subscribe_newsletter'] === '1' ? 1 : 0
|
|
];
|
|
|
|
foreach ($formData as $key => $value) {
|
|
Logger::info("Sanitized input: {$key} = {$value}");
|
|
}
|
|
|
|
// Validate required fields and email format
|
|
if (
|
|
empty($formData['first_name']) ||
|
|
empty($formData['last_name']) ||
|
|
empty($formData['email']) ||
|
|
empty($formData['phone']) ||
|
|
empty($formData['subject']) ||
|
|
empty($formData['message']) ||
|
|
!Validator::isEmail($formData['email'])
|
|
) {
|
|
Logger::info("Validation failed for contact form submission");
|
|
$_SESSION['contact_error'] = 'Validation error. Please try again.';
|
|
SessionHelper::writeClose();
|
|
$this->respondOrRedirect(false, 'Validation error.');
|
|
}
|
|
|
|
$db = Database::getConnection();
|
|
|
|
// Run submission abuse heuristics
|
|
$evaluation = SubmissionCheck::evaluate($db, $formData['email'], $formData['phone'], $formData['ip_address']);
|
|
Logger::info("Submission evaluation result: " . json_encode($evaluation));
|
|
|
|
if ($evaluation['action'] === 'block') {
|
|
$_SESSION['contact_error'] = "Submission blocked due to suspicious activity. If this is a mistake, please contact us directly.";
|
|
Logger::warning("Blocked submission from IP: {$formData['ip_address']}, Reason: {$evaluation['reason']}");
|
|
$this->emailService->alertAdmins('Blocked Submission Detected', $evaluation['reason'], $formData);
|
|
SessionHelper::writeClose();
|
|
$this->respondOrRedirect(false, 'Submission blocked.');
|
|
}
|
|
|
|
// Log submission intent
|
|
$logId = null;
|
|
try {
|
|
$logStmt = $db->prepare("INSERT INTO submission_logs (email, phone, ip_address, user_agent, was_saved, reason) VALUES (:email, :phone, :ip, :ua, :saved, :reason)");
|
|
$logStmt->execute([
|
|
':email' => $formData['email'],
|
|
':phone' => $formData['phone'],
|
|
':ip' => $formData['ip_address'],
|
|
':ua' => $formData['user_agent'],
|
|
':saved' => 0,
|
|
':reason' => $evaluation['reason'],
|
|
]);
|
|
$logId = $db->lastInsertId();
|
|
} catch (\Throwable $e) {
|
|
Logger::error("Failed to insert into submission_logs: " . $e->getMessage());
|
|
}
|
|
|
|
// Save form content
|
|
$contactModel = new ContactModel($db);
|
|
$saveSuccess = $contactModel->saveContactForm($formData);
|
|
$contactId = $db->lastInsertId();
|
|
|
|
// Assign verification code
|
|
if ($saveSuccess) {
|
|
$verificationCode = $this->verificationService->generateCode();
|
|
$expiresAt = $this->verificationService->getExpirationTime();
|
|
$this->verificationService->assignCodeToRecord('contact_messages', $contactId, $verificationCode, $expiresAt);
|
|
|
|
$this->emailService->sendVerificationEmail(
|
|
$formData['email'],
|
|
$verificationCode,
|
|
'verify_contact',
|
|
['first_name' => $formData['first_name']]
|
|
);
|
|
}
|
|
|
|
// Update log if save succeeded
|
|
if ($saveSuccess && $logId) {
|
|
$update = $db->prepare("UPDATE submission_logs SET was_saved = 1 WHERE id = :id");
|
|
$update->execute([':id' => $logId]);
|
|
}
|
|
|
|
SessionHelper::writeClose();
|
|
$this->respondOrRedirect(true, 'Your message was submitted. Please check your email to verify.');
|
|
|
|
} catch (\Throwable $e) {
|
|
Logger::error("Fatal error in ContactController::submit: " . $e->getMessage());
|
|
$this->emailService->alertAdmins('ContactController::submit - Uncaught Exception', $e->getMessage(), $_POST ?? []);
|
|
$_SESSION['contact_error'] = 'An internal error occurred. Please try again later.';
|
|
SessionHelper::writeClose();
|
|
$this->respondOrRedirect(false, 'An internal error occurred.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Responds to client depending on request type (AJAX vs standard).
|
|
* @param bool $success Indicates if the operation succeeded
|
|
* @param string $message Message to return or display
|
|
*/
|
|
private function respondOrRedirect(bool $success, string $message): void
|
|
{
|
|
$isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&
|
|
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
|
|
|
|
Logger::debug('Detected request type: ' . ($_SERVER['HTTP_X_REQUESTED_WITH'] ?? 'none'));
|
|
Logger::debug('Will respond with: ' . ($isAjax ? 'JSON' : 'HTML fallback'));
|
|
|
|
if ($isAjax) {
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['success' => $success, 'message' => $message]);
|
|
exit;
|
|
}
|
|
|
|
if ($success) {
|
|
View::render('pages/contact_check_email');
|
|
} else {
|
|
header("Location: /#contact");
|
|
}
|
|
|
|
exit;
|
|
}
|
|
}
|