diff --git a/admin_platform_users.php b/admin_platform_users.php index 1026284..ff18382 100644 --- a/admin_platform_users.php +++ b/admin_platform_users.php @@ -12,7 +12,8 @@ if (!isset($_SESSION['user_id']) || ($_SESSION['user_role'] ?? '') !== 'admin') // Check permission if (!has_permission('manage_platform_users')) { render_header(t('nav_platform_users'), 'platform_users'); - echo '
Access Denied. You do not have permission to manage platform users.
'; + $uid = $_SESSION['user_id'] ?? 'unknown'; + echo '
Access Denied. You do not have permission to manage platform users. (Error Code: 403-U' . $uid . ')
'; render_footer(); exit; } diff --git a/login.php b/login.php index ce01fb0..36caeba 100644 --- a/login.php +++ b/login.php @@ -75,7 +75,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { render_header('Login / Reset Password', 'login', false, false); ?> -
+
diff --git a/mail/MailService.php b/mail/MailService.php index d801067..58518d2 100644 --- a/mail/MailService.php +++ b/mail/MailService.php @@ -7,6 +7,11 @@ // // Contact form helper: // MailService::sendContactMessage($name, $email, $message, $to = null, $subject = 'New contact form'); +// Ensure DB config is loaded for settings +if (!function_exists('db') && file_exists(__DIR__ . '/../db/config.php')) { + require_once __DIR__ . '/../db/config.php'; +} + class MailService { // Universal mail sender (no attachments by design) @@ -18,26 +23,26 @@ class MailService if (file_exists($autoload)) { require_once $autoload; } - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { + if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { @require_once 'libphp-phpmailer/autoload.php'; - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { + if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { @require_once 'libphp-phpmailer/src/Exception.php'; @require_once 'libphp-phpmailer/src/SMTP.php'; @require_once 'libphp-phpmailer/src/PHPMailer.php'; } - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { + if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { @require_once 'PHPMailer/src/Exception.php'; @require_once 'PHPMailer/src/SMTP.php'; @require_once 'PHPMailer/src/PHPMailer.php'; } - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { + if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { @require_once 'PHPMailer/Exception.php'; @require_once 'PHPMailer/SMTP.php'; @require_once 'PHPMailer/PHPMailer.php'; } } - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { + if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { return [ 'success' => false, 'error' => 'PHPMailer not available' ]; } @@ -91,14 +96,18 @@ class MailService $mail->isHTML(true); $mail->Subject = $subject; - $mail->Body = $htmlBody; + + // Apply standardized HTML wrapper + $mail->Body = self::wrapHtml($htmlBody, $subject); $mail->AltBody = $textBody ?? strip_tags($htmlBody); + $ok = $mail->send(); return [ 'success' => $ok ]; - } catch (\Throwable $e) { + } catch (Throwable $e) { return [ 'success' => false, 'error' => 'PHPMailer error: ' . $e->getMessage() ]; } } + private static function loadConfig(): array { $configPath = __DIR__ . '/config.php'; @@ -124,21 +133,21 @@ class MailService require_once $autoload; } // Fallback to system-wide PHPMailer (installed via apt: libphp-phpmailer) - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { + if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { // Debian/Ubuntu package layout (libphp-phpmailer) @require_once 'libphp-phpmailer/autoload.php'; - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { + if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { @require_once 'libphp-phpmailer/src/Exception.php'; @require_once 'libphp-phpmailer/src/SMTP.php'; @require_once 'libphp-phpmailer/src/PHPMailer.php'; } // Alternative layout (older PHPMailer package names) - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { + if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { @require_once 'PHPMailer/src/Exception.php'; @require_once 'PHPMailer/src/SMTP.php'; @require_once 'PHPMailer/src/PHPMailer.php'; } - if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { + if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { @require_once 'PHPMailer/Exception.php'; @require_once 'PHPMailer/SMTP.php'; @require_once 'PHPMailer/PHPMailer.php'; @@ -146,7 +155,7 @@ class MailService } $transport = $cfg['transport'] ?? 'smtp'; - if ($transport === 'smtp' && class_exists('PHPMailer\\PHPMailer\\PHPMailer')) { + if ($transport === 'smtp' && class_exists('PHPMailer\PHPMailer\PHPMailer')) { return self::sendViaPHPMailer($cfg, $name, $email, $message, $to, $subject); } @@ -216,12 +225,16 @@ class MailService $safeName = htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); $safeEmail = htmlspecialchars($email, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); $safeBody = nl2br(htmlspecialchars($body, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')); - $mail->Body = "

Name: {$safeName}

Email: {$safeEmail}


{$safeBody}"; + + $innerHtml = "

Name: {$safeName}

Email: {$safeEmail}


{$safeBody}"; + + // Apply standardized HTML wrapper + $mail->Body = self::wrapHtml($innerHtml, $subject); $mail->AltBody = "Name: {$name}\nEmail: {$email}\n\n{$body}"; $ok = $mail->send(); return [ 'success' => $ok ]; - } catch (\Throwable $e) { + } catch (Throwable $e) { return [ 'success' => false, 'error' => 'PHPMailer error: ' . $e->getMessage() ]; } } @@ -229,7 +242,108 @@ class MailService private static function sendViaNativeMail(array $cfg, string $name, string $email, string $body, $to, string $subject) { $opts = ['reply_to' => $email]; - $html = nl2br(htmlspecialchars($body, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')); - return self::sendMail($to, $subject, $html, $body, $opts); + $safeName = htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + $safeEmail = htmlspecialchars($email, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + $safeBody = nl2br(htmlspecialchars($body, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')); + + $innerHtml = "

Name: {$safeName}

Email: {$safeEmail}


{$safeBody}"; + + // sendMail will wrap the HTML now + return self::sendMail($to, $subject, $innerHtml, $body, $opts); + } + + private static function getSetting(string $key, string $default = ''): string + { + // Use global get_setting if available (requires includes/app.php) + if (function_exists('get_setting')) { + return get_setting($key, $default); + } + + // Fallback: Direct DB query if db() helper is available + if (function_exists('db')) { + try { + $stmt = db()->prepare("SELECT setting_value FROM settings WHERE setting_key = ? LIMIT 1"); + $stmt->execute([$key]); + $val = $stmt->fetchColumn(); + return $val !== false ? (string)$val : $default; + } catch (Throwable $e) { + return $default; + } + } + + return $default; + } + + private static function wrapHtml(string $content, string $subject): string + { + $companyName = self::getSetting('company_name', 'CargoLink'); + $logoPath = self::getSetting('logo_path', ''); + $companyAddress = self::getSetting('company_address', ''); + + // Build absolute URL for logo + $logoUrl = ''; + if ($logoPath) { + $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https' : 'http'; + $host = $_SERVER['HTTP_HOST'] ?? 'localhost'; + // Assuming logo_path is relative to public root (e.g., uploads/logos/...) + $logoUrl = "{$scheme}://{$host}/" . ltrim($logoPath, '/'); + } + + $year = date('Y'); + $footerInfo = []; + if ($companyAddress) { + $footerInfo[] = nl2br(htmlspecialchars($companyAddress)); + } + $footerHtml = implode('
', $footerInfo); + + $logoHtml = ''; + if ($logoUrl) { + $logoHtml = "
\"{$companyName}\"
"; + } + + return << + + + + + {$subject} + + + +
+
+ +
+ {$logoHtml} +

{$companyName}

+
+ + +
+ {$content} +
+ + + +
+
+ + +HTML; } }