Fix session handling for contact form success/error alerts and relocate SessionHelper::start in index.php
This commit is contained in:
parent
7a0594d4f5
commit
4e35d36485
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
/**
|
||||
* File: ContactController.php
|
||||
* Version: 1.6
|
||||
* Version: 2.0
|
||||
* Path: /app/Controllers/ContactController.php
|
||||
* Purpose: Handles contact form display and submission logic.
|
||||
* Purpose: Handles contact form submission with error alerts to admins and success to sales.
|
||||
* Project: Wizdom Networks Website
|
||||
*/
|
||||
|
||||
|
|
@ -12,85 +12,103 @@ namespace WizdomNetworks\WizeWeb\Controllers;
|
|||
use WizdomNetworks\WizeWeb\Core\View;
|
||||
use WizdomNetworks\WizeWeb\Utilities\Logger;
|
||||
use WizdomNetworks\WizeWeb\Utilities\Validator;
|
||||
use WizdomNetworks\WizeWeb\Utilities\Response;
|
||||
use WizdomNetworks\WizeWeb\Utilities\Sanitizer;
|
||||
use WizdomNetworks\WizeWeb\Utilities\Database;
|
||||
use WizdomNetworks\WizeWeb\Utilities\EmailHelper;
|
||||
use WizdomNetworks\WizeWeb\Models\ContactModel;
|
||||
use Exception;
|
||||
|
||||
class ContactController
|
||||
{
|
||||
/**
|
||||
* Display the contact form page.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function index(): void
|
||||
{
|
||||
View::render('pages/contact');
|
||||
View::render('pages/landing');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle contact form submission.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function submit(): void
|
||||
{
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
Response::badRequest('Invalid request method.');
|
||||
}
|
||||
{
|
||||
Logger::info("Executing controller: ContactController::submit");
|
||||
Logger::info("📦 PHP Session ID: " . session_id());
|
||||
|
||||
$data = $_POST;
|
||||
|
||||
$requiredFields = ['first_name', 'last_name', 'email', 'phone', 'subject', 'message'];
|
||||
foreach ($requiredFields as $field) {
|
||||
if (empty($data[$field])) {
|
||||
Response::badRequest("Missing required field: $field");
|
||||
}
|
||||
}
|
||||
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',
|
||||
];
|
||||
|
||||
// Sanitize input
|
||||
$firstName = Sanitizer::sanitizeString($data['first_name']);
|
||||
$lastName = Sanitizer::sanitizeString($data['last_name']);
|
||||
$email = Sanitizer::sanitizeString($data['email']);
|
||||
$phone = Sanitizer::sanitizeString($data['phone']);
|
||||
$subject = Sanitizer::sanitizeString($data['subject']);
|
||||
$message = Sanitizer::sanitizeString($data['message']);
|
||||
|
||||
if (!Validator::IsEmail($email)) {
|
||||
Response::badRequest('Invalid email address.');
|
||||
}
|
||||
|
||||
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
|
||||
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
|
||||
|
||||
$pdo = (new Database())->getConnection();
|
||||
$contact = new ContactModel($pdo);
|
||||
$result = $contact->saveContactForm([
|
||||
'first_name' => $firstName,
|
||||
'last_name' => $lastName,
|
||||
'email' => $email,
|
||||
'phone' => $phone,
|
||||
'subject' => $subject,
|
||||
'message' => $message,
|
||||
'ip_address' => $ip,
|
||||
'user_agent' => $userAgent,
|
||||
]);
|
||||
|
||||
if (!$result) {
|
||||
Logger::error("Contact form submission failed for email: $email");
|
||||
Response::serverError('An error occurred while submitting your message. Please try again later.');
|
||||
}
|
||||
|
||||
Response::json([
|
||||
'success' => true,
|
||||
'message' => 'Your message has been successfully submitted. Thank you!'
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
Logger::error("Exception in contact submission: " . $e->getMessage());
|
||||
Response::serverError('A server error occurred. Please try again later.');
|
||||
foreach ($formData as $key => $value) {
|
||||
Logger::info("Sanitized input: {$key} = {$value}");
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
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'] = 'An internal error occurred. Please try again later.';
|
||||
|
||||
header("Location: /?contact_error=1#contact");
|
||||
|
||||
exit;
|
||||
|
||||
}
|
||||
|
||||
// Save to DB
|
||||
$db = Database::getConnection();
|
||||
$contactModel = new ContactModel($db);
|
||||
$saveSuccess = $contactModel->saveContactForm($formData);
|
||||
|
||||
// Send to sales team
|
||||
$emailSuccess = EmailHelper::sendContactNotification($formData);
|
||||
|
||||
// Send confirmation to user
|
||||
$confirmationSuccess = EmailHelper::sendConfirmationToUser($formData);
|
||||
|
||||
if ($saveSuccess && $emailSuccess) {
|
||||
$_SESSION['contact_success'] = true;
|
||||
|
||||
} else {
|
||||
Logger::error("Form processed but saveSuccess={$saveSuccess}, emailSuccess={$emailSuccess}");
|
||||
$_SESSION['contact_error'] = 'Your message was received but an internal error occurred. A confirmation may not have been sent.';
|
||||
|
||||
EmailHelper::alertAdmins('ContactController::submit - DB or email failure', 'Partial failure', $formData);
|
||||
}
|
||||
|
||||
if (!$confirmationSuccess) {
|
||||
Logger::error("Confirmation email failed to send to user: {$formData['email']}");
|
||||
// Don't show user error — it's non-critical
|
||||
}
|
||||
Logger::info("✅ Writing session flag: contact_success = true");
|
||||
Logger::info("✅ Session content before redirect: " . json_encode($_SESSION));
|
||||
|
||||
header("Location: /?contact_submitted=1#contact");
|
||||
|
||||
exit;
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
Logger::error("Fatal error in ContactController::submit: " . $e->getMessage());
|
||||
EmailHelper::alertAdmins('ContactController::submit - Uncaught Exception', $e->getMessage(), $_POST ?? []);
|
||||
$_SESSION['contact_error'] = 'An internal error occurred. Please try again later.';
|
||||
|
||||
Logger::info("✅ Writing session flag: catch contact_error = " . $_SESSION['contact_error']);
|
||||
Logger::info("✅ Session content before redirect: " . json_encode($_SESSION));
|
||||
header("Location: /?contact_error=2#contact");
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
// File: app/Controllers/LandingController.php
|
||||
// Version: v1.0
|
||||
// Version: v1.1
|
||||
// Purpose: Handles landing page rendering for Arsha one-pager
|
||||
// Project: Wizdom Networks Website
|
||||
|
||||
|
|
@ -8,12 +8,16 @@ namespace WizdomNetworks\WizeWeb\Controllers;
|
|||
|
||||
use WizdomNetworks\WizeWeb\Core\View;
|
||||
use WizdomNetworks\WizeWeb\Utilities\Logger;
|
||||
use WizdomNetworks\WizeWeb\Utilities\SessionHelper;
|
||||
|
||||
class LandingController
|
||||
{
|
||||
public function index()
|
||||
public function index(): void
|
||||
{
|
||||
Logger::info("Rendering landing page via LandingController@index");
|
||||
SessionHelper::start(); // ✅ Start session before rendering
|
||||
Logger::info("Session status: " . session_status());
|
||||
Logger::info("📥 Landing page session ID: " . session_id());
|
||||
Logger::info("🟡 Landing page session before render: " . json_encode($_SESSION));
|
||||
|
||||
$data = [
|
||||
'pageTitle' => 'Wizdom Networks | One-Pager'
|
||||
|
|
|
|||
|
|
@ -1,26 +1,98 @@
|
|||
<?php
|
||||
/**
|
||||
* File: ContactModel.php
|
||||
* Version: 2.1
|
||||
* Path: /app/Models/ContactModel.php
|
||||
* Purpose: Manages saving and retrieving contact records from both legacy and full form submissions.
|
||||
* Project: Wizdom Networks Website
|
||||
*/
|
||||
|
||||
namespace WizdomNetworks\WizeWeb\Models;
|
||||
|
||||
use PDO;
|
||||
use Exception;
|
||||
use WizdomNetworks\WizeWeb\Utilities\Logger;
|
||||
use WizdomNetworks\WizeWeb\Utilities\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Contact Model
|
||||
*
|
||||
* Handles database operations related to contacts.
|
||||
*/
|
||||
class ContactModel
|
||||
{
|
||||
private $db;
|
||||
private PDO $db;
|
||||
|
||||
public function __construct($db)
|
||||
/**
|
||||
* ContactModel constructor.
|
||||
*
|
||||
* @param PDO $db Database connection
|
||||
*/
|
||||
public function __construct(PDO $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a contact by ID.
|
||||
* Legacy method to insert simplified contact into `contacts` table.
|
||||
*
|
||||
* @param array $contactData ['name' => string, 'email' => string, 'message' => string]
|
||||
* @return bool
|
||||
*/
|
||||
public function addContact(array $contactData): bool
|
||||
{
|
||||
try {
|
||||
$stmt = $this->db->prepare("
|
||||
INSERT INTO contacts (name, email, message)
|
||||
VALUES (:name, :email, :message)
|
||||
");
|
||||
|
||||
$name = trim(($contactData['name'] ?? '') ?: (($contactData['first_name'] ?? '') . ' ' . ($contactData['last_name'] ?? '')));
|
||||
$stmt->bindParam(':name', $name);
|
||||
$stmt->bindParam(':email', $contactData['email']);
|
||||
$stmt->bindParam(':message', $contactData['message']);
|
||||
|
||||
return $stmt->execute();
|
||||
} catch (Exception $e) {
|
||||
Logger::error("ContactModel::addContact failed: " . $e->getMessage());
|
||||
ErrorHandler::exception($e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves full contact form submission to the `contact_messages` table.
|
||||
*
|
||||
* @param array $formData Associative array of form input
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
public function saveContactForm(array $formData): bool
|
||||
{
|
||||
try {
|
||||
$stmt = $this->db->prepare("
|
||||
INSERT INTO contact_messages (
|
||||
first_name, last_name, email, phone, subject, message,
|
||||
ip_address, user_agent
|
||||
) VALUES (
|
||||
:first_name, :last_name, :email, :phone, :subject, :message,
|
||||
:ip_address, :user_agent
|
||||
)
|
||||
");
|
||||
|
||||
$stmt->bindParam(':first_name', $formData['first_name']);
|
||||
$stmt->bindParam(':last_name', $formData['last_name']);
|
||||
$stmt->bindParam(':email', $formData['email']);
|
||||
$stmt->bindParam(':phone', $formData['phone']);
|
||||
$stmt->bindParam(':subject', $formData['subject']);
|
||||
$stmt->bindParam(':message', $formData['message']);
|
||||
$stmt->bindParam(':ip_address', $formData['ip_address']);
|
||||
$stmt->bindParam(':user_agent', $formData['user_agent']);
|
||||
|
||||
return $stmt->execute();
|
||||
} catch (Exception $e) {
|
||||
Logger::error("ContactModel::saveContactForm failed: " . $e->getMessage());
|
||||
ErrorHandler::exception($e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a contact record by ID from `contact_messages`.
|
||||
*
|
||||
* @param int $id
|
||||
* @return array|null
|
||||
|
|
@ -28,56 +100,21 @@ class ContactModel
|
|||
public function getContactById(int $id): ?array
|
||||
{
|
||||
try {
|
||||
Logger::info("[DEBUG] Fetching contact with ID: $id");
|
||||
|
||||
$stmt = $this->db->prepare("SELECT * FROM contacts WHERE id = :id");
|
||||
$stmt->bindParam(':id', $id, \PDO::PARAM_INT);
|
||||
$stmt = $this->db->prepare("SELECT * FROM contact_messages WHERE id = :id");
|
||||
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch();
|
||||
|
||||
$contact = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
|
||||
Logger::info("[DEBUG] Contact data retrieved: " . json_encode($contact));
|
||||
|
||||
return $contact ?: null;
|
||||
} catch (\Exception $e) {
|
||||
Logger::error("[ERROR] Failed to fetch contact with ID $id: " . $e->getMessage());
|
||||
return $result ?: null;
|
||||
} catch (Exception $e) {
|
||||
Logger::error("ContactModel::getContactById failed: " . $e->getMessage());
|
||||
ErrorHandler::exception($e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new contact to the database.
|
||||
*
|
||||
* @param array $contactData
|
||||
* @return bool
|
||||
*/
|
||||
public function addContact(array $contactData): bool
|
||||
{
|
||||
try {
|
||||
Logger::info("[DEBUG] Adding new contact: " . json_encode($contactData));
|
||||
|
||||
$stmt = $this->db->prepare(
|
||||
"INSERT INTO contacts (name, email, message) VALUES (:name, :email, :message)"
|
||||
);
|
||||
$stmt->bindParam(':name', $contactData['name']);
|
||||
$stmt->bindParam(':email', $contactData['email']);
|
||||
$stmt->bindParam(':message', $contactData['message']);
|
||||
|
||||
$stmt->execute();
|
||||
|
||||
Logger::info("[DEBUG] Contact successfully added.");
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Logger::error("[ERROR] Failed to add contact: " . $e->getMessage());
|
||||
ErrorHandler::exception($e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a contact by ID.
|
||||
* Deletes a contact record by ID from `contact_messages`.
|
||||
*
|
||||
* @param int $id
|
||||
* @return bool
|
||||
|
|
@ -85,65 +122,13 @@ class ContactModel
|
|||
public function deleteContactById(int $id): bool
|
||||
{
|
||||
try {
|
||||
Logger::info("[DEBUG] Deleting contact with ID: $id");
|
||||
|
||||
$stmt = $this->db->prepare("DELETE FROM contacts WHERE id = :id");
|
||||
$stmt->bindParam(':id', $id, \PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
|
||||
Logger::info("[DEBUG] Contact with ID $id successfully deleted.");
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
Logger::error("[ERROR] Failed to delete contact with ID $id: " . $e->getMessage());
|
||||
$stmt = $this->db->prepare("DELETE FROM contact_messages WHERE id = :id");
|
||||
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
return $stmt->execute();
|
||||
} catch (Exception $e) {
|
||||
Logger::error("ContactModel::deleteContactById failed: " . $e->getMessage());
|
||||
ErrorHandler::exception($e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Save a full contact form submission to contact_messages table.
|
||||
*
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function saveContactForm(array $data): bool
|
||||
{
|
||||
try {
|
||||
$sql = "INSERT INTO contact_messages (
|
||||
first_name,
|
||||
last_name,
|
||||
email,
|
||||
phone,
|
||||
subject,
|
||||
message,
|
||||
ip_address,
|
||||
user_agent
|
||||
) VALUES (
|
||||
:first_name,
|
||||
:last_name,
|
||||
:email,
|
||||
:phone,
|
||||
:subject,
|
||||
:message,
|
||||
:ip_address,
|
||||
:user_agent
|
||||
)";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->bindParam(':first_name', $data['first_name']);
|
||||
$stmt->bindParam(':last_name', $data['last_name']);
|
||||
$stmt->bindParam(':email', $data['email']);
|
||||
$stmt->bindParam(':phone', $data['phone']);
|
||||
$stmt->bindParam(':subject', $data['subject']);
|
||||
$stmt->bindParam(':message', $data['message']);
|
||||
$stmt->bindParam(':ip_address', $data['ip_address']);
|
||||
$stmt->bindParam(':user_agent', $data['user_agent']);
|
||||
|
||||
return $stmt->execute();
|
||||
} catch (\Exception $e) {
|
||||
Logger::error("Failed to save contact form: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,78 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* File: Database.php
|
||||
* Version: 1.1
|
||||
* Path: /app/Utilities/Database.php
|
||||
* Purpose: Provides static method to retrieve PDO database connection using environment variables.
|
||||
* Project: Wizdom Networks Website & HelpDesk+
|
||||
*/
|
||||
|
||||
namespace WizdomNetworks\WizeWeb\Utilities;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use WizdomNetworks\WizeWeb\Utilities\Logger;
|
||||
use WizdomNetworks\WizeWeb\Utilities\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Database Utility
|
||||
*
|
||||
* A utility for managing database connections and queries.
|
||||
*
|
||||
* Integrates logging for connection status and query execution.
|
||||
*/
|
||||
class Database
|
||||
{
|
||||
/**
|
||||
* @var PDO|null The PDO instance for database connection.
|
||||
*/
|
||||
private ?PDO $connection = null;
|
||||
|
||||
/**
|
||||
* Database constructor.
|
||||
* Returns a PDO connection using environment variables.
|
||||
*
|
||||
* Initializes the database connection.
|
||||
* @return PDO
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a connection to the database.
|
||||
*/
|
||||
private function connect(): void
|
||||
{
|
||||
$dsn = sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4', $_ENV['DB_HOST'], $_ENV['DB_NAME']);
|
||||
|
||||
try {
|
||||
$this->connection = new PDO($dsn, $_ENV['DB_USER'], $_ENV['DB_PASS']);
|
||||
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
Logger::info('Database connection established successfully.');
|
||||
} catch (PDOException $e) {
|
||||
Logger::error('Database connection failed: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a query and returns the result.
|
||||
*
|
||||
* @param string $query The SQL query to execute.
|
||||
* @param array $params Parameters for prepared statements (optional).
|
||||
* @return array The query result.
|
||||
*/
|
||||
public function query(string $query, array $params = []): array
|
||||
public static function getConnection(): PDO
|
||||
{
|
||||
try {
|
||||
$stmt = $this->connection->prepare($query);
|
||||
$stmt->execute($params);
|
||||
Logger::info('Query executed successfully: ' . $query);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
Logger::error('Query failed: ' . $query . ' | Error: ' . $e->getMessage());
|
||||
$host = $_ENV['DB_HOST'];
|
||||
$port = $_ENV['DB_PORT'];
|
||||
$dbname = $_ENV['DB_NAME'];
|
||||
$username = $_ENV['DB_USER'];
|
||||
$password = $_ENV['DB_PASS'];
|
||||
|
||||
$dsn = "mysql:host={$host};port={$port};dbname={$dbname};charset=utf8mb4";
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
];
|
||||
|
||||
Logger::info("Database connection established successfully.");
|
||||
return new PDO($dsn, $username, $password, $options);
|
||||
} catch (\Throwable $e) {
|
||||
Logger::error("Database connection failed: " . $e->getMessage());
|
||||
ErrorHandler::exception($e);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the PDO connection instance.
|
||||
*
|
||||
* @return PDO The PDO instance.
|
||||
*/
|
||||
public function getConnection(): PDO
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,218 @@
|
|||
<?php
|
||||
/**
|
||||
* File: EmailHelper.php
|
||||
* Version: 2.4
|
||||
* Path: /app/Utilities/EmailHelper.php
|
||||
* Purpose: Sends contact confirmations, sales notifications, and admin alerts with proper formatting and logic.
|
||||
* Project: Wizdom Networks Website
|
||||
*/
|
||||
|
||||
namespace WizdomNetworks\WizeWeb\Utilities;
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
class EmailHelper
|
||||
{
|
||||
public static function configureMailer(PHPMailer $mail): void
|
||||
{
|
||||
$mail->isSMTP();
|
||||
$mail->Host = $_ENV['SMTP_HOST'] ?? 'localhost';
|
||||
$mail->Port = $_ENV['SMTP_PORT'] ?? 25;
|
||||
|
||||
$mail->SMTPAuth = filter_var($_ENV['SMTP_AUTH'] ?? false, FILTER_VALIDATE_BOOLEAN);
|
||||
$mail->Username = $_ENV['SMTP_USER'] ?? '';
|
||||
$mail->Password = $_ENV['SMTP_PASS'] ?? '';
|
||||
|
||||
$mail->SMTPAutoTLS = filter_var($_ENV['SMTP_AUTO_TLS'] ?? true, FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
$encryption = strtolower(trim($_ENV['SMTP_ENCRYPTION'] ?? ''));
|
||||
if ($encryption === 'ssl') {
|
||||
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
|
||||
} elseif ($encryption === 'tls') {
|
||||
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
|
||||
} else {
|
||||
$mail->SMTPSecure = '';
|
||||
}
|
||||
|
||||
$fromEmail = $_ENV['SMTP_FROM_EMAIL'] ?? 'no-reply@localhost';
|
||||
$fromName = $_ENV['SMTP_FROM_NAME'] ?? 'Wizdom Mailer';
|
||||
$mail->setFrom($fromEmail, $fromName);
|
||||
|
||||
$mail->SMTPOptions = [
|
||||
'ssl' => [
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
'allow_self_signed' => true
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
private static function parseRecipients(string $rawList): array
|
||||
{
|
||||
$emails = explode(',', $rawList);
|
||||
$validEmails = [];
|
||||
|
||||
foreach ($emails as $email) {
|
||||
$email = trim($email);
|
||||
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$validEmails[] = $email;
|
||||
}
|
||||
}
|
||||
|
||||
return $validEmails;
|
||||
}
|
||||
|
||||
private static function buildContactHtmlBody(array $data): string
|
||||
{
|
||||
return "
|
||||
<strong>Name:</strong> {$data['first_name']} {$data['last_name']}<br>
|
||||
<strong>Email:</strong> {$data['email']}<br>
|
||||
<strong>Phone:</strong> {$data['phone']}<br>
|
||||
<strong>Subject:</strong> {$data['subject']}<br>
|
||||
<strong>Message:</strong><br>
|
||||
<pre style='white-space:pre-wrap;'>{$data['message']}</pre>
|
||||
";
|
||||
}
|
||||
|
||||
private static function buildErrorReportHtml(string $context, string $errorMessage, $data = []): string
|
||||
{
|
||||
if (is_string($data)) {
|
||||
$decoded = json_decode($data, true);
|
||||
$data = is_array($decoded) ? $decoded : ['raw_data' => $data];
|
||||
}
|
||||
|
||||
$body = "
|
||||
<strong>Context:</strong> {$context}<br>
|
||||
<strong>Error Message:</strong><br>
|
||||
<pre style='white-space:pre-wrap;color:#8b0000;'>{$errorMessage}</pre>
|
||||
";
|
||||
|
||||
if (!empty($data)) {
|
||||
$body .= "<hr><strong>Associated Data:</strong><br><ul>";
|
||||
foreach ($data as $key => $value) {
|
||||
$safeKey = htmlspecialchars($key);
|
||||
$safeValue = nl2br(htmlspecialchars((string)$value));
|
||||
$body .= "<li><strong>{$safeKey}:</strong> {$safeValue}</li>";
|
||||
}
|
||||
$body .= "</ul>";
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
private static function buildSalesHtmlBody(array $data): string
|
||||
{
|
||||
$submittedAt = date('Y-m-d H:i:s');
|
||||
return "
|
||||
<p><strong>New contact submission received</strong> on {$submittedAt}.</p>
|
||||
<ul>
|
||||
<li><strong>Name:</strong> {$data['first_name']} {$data['last_name']}</li>
|
||||
<li><strong>Email:</strong> {$data['email']}</li>
|
||||
<li><strong>Phone:</strong> {$data['phone']}</li>
|
||||
<li><strong>Subject:</strong> {$data['subject']}</li>
|
||||
<li><strong>Message:</strong><br><pre style='white-space:pre-wrap;'>{$data['message']}</pre></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p style='font-size: 0.9em; color: #888;'>IP: {$data['ip_address']}<br>User-Agent: {$data['user_agent']}</p>
|
||||
";
|
||||
}
|
||||
|
||||
private static function buildConfirmationHtmlBody(array $data): string
|
||||
{
|
||||
$submittedAt = date('Y-m-d H:i:s');
|
||||
return "
|
||||
<p>Hi {$data['first_name']},</p>
|
||||
<p>Thank you for contacting Wizdom Networks. This message confirms that we received your inquiry on <strong>{$submittedAt}</strong>.</p>
|
||||
<ul>
|
||||
<li><strong>Subject:</strong> {$data['subject']}</li>
|
||||
<li><strong>Message:</strong><br><pre style='white-space:pre-wrap;'>{$data['message']}</pre></li>
|
||||
</ul>
|
||||
<p>We’ll be in touch shortly. If it’s urgent, call us at <strong>416-USE-WISE</strong>.</p>
|
||||
<p style='font-size: 0.9em; color: #888;'>IP: {$data['ip_address']}<br>User-Agent: {$data['user_agent']}</p>
|
||||
";
|
||||
}
|
||||
|
||||
public static function sendContactNotification(array $data): bool
|
||||
{
|
||||
try {
|
||||
$mail = new PHPMailer(true);
|
||||
self::configureMailer($mail);
|
||||
|
||||
$recipients = self::parseRecipients($_ENV['SALES_EMAILS'] ?? '');
|
||||
foreach ($recipients as $email) {
|
||||
$mail->addAddress($email);
|
||||
}
|
||||
|
||||
if (empty($mail->getToAddresses())) {
|
||||
Logger::error("EmailHelper: No valid SALES_EMAILS configured.");
|
||||
return false;
|
||||
}
|
||||
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = 'New Contact Form Submission';
|
||||
$mail->Body = self::buildSalesHtmlBody($data);
|
||||
|
||||
$mail->send();
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
Logger::error("Email send failure to sales: " . $e->getMessage());
|
||||
self::alertAdmins("sendContactNotification", $e->getMessage(), $data);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function sendConfirmationToUser(array $data): bool
|
||||
{
|
||||
try {
|
||||
$mail = new PHPMailer(true);
|
||||
self::configureMailer($mail);
|
||||
|
||||
$mail->addAddress($data['email'], "{$data['first_name']} {$data['last_name']}");
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = 'Your Wizdom Networks Contact Form Submission';
|
||||
$mail->Body = self::buildConfirmationHtmlBody($data);
|
||||
|
||||
$mail->send();
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
Logger::error("Email send failure to user: " . $e->getMessage());
|
||||
self::alertAdmins("sendConfirmationToUser", $e->getMessage(), $data);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a system alert to ADMIN_EMAILS with error context and message.
|
||||
*
|
||||
* @param string $context
|
||||
* @param string $errorMessage
|
||||
* @param array|string $data Data array or JSON string for the report
|
||||
* @return void
|
||||
*/
|
||||
public static function alertAdmins(string $context, string $errorMessage, $data = []): void
|
||||
{
|
||||
try {
|
||||
$mail = new PHPMailer(true);
|
||||
self::configureMailer($mail);
|
||||
|
||||
$recipients = self::parseRecipients($_ENV['ADMIN_EMAILS'] ?? '');
|
||||
foreach ($recipients as $email) {
|
||||
$mail->addAddress($email);
|
||||
}
|
||||
|
||||
if (empty($mail->getToAddresses())) {
|
||||
Logger::error("EmailHelper: No valid ADMIN_EMAILS configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = "[System Alert] Error in {$context}";
|
||||
$mail->Body = self::buildErrorReportHtml($context, $errorMessage, $data);
|
||||
|
||||
$mail->send();
|
||||
} catch (Exception $e) {
|
||||
Logger::error("EmailHelper::alertAdmins failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
/**
|
||||
* File: HoneypotHelper.php
|
||||
* Version: 1.0
|
||||
* Path: /app/Utilities/HoneypotHelper.php
|
||||
* Purpose: Provides honeypot-based bot protection with JS-injected token verification.
|
||||
* Project: Wizdom Networks Website
|
||||
*/
|
||||
|
||||
namespace WizdomNetworks\WizeWeb\Utilities;
|
||||
|
||||
class HoneypotHelper
|
||||
{
|
||||
const SESSION_KEY = 'wiz_hpt';
|
||||
const FIELD_NAME = 'wiz_hpt';
|
||||
|
||||
/**
|
||||
* Start session if needed and generate a honeypot token.
|
||||
*/
|
||||
public static function generate(): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (!isset($_SESSION[self::SESSION_KEY])) {
|
||||
$_SESSION[self::SESSION_KEY] = bin2hex(random_bytes(16));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current honeypot token from the session.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getToken(): ?string
|
||||
{
|
||||
return $_SESSION[self::SESSION_KEY] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the submitted honeypot token and invalidate it after use.
|
||||
*
|
||||
* @param string|null $submitted
|
||||
* @return bool
|
||||
*/
|
||||
public static function validate(?string $submitted): bool
|
||||
{
|
||||
$expected = $_SESSION[self::SESSION_KEY] ?? null;
|
||||
unset($_SESSION[self::SESSION_KEY]);
|
||||
|
||||
if (!$expected || !$submitted || $submitted !== $expected) {
|
||||
Logger::warning("Honeypot validation failed. Expected: $expected, Got: $submitted");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the HTML for the honeypot field.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function renderField(): string
|
||||
{
|
||||
return sprintf(
|
||||
'<input type="text" name="%s" id="%s" class="form-control" required style="position: absolute; left: -9999px;" tabindex="-1" autocomplete="off">',
|
||||
self::FIELD_NAME,
|
||||
self::FIELD_NAME
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
/**
|
||||
* File: SessionHelper.php
|
||||
* Version: 1.0
|
||||
* Path: /app/Utilities/SessionHelper.php
|
||||
* Purpose: Utility to simplify session handling, especially flash messages.
|
||||
* Project: Wizdom Networks Website
|
||||
*/
|
||||
|
||||
namespace WizdomNetworks\WizeWeb\Utilities;
|
||||
|
||||
class SessionHelper
|
||||
{
|
||||
/**
|
||||
* Start the PHP session if it hasn’t been started yet.
|
||||
*/
|
||||
public static function start(): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_set_cookie_params([
|
||||
'lifetime' => 0,
|
||||
'path' => '/',
|
||||
'domain' => $_SERVER['HTTP_HOST'], // <- ensures subdomain support
|
||||
'secure' => true, // <- required for HTTPS
|
||||
'httponly' => true,
|
||||
'samesite' => 'Lax'
|
||||
]);
|
||||
session_start([
|
||||
'cookie_secure' => true,
|
||||
'cookie_httponly' => true,
|
||||
'cookie_samesite' => 'Lax'
|
||||
]);
|
||||
|
||||
Logger::info("Session started manually via SessionHelper.");
|
||||
} else {
|
||||
Logger::info("Session already active.");
|
||||
}
|
||||
|
||||
Logger::info("Session status: " . session_status());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a session variable.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function set(string $key, $value): void
|
||||
{
|
||||
$_SESSION[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a session variable (does not unset).
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get(string $key, $default = null)
|
||||
{
|
||||
return $_SESSION[$key] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and remove a session flash variable.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function flash(string $key, $default = null)
|
||||
{
|
||||
$value = $_SESSION[$key] ?? $default;
|
||||
unset($_SESSION[$key]);
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a session key is set.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
public static function has(string $key): bool
|
||||
{
|
||||
return isset($_SESSION[$key]);
|
||||
}
|
||||
}
|
||||
111
public/app.log
111
public/app.log
|
|
@ -1,2 +1,113 @@
|
|||
[2025-05-08 00:33:46] [INFO]: Bootstrapping application
|
||||
[2025-05-08 00:33:46] [ERROR]: Route not found: index.php
|
||||
[2025-05-14 17:15:31] [INFO]: Session started manually via SessionHelper.
|
||||
[2025-05-14 17:15:31] [INFO]: Session status: 2
|
||||
[2025-05-14 17:15:31] [INFO]: Bootstrapping application
|
||||
[2025-05-14 17:15:31] [INFO]: Executing controller: WizdomNetworks\WizeWeb\Controllers\LandingController::index
|
||||
[2025-05-14 17:15:31] [INFO]: Session already active.
|
||||
[2025-05-14 17:15:31] [INFO]: Session status: 2
|
||||
[2025-05-14 17:15:31] [INFO]: Session status: 2
|
||||
[2025-05-14 17:15:31] [INFO]: 📥 Landing page session ID: 4s18mr50hk6p8mv7f0kbfntl3c
|
||||
[2025-05-14 17:15:31] [INFO]: 🟡 Landing page session before render: []
|
||||
[2025-05-14 17:15:47] [INFO]: Session started manually via SessionHelper.
|
||||
[2025-05-14 17:15:47] [INFO]: Session status: 2
|
||||
[2025-05-14 17:15:47] [INFO]: Bootstrapping application
|
||||
[2025-05-14 17:15:47] [INFO]: Executing controller: WizdomNetworks\WizeWeb\Controllers\ContactController::submit
|
||||
[2025-05-14 17:15:47] [INFO]: Executing controller: ContactController::submit
|
||||
[2025-05-14 17:15:47] [INFO]: 📦 PHP Session ID: 4s18mr50hk6p8mv7f0kbfntl3c
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: Original: John | Sanitized: John
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: Original: Clement | Sanitized: Clement
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: Original: essae@wizdom.ca | Sanitized: essae@wizdom.ca
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: Original: 4168778483 | Sanitized: 4168778483
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: Original: second new subject | Sanitized: second new subject
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: Original: econd new subject | Sanitized: econd new subject
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: first_name = John
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: last_name = Clement
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: email = essae@wizdom.ca
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: phone = 4168778483
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: subject = second new subject
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: message = econd new subject
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: ip_address = 10.10.3.1
|
||||
[2025-05-14 17:15:47] [INFO]: Sanitized input: user_agent = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
|
||||
[2025-05-14 17:15:47] [INFO]: [DEBUG] Validating email address: essae@wizdom.ca
|
||||
[2025-05-14 17:15:47] [INFO]: Database connection established successfully.
|
||||
[2025-05-14 17:15:47] [INFO]: ✅ Writing session flag: contact_success = true
|
||||
[2025-05-14 17:15:47] [INFO]: ✅ Session content before redirect: {"contact_success":true}
|
||||
[2025-05-14 17:15:47] [INFO]: Session started manually via SessionHelper.
|
||||
[2025-05-14 17:15:47] [INFO]: Session status: 2
|
||||
[2025-05-14 17:15:47] [INFO]: Bootstrapping application
|
||||
[2025-05-14 17:15:47] [INFO]: Executing controller: WizdomNetworks\WizeWeb\Controllers\LandingController::index
|
||||
[2025-05-14 17:15:47] [INFO]: Session already active.
|
||||
[2025-05-14 17:15:47] [INFO]: Session status: 2
|
||||
[2025-05-14 17:15:47] [INFO]: Session status: 2
|
||||
[2025-05-14 17:15:47] [INFO]: 📥 Landing page session ID: 4s18mr50hk6p8mv7f0kbfntl3c
|
||||
[2025-05-14 17:15:47] [INFO]: 🟡 Landing page session before render: []
|
||||
[2025-05-14 17:21:55] [INFO]: Session started manually via SessionHelper.
|
||||
[2025-05-14 17:21:55] [INFO]: Session status: 2
|
||||
[2025-05-14 17:21:55] [INFO]: Bootstrapping application
|
||||
[2025-05-14 17:21:55] [INFO]: Executing controller: WizdomNetworks\WizeWeb\Controllers\LandingController::index
|
||||
[2025-05-14 17:21:55] [INFO]: Session already active.
|
||||
[2025-05-14 17:21:55] [INFO]: Session status: 2
|
||||
[2025-05-14 17:21:55] [INFO]: Session status: 2
|
||||
[2025-05-14 17:21:55] [INFO]: 📥 Landing page session ID: 4s18mr50hk6p8mv7f0kbfntl3c
|
||||
[2025-05-14 17:21:55] [INFO]: 🟡 Landing page session before render: {"contact_success":true}
|
||||
[2025-05-14 17:22:23] [INFO]: Session started manually via SessionHelper.
|
||||
[2025-05-14 17:22:23] [INFO]: Session status: 2
|
||||
[2025-05-14 17:22:23] [INFO]: Bootstrapping application
|
||||
[2025-05-14 17:22:23] [INFO]: Executing controller: WizdomNetworks\WizeWeb\Controllers\LandingController::index
|
||||
[2025-05-14 17:22:23] [INFO]: Session already active.
|
||||
[2025-05-14 17:22:23] [INFO]: Session status: 2
|
||||
[2025-05-14 17:22:23] [INFO]: Session status: 2
|
||||
[2025-05-14 17:22:23] [INFO]: 📥 Landing page session ID: 4s18mr50hk6p8mv7f0kbfntl3c
|
||||
[2025-05-14 17:22:23] [INFO]: 🟡 Landing page session before render: []
|
||||
[2025-05-14 17:22:42] [INFO]: Session started manually via SessionHelper.
|
||||
[2025-05-14 17:22:42] [INFO]: Session status: 2
|
||||
[2025-05-14 17:22:42] [INFO]: Bootstrapping application
|
||||
[2025-05-14 17:22:42] [INFO]: Executing controller: WizdomNetworks\WizeWeb\Controllers\ContactController::submit
|
||||
[2025-05-14 17:22:42] [INFO]: Executing controller: ContactController::submit
|
||||
[2025-05-14 17:22:42] [INFO]: 📦 PHP Session ID: 4s18mr50hk6p8mv7f0kbfntl3c
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: Original: Ben | Sanitized: Ben
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: Original: brown | Sanitized: brown
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: Original: code@cloudiq.ca | Sanitized: code@cloudiq.ca
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: Original: 4168778483 | Sanitized: 4168778483
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: Original: second new subject | Sanitized: second new subject
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: Original: second new subject | Sanitized: second new subject
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: first_name = Ben
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: last_name = brown
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: email = code@cloudiq.ca
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: phone = 4168778483
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: subject = second new subject
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: message = second new subject
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: ip_address = 10.10.3.1
|
||||
[2025-05-14 17:22:42] [INFO]: Sanitized input: user_agent = Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
|
||||
[2025-05-14 17:22:42] [INFO]: [DEBUG] Validating email address: code@cloudiq.ca
|
||||
[2025-05-14 17:22:42] [INFO]: Database connection established successfully.
|
||||
[2025-05-14 17:22:43] [INFO]: ✅ Writing session flag: contact_success = true
|
||||
[2025-05-14 17:22:43] [INFO]: ✅ Session content before redirect: {"contact_success":true}
|
||||
[2025-05-14 17:22:43] [INFO]: Session started manually via SessionHelper.
|
||||
[2025-05-14 17:22:43] [INFO]: Session status: 2
|
||||
[2025-05-14 17:22:43] [INFO]: Bootstrapping application
|
||||
[2025-05-14 17:22:43] [INFO]: Executing controller: WizdomNetworks\WizeWeb\Controllers\LandingController::index
|
||||
[2025-05-14 17:22:43] [INFO]: Session already active.
|
||||
[2025-05-14 17:22:43] [INFO]: Session status: 2
|
||||
[2025-05-14 17:22:43] [INFO]: Session status: 2
|
||||
[2025-05-14 17:22:43] [INFO]: 📥 Landing page session ID: 4s18mr50hk6p8mv7f0kbfntl3c
|
||||
[2025-05-14 17:22:43] [INFO]: 🟡 Landing page session before render: {"contact_success":true}
|
||||
[2025-05-14 17:27:26] [INFO]: Session started manually via SessionHelper.
|
||||
[2025-05-14 17:27:26] [INFO]: Session status: 2
|
||||
[2025-05-14 17:27:26] [INFO]: Bootstrapping application
|
||||
[2025-05-14 17:27:26] [INFO]: Executing controller: WizdomNetworks\WizeWeb\Controllers\LandingController::index
|
||||
[2025-05-14 17:27:26] [INFO]: Session already active.
|
||||
[2025-05-14 17:27:26] [INFO]: Session status: 2
|
||||
[2025-05-14 17:27:26] [INFO]: Session status: 2
|
||||
[2025-05-14 17:27:26] [INFO]: 📥 Landing page session ID: 4s18mr50hk6p8mv7f0kbfntl3c
|
||||
[2025-05-14 17:27:26] [INFO]: 🟡 Landing page session before render: []
|
||||
[2025-05-14 17:31:29] [INFO]: Session started manually via SessionHelper.
|
||||
[2025-05-14 17:31:29] [INFO]: Session status: 2
|
||||
[2025-05-14 17:31:29] [INFO]: Bootstrapping application
|
||||
[2025-05-14 17:31:29] [INFO]: Executing controller: WizdomNetworks\WizeWeb\Controllers\LandingController::index
|
||||
[2025-05-14 17:31:29] [INFO]: Session already active.
|
||||
[2025-05-14 17:31:29] [INFO]: Session status: 2
|
||||
[2025-05-14 17:31:29] [INFO]: Session status: 2
|
||||
[2025-05-14 17:31:29] [INFO]: 📥 Landing page session ID: 4s18mr50hk6p8mv7f0kbfntl3c
|
||||
[2025-05-14 17:31:29] [INFO]: 🟡 Landing page session before render: []
|
||||
|
|
|
|||
|
|
@ -2195,6 +2195,7 @@ section,
|
|||
|
||||
.contact .php-email-form input[type=text],
|
||||
.contact .php-email-form input[type=email],
|
||||
.contact .php-email-form input[type=tel],
|
||||
.contact .php-email-form textarea {
|
||||
font-size: 14px;
|
||||
padding: 10px 15px;
|
||||
|
|
@ -2207,12 +2208,14 @@ section,
|
|||
|
||||
.contact .php-email-form input[type=text]:focus,
|
||||
.contact .php-email-form input[type=email]:focus,
|
||||
.contact .php-email-form input[type=tel]:focus,
|
||||
.contact .php-email-form textarea:focus {
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.contact .php-email-form input[type=text]::placeholder,
|
||||
.contact .php-email-form input[type=email]::placeholder,
|
||||
.contact .php-email-form input[type=tel]::placeholder,
|
||||
.contact .php-email-form textarea::placeholder {
|
||||
color: color-mix(in srgb, var(--default-color), transparent 70%);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,12 @@
|
|||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (params.get('contact_submitted') || params.get('contact_error')) {
|
||||
const contactSection = document.getElementById('contact');
|
||||
if (contactSection) {
|
||||
contactSection.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const form = document.querySelector('.php-email-form');
|
||||
if (!form) return;
|
||||
|
|
|
|||
|
|
@ -1,52 +1,53 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* File: public/index.php
|
||||
* Version: v1.4
|
||||
* Purpose: Application entry point for Arsha one-pager with early session start
|
||||
* Project: Wizdom Networks Website
|
||||
*/
|
||||
|
||||
// Enable strict error reporting
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
// File: public/index.php
|
||||
// Version: v1.3
|
||||
// Purpose: Application entry point with Arsha one-pager routing
|
||||
// Project: Wizdom Networks Website
|
||||
|
||||
// Autoload classes via Composer
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// Load environment variables
|
||||
use Dotenv\Dotenv;
|
||||
use WizdomNetworks\WizeWeb\Core\Router;
|
||||
use WizdomNetworks\WizeWeb\Utilities\Logger;
|
||||
use WizdomNetworks\WizeWeb\Utilities\ErrorHandler;
|
||||
use WizdomNetworks\WizeWeb\Controllers\LandingController;
|
||||
use WizdomNetworks\WizeWeb\Controllers\ContactController; //
|
||||
|
||||
$dotenv = Dotenv::createImmutable(__DIR__ . '/../');
|
||||
$dotenv->load();
|
||||
|
||||
// Start session before any router logic
|
||||
use WizdomNetworks\WizeWeb\Utilities\SessionHelper;
|
||||
SessionHelper::start();
|
||||
|
||||
// Import core system utilities
|
||||
use WizdomNetworks\WizeWeb\Core\Router;
|
||||
use WizdomNetworks\WizeWeb\Utilities\Logger;
|
||||
use WizdomNetworks\WizeWeb\Utilities\ErrorHandler;
|
||||
|
||||
// Import controllers
|
||||
use WizdomNetworks\WizeWeb\Controllers\LandingController;
|
||||
use WizdomNetworks\WizeWeb\Controllers\ContactController;
|
||||
|
||||
// Boot the application
|
||||
Logger::info("Bootstrapping application");
|
||||
|
||||
// Initialize router and define routes
|
||||
$router = new Router();
|
||||
|
||||
// Arsha landing routes
|
||||
// Landing page routes
|
||||
$router->add('', LandingController::class, 'index');
|
||||
$router->add('/', LandingController::class, 'index');
|
||||
$router->add('index.php', LandingController::class, 'index');
|
||||
|
||||
// Contact form
|
||||
$router->add('/contact', ContactController::class, 'index', 'GET'); // Show contact form
|
||||
$router->add('/contact', ContactController::class, 'submit', 'POST'); // Handle submission
|
||||
|
||||
|
||||
// Optional: fallback for /contact without leading slash (rare case)
|
||||
// $router->add('contact', ContactController::class, 'submit', 'POST');
|
||||
|
||||
// Debug block — safe to leave commented
|
||||
/*
|
||||
echo "<pre>";
|
||||
echo "REQUEST_URI: " . $_SERVER['REQUEST_URI'] . "\n";
|
||||
echo "Parsed path: " . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) . "\n";
|
||||
echo "Trimmed: " . trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/') . "\n";
|
||||
echo "</pre>";
|
||||
exit;
|
||||
*/
|
||||
// Contact form routes
|
||||
$router->add('/contact', ContactController::class, 'index', 'GET');
|
||||
$router->add('/contact', ContactController::class, 'submit', 'POST');
|
||||
|
||||
// Dispatch the incoming request
|
||||
$requestedPath = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
$router->dispatch($requestedPath);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
// use WizdomNetworks\WizeWeb\Utilities\SessionHelper;
|
||||
// SessionHelper::start();
|
||||
|
||||
// ============================================
|
||||
// File: arsha.php
|
||||
// Version: 1.2
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@
|
|||
* Usage: Rendered via LandingController::index()
|
||||
* ============================================
|
||||
*/
|
||||
echo "<!-- SESSION: " . json_encode($_SESSION) . " -->";
|
||||
?>
|
||||
|
||||
|
||||
|
||||
<!-- ======= Hero Section ======= -->
|
||||
<section id="hero" class="hero section dark-background">
|
||||
<div class="container">
|
||||
|
|
@ -709,17 +713,43 @@
|
|||
</div>
|
||||
|
||||
<div class="col-lg-7">
|
||||
<form action="/contact" method="POST" class="php-email-form">
|
||||
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
|
||||
$hasSuccess = $_SESSION['contact_success'] ?? false;
|
||||
$hasError = $_SESSION['contact_error'] ?? null;
|
||||
|
||||
unset($_SESSION['contact_success']);
|
||||
unset($_SESSION['contact_error']);
|
||||
|
||||
if ($hasSuccess): ?>
|
||||
<div class="alert alert-success">✅ Thank you! Your message has been received. We'll be in touch shortly.</div>
|
||||
<?php elseif (!empty($hasError)): ?>
|
||||
<div class="alert alert-danger">⚠️ <?= htmlspecialchars($hasError) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<form action="/contact" method="POST" class="needs-validation" novalidate>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label for="first_name" class="pb-2">First Name</label>
|
||||
<input type="text" name="first_name" id="first_name" class="form-control" required>
|
||||
<div class="invalid-feedback">First name is required.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="last_name" class="pb-2">Last Name</label>
|
||||
<input type="text" name="last_name" id="last_name" class="form-control" required>
|
||||
<div class="invalid-feedback">Last name is required.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -727,34 +757,34 @@
|
|||
<div class="col-md-6">
|
||||
<label for="email" class="pb-2">Your Email</label>
|
||||
<input type="email" name="email" id="email" class="form-control" required>
|
||||
<div class="invalid-feedback">Please enter a valid email address.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="phone" class="pb-2">Phone Number <small class="text-muted">(required to qualify submissions)</small></label>
|
||||
<input type="tel" name="phone" id="phone" class="form-control" required>
|
||||
<div class="invalid-feedback">Phone number is required.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="form-group mt-3">
|
||||
<label for="subject">Subject</label>
|
||||
<input
|
||||
type="text"
|
||||
id="subject"
|
||||
name="subject"
|
||||
class="form-control"
|
||||
required
|
||||
placeholder="Subject of your inquiry"
|
||||
>
|
||||
<div class="col-12">
|
||||
<label for="subject" class="pb-2">Subject</label>
|
||||
<input type="text" name="subject" id="subject" class="form-control" required placeholder="Subject of your inquiry">
|
||||
<div class="invalid-feedback">Subject is required.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<label for="message" class="pb-2">Message</label>
|
||||
<textarea name="message" id="message" rows="5" class="form-control" required></textarea>
|
||||
<div class="invalid-feedback">Please enter your message.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<button type="submit">Send Message</button>
|
||||
<button type="submit" class="btn btn-primary">Send Message</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
|
@ -902,3 +932,18 @@
|
|||
|
||||
</footer><!-- End Footer -->
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
'use strict'
|
||||
const forms = document.querySelectorAll('.needs-validation')
|
||||
Array.from(forms).forEach(form => {
|
||||
form.addEventListener('submit', event => {
|
||||
if (!form.checkValidity()) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}
|
||||
form.classList.add('was-validated')
|
||||
}, false)
|
||||
})
|
||||
})()
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue