mirror of
https://github.com/xodivorce/mailer-dev-console.git
synced 2025-12-19 23:59:34 +05:30
(chore): initial MVP release
This commit is contained in:
BIN
htdocs/app/.DS_Store
vendored
Normal file
BIN
htdocs/app/.DS_Store
vendored
Normal file
Binary file not shown.
108
htdocs/app/mail/mailer.php
Normal file
108
htdocs/app/mail/mailer.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
require_once __DIR__ . '/../../config/env.php';
|
||||
require_once __DIR__ . '/templates/loader.php';
|
||||
|
||||
/**
|
||||
* Build and optionally send mail from a file-based template.
|
||||
*/
|
||||
function sendMailFromTemplate(string $templateKey, bool $previewOnly = false): bool
|
||||
{
|
||||
$mail = new PHPMailer(true);
|
||||
|
||||
$email = $_ENV['MAIL_TO'];
|
||||
$username = $_ENV['USERNAME'];
|
||||
|
||||
try {
|
||||
$mail->isSMTP();
|
||||
$mail->Host = $_ENV['MAIL_HOST'];
|
||||
$mail->SMTPAuth = true;
|
||||
$mail->Username = $_ENV['MAIL_USERNAME'];
|
||||
$mail->Password = $_ENV['MAIL_PASSWORD'];
|
||||
$mail->SMTPSecure = $_ENV['MAIL_ENCRYPTION'];
|
||||
$mail->Port = (int) $_ENV['MAIL_PORT'];
|
||||
|
||||
$mail->setFrom($_ENV['MAIL_FROM'], $_ENV['MAIL_FROM_NAME']);
|
||||
$mail->addAddress($email, $username);
|
||||
|
||||
$domain = $_ENV['DOMAIN'] ?? 'UNKNOWN DOMAIN';
|
||||
$company = $_ENV['COMPANY_NAME'] ?? 'UNKNOWN COMPANY';
|
||||
$github = $_ENV['GITHUB'] ?? 'UNKNOWN GITHUB';
|
||||
$website = $_ENV['WEBSITE'] ?? 'UNKNOWN WEBSITE';
|
||||
$projectStartYear = $_ENV['PROJECT_START_YEAR'] ?? 2025;
|
||||
$currentYear = (int) date('Y');
|
||||
$startYear = min((int) $projectStartYear, $currentYear);
|
||||
$yearText = ($currentYear > $startYear)
|
||||
? $startYear . '-' . substr((string) $currentYear, -2)
|
||||
: $startYear;
|
||||
|
||||
// context passed to template files
|
||||
$ctx = [
|
||||
'email' => $email,
|
||||
'username' => $username,
|
||||
'domain' => $domain,
|
||||
'company' => $company,
|
||||
'github' => $github,
|
||||
'website' => $website,
|
||||
'yearText' => $yearText,
|
||||
];
|
||||
|
||||
// 🔥 load subject + body from the selected template file
|
||||
$tpl = loadMailTemplate($templateKey, $ctx);
|
||||
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->Encoding = 'base64';
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = $tpl['subject'];
|
||||
$mail->Body = $tpl['body'];
|
||||
|
||||
// expose everything for preview
|
||||
$GLOBALS['MAIL_PREVIEW_SUBJECT'] = $mail->Subject;
|
||||
$GLOBALS['MAIL_PREVIEW_BODY'] = $mail->Body;
|
||||
$GLOBALS['MAIL_PREVIEW_TO'] = $email;
|
||||
$GLOBALS['MAIL_PREVIEW_USERNAME'] = $username;
|
||||
$GLOBALS['MAIL_PREVIEW_DOMAIN'] = $domain;
|
||||
$GLOBALS['MAIL_PREVIEW_COMPANY'] = $company;
|
||||
$GLOBALS['MAIL_PREVIEW_FROM'] = $_ENV['MAIL_FROM'];
|
||||
$GLOBALS['MAIL_PREVIEW_FROM_NAME'] = $_ENV['MAIL_FROM_NAME'];
|
||||
|
||||
$GLOBALS['MAIL_PREVIEW_MAIL_HOST'] = $_ENV['MAIL_HOST'] ?? null;
|
||||
$GLOBALS['MAIL_PREVIEW_MAIL_USERNAME'] = $_ENV['MAIL_USERNAME'] ?? null;
|
||||
$GLOBALS['MAIL_PREVIEW_MAIL_PORT'] = $_ENV['MAIL_PORT'] ?? null;
|
||||
$GLOBALS['MAIL_PREVIEW_MAIL_ENCRYPTION'] = $_ENV['MAIL_ENCRYPTION'] ?? null;
|
||||
$GLOBALS['MAIL_PREVIEW_PROJECT_START'] = $startYear;
|
||||
$GLOBALS['MAIL_PREVIEW_CURRENT_YEAR'] = $currentYear;
|
||||
$GLOBALS['MAIL_PREVIEW_MAIL_PASSWORD'] = empty($_ENV['MAIL_PASSWORD']) ? 'not set' : '•••• (configured)';
|
||||
|
||||
// template info for UI
|
||||
$GLOBALS['MAIL_PREVIEW_TEMPLATE_KEY'] = $tpl['key'];
|
||||
$GLOBALS['MAIL_PREVIEW_TEMPLATE_LABEL'] = $tpl['label'];
|
||||
$GLOBALS['MAIL_PREVIEW_TEMPLATES_INDEX'] = getMailTemplatesIndex($ctx);
|
||||
|
||||
if ($previewOnly) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$mail->send();
|
||||
return true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('Mailer Error: ' . $mail->ErrorInfo);
|
||||
return false;
|
||||
} catch (\Throwable $e) {
|
||||
error_log('Mailer Exception: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Backwards-compatible wrapper.
|
||||
* Default template: 'quary'
|
||||
*/
|
||||
function sendAccountCreationMail(bool $previewOnly = false, string $templateKey = 'quary'): bool
|
||||
{
|
||||
return sendMailFromTemplate($templateKey, $previewOnly);
|
||||
}
|
||||
108
htdocs/app/mail/templates/approved-template.php
Normal file
108
htdocs/app/mail/templates/approved-template.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
$email = $ctx['email'] ?? '';
|
||||
$username = $ctx['username'] ?? '';
|
||||
$domain = $ctx['domain'] ?? '';
|
||||
$company = $ctx['company'] ?? '';
|
||||
$github = $ctx['github'] ?? '';
|
||||
$website = $ctx['website'] ?? '';
|
||||
$yearText = $ctx['yearText'] ?? '';
|
||||
|
||||
$subject = "{$domain} - Query Response #10";
|
||||
|
||||
$body = <<<HTML
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@300;400;600;700&display=swap" rel="stylesheet" />
|
||||
<style type="text/css">
|
||||
@import url('https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@300;400;600;700&display=swap');
|
||||
.body-font { font-family: 'Lexend Deca', -apple-system !important; }
|
||||
.heading-font { font-family: 'Lexend Deca', -apple-system !important; font-weight: 600; }
|
||||
a { color: #1d4ed8; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="body-font" style="margin:0;padding:0;background-color:#ffffff;color:#0f172a;font-family:'Lexend Deca', -apple-system;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="background:#ffffff;padding:24px 12px;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="640" style="max-width:640px;width:100%;border-collapse:collapse;text-align:left;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td style="padding-top:28px;padding-bottom:8px;text-align:center;">
|
||||
<h1 class="heading-font" style="font-size:28px;line-height:34px;margin:0;font-weight:600;color:#0f172a;text-align:center;font-family:'Lexend Deca', -apple-system;">
|
||||
Query Response #10
|
||||
</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:18px 16px 8px 16px;background:#ffffff;font-family:'Lexend Deca', -apple-system;">
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
Hi <strong style="color:#0f172a;">{$username}</strong>,
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
<strong style="color:#0f172a;">{$domain}</strong> had received a query about (the things..).
|
||||
We're pleased to announce you that your <strong style="color:#0f172a;">{$domain}</strong>
|
||||
query has been approved. Do you have any other questions or suggestions? We're always here to help.
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
We reviewed your recommendations and we want to thank you for your input.
|
||||
We're a small, quiet corner of the internet, did you want to join us to grow it further?
|
||||
|
||||
Send your github username via reply to this email and we will get back to you;
|
||||
After accepting the invitation, you can start contributing to the project.
|
||||
</p>
|
||||
<div style="height:1px;background:#e6e9ef;margin:20px 0;"></div>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
This query was received by the email address:
|
||||
<a href="mailto:{$email}" style="color:#0f172a;font-family:'Lexend Deca', -apple-system;">{$email}</a>.
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system, BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;">
|
||||
🪢 <strong style="color:#0f172a;">GitHub:</strong> {$github}<br>
|
||||
</p>
|
||||
<div style="height:1px;background:#e6e9ef;margin:20px 0;"></div>
|
||||
<p style="font-size:14px;line-height:20px;color:#6b7280;margin:0 0 12px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
If you did not make this query, or believe we contacted the wrong person, kidnly ignore this email.
|
||||
However, if you have any new queries, requests, or wish to contribute, feel free to reach out to us at this email address.
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:18px;color:#475569;margin:18px 0 0 0;font-family:'Lexend Deca', -apple-system;">
|
||||
Sincerely,
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:18px;color:#0f172a;font-weight:600;margin:6px 0 0 0;font-family:'Lexend Deca', -apple-system;">
|
||||
{$domain} Support
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:14px 12px 28px 12px;font-family:'Lexend Deca', -apple-system;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="background:#f8fafc;padding:12px;border-radius:6px;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td style="text-align:center;font-size:12px;color:#6b7280;line-height:20px;font-family:'Lexend Deca', -apple-system;">
|
||||
<div style="margin-bottom:4px;font-family:'Lexend Deca', -apple-system;">
|
||||
<a href="https://{$domain}.in" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">{$domain}</a> |
|
||||
<a href="https://{$domain}.in/support" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">Support</a> |
|
||||
<a href="https://{$domain}.in/privacy-policy" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">Privacy Policy</a>
|
||||
</div>
|
||||
<div style="font-size:12px;color:#475569;font-family:'Lexend Deca', -apple-system;">
|
||||
A small community under {$company}.<br>
|
||||
© {$yearText} {$company}. All rights reserved.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
|
||||
return [
|
||||
'label' => "{$domain} Approved Temp.",
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
];
|
||||
102
htdocs/app/mail/templates/declined-template.php
Normal file
102
htdocs/app/mail/templates/declined-template.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
$email = $ctx['email'] ?? '';
|
||||
$username = $ctx['username'] ?? '';
|
||||
$domain = $ctx['domain'] ?? '';
|
||||
$company = $ctx['company'] ?? '';
|
||||
$github = $ctx['github'] ?? '';
|
||||
$website = $ctx['website'] ?? '';
|
||||
$yearText = $ctx['yearText'] ?? '';
|
||||
|
||||
$subject = "{$domain} - Query Response #11";
|
||||
|
||||
$body = <<<HTML
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@300;400;600;700&display=swap" rel="stylesheet" />
|
||||
<style type="text/css">
|
||||
@import url('https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@300;400;600;700&display=swap');
|
||||
.body-font { font-family: 'Lexend Deca', -apple-system !important; }
|
||||
.heading-font { font-family: 'Lexend Deca', -apple-system !important; font-weight: 600; }
|
||||
a { color: #1d4ed8; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="body-font" style="margin:0;padding:0;background-color:#ffffff;color:#0f172a;font-family:'Lexend Deca', -apple-system;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="background:#ffffff;padding:24px 12px;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="640" style="max-width:640px;width:100%;border-collapse:collapse;text-align:left;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td style="padding-top:28px;padding-bottom:8px;text-align:center;">
|
||||
<h1 class="heading-font" style="font-size:28px;line-height:34px;margin:0;font-weight:600;color:#0f172a;text-align:center;font-family:'Lexend Deca', -apple-system;">
|
||||
Query Response #11
|
||||
</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:18px 16px 8px 16px;background:#ffffff;font-family:'Lexend Deca', -apple-system;">
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
Hi <strong style="color:#0f172a;">{$username}</strong>,
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
<strong style="color:#0f172a;">{$domain}</strong> had received a query about (the things..).
|
||||
We're so sorry to announce you that your <strong style="color:#0f172a;">{$domain}</strong>
|
||||
query has been declined. Do you have any other questions or suggestions? We're always here to help.
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
We reviewed your recommendations and we appreciate your valuable input;
|
||||
but unfortunately, we are unable to implement your suggestions at this time.
|
||||
</p>
|
||||
<div style="height:1px;background:#e6e9ef;margin:20px 0;"></div>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
This query was received by the email address:
|
||||
<a href="mailto:{$email}" style="color:#0f172a;font-family:'Lexend Deca', -apple-system;">{$email}</a>.
|
||||
</p>
|
||||
<div style="height:1px;background:#e6e9ef;margin:20px 0;"></div>
|
||||
<p style="font-size:14px;line-height:20px;color:#6b7280;margin:0 0 12px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
If you did not make this query, or believe we contacted the wrong person, kidnly ignore this email.
|
||||
However, if you have any new queries, requests, or wish to contribute, feel free to reach out to us at this email address.
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:18px;color:#475569;margin:18px 0 0 0;font-family:'Lexend Deca', -apple-system;">
|
||||
Sincerely,
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:18px;color:#0f172a;font-weight:600;margin:6px 0 0 0;font-family:'Lexend Deca', -apple-system;">
|
||||
{$domain} Support
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:14px 12px 28px 12px;font-family:'Lexend Deca', -apple-system;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="background:#f8fafc;padding:12px;border-radius:6px;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td style="text-align:center;font-size:12px;color:#6b7280;line-height:20px;font-family:'Lexend Deca', -apple-system;">
|
||||
<div style="margin-bottom:4px;font-family:'Lexend Deca', -apple-system;">
|
||||
<a href="https://{$domain}.in" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">{$domain}</a> |
|
||||
<a href="https://{$domain}.in/support" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">Support</a> |
|
||||
<a href="https://{$domain}.in/privacy-policy" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">Privacy Policy</a>
|
||||
</div>
|
||||
<div style="font-size:12px;color:#475569;font-family:'Lexend Deca', -apple-system;">
|
||||
A small community under {$company}.<br>
|
||||
© {$yearText} {$company}. All rights reserved.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
|
||||
return [
|
||||
'label' => "{$domain} Declined Temp.",
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
];
|
||||
89
htdocs/app/mail/templates/loader.php
Normal file
89
htdocs/app/mail/templates/loader.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
// app/mail/templates/loader.php
|
||||
|
||||
/**
|
||||
* Static map: do NOT use $domain here (no ctx available yet)
|
||||
*/
|
||||
function mailTemplateMap(): array
|
||||
{
|
||||
return [
|
||||
'quary' => [
|
||||
'file' => __DIR__ . '/query-template.php',
|
||||
],
|
||||
'thanks' => [
|
||||
'file' => __DIR__ . '/thanks-template.php',
|
||||
],
|
||||
'approve' => [
|
||||
'file' => __DIR__ . '/approved-template.php',
|
||||
],
|
||||
'decline' => [
|
||||
'file' => __DIR__ . '/declined-template.php',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a single template by key.
|
||||
*/
|
||||
function loadMailTemplate(string $key, array $ctx): array
|
||||
{
|
||||
$map = mailTemplateMap();
|
||||
|
||||
if (!isset($map[$key])) {
|
||||
throw new RuntimeException("Unknown mail template key: {$key}");
|
||||
}
|
||||
|
||||
$file = $map[$key]['file'];
|
||||
$label = $map[$key]['label'];
|
||||
|
||||
// Make $ctx visible inside template file
|
||||
$template = require $file;
|
||||
|
||||
if (!is_array($template)) {
|
||||
throw new RuntimeException("Template file {$file} did not return array");
|
||||
}
|
||||
|
||||
// attach meta
|
||||
$template['key'] = $key;
|
||||
$template['label'] = $template['label'] ?? $label;
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build index for dropdown (dynamic domain support)
|
||||
*/
|
||||
function getMailTemplatesIndex(array $ctx): array
|
||||
{
|
||||
$map = mailTemplateMap();
|
||||
$index = [];
|
||||
$domain = $ctx['domain'] ?? '';
|
||||
|
||||
foreach ($map as $key => $meta) {
|
||||
|
||||
$label = $meta['label'];
|
||||
|
||||
if ($domain) {
|
||||
if ($key === 'quary') {
|
||||
$label = "{$domain} Query Response";
|
||||
}
|
||||
if ($key === 'thanks') {
|
||||
$label = "{$domain} Thanks Reaching";
|
||||
}
|
||||
if ($key === 'approve') {
|
||||
$label = "{$domain} Approved Temp.";
|
||||
}
|
||||
if ($key === 'decline') {
|
||||
$label = "{$domain} Declined Temp.";
|
||||
}
|
||||
}
|
||||
|
||||
$index[] = [
|
||||
'key' => $key,
|
||||
'label' => $label,
|
||||
];
|
||||
}
|
||||
|
||||
return $index;
|
||||
}
|
||||
|
||||
107
htdocs/app/mail/templates/query-template.php
Normal file
107
htdocs/app/mail/templates/query-template.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
$email = $ctx['email'] ?? '';
|
||||
$username = $ctx['username'] ?? '';
|
||||
$domain = $ctx['domain'] ?? '';
|
||||
$company = $ctx['company'] ?? '';
|
||||
$github = $ctx['github'] ?? '';
|
||||
$website = $ctx['website'] ?? '';
|
||||
$yearText = $ctx['yearText'] ?? '';
|
||||
|
||||
$subject = "{$domain} - Query Response #09";
|
||||
|
||||
$body = <<<HTML
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@300;400;600;700&display=swap" rel="stylesheet" />
|
||||
<style type="text/css">
|
||||
@import url('https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@300;400;600;700&display=swap');
|
||||
.body-font { font-family: 'Lexend Deca', -apple-system !important; }
|
||||
.heading-font { font-family: 'Lexend Deca', -apple-system !important; font-weight: 600; }
|
||||
a { color: #1d4ed8; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="body-font" style="margin:0;padding:0;background-color:#ffffff;color:#0f172a;font-family:'Lexend Deca', -apple-system;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="background:#ffffff;padding:24px 12px;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="640" style="max-width:640px;width:100%;border-collapse:collapse;text-align:left;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td style="padding-top:28px;padding-bottom:8px;text-align:center;">
|
||||
<h1 class="heading-font" style="font-size:28px;line-height:34px;margin:0;font-weight:600;color:#0f172a;text-align:center;font-family:'Lexend Deca', -apple-system;">
|
||||
Query Response #09
|
||||
</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:18px 16px 8px 16px;background:#ffffff;font-family:'Lexend Deca', -apple-system;">
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
Hi <strong style="color:#0f172a;">{$username}</strong>,
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
<strong style="color:#0f172a;">{$domain}</strong> had received a query about createing the authentication system on the platfrom.
|
||||
We're pleased to announce you that <strong style="color:#0f172a;">{$domain}</strong>
|
||||
AUTH system has been successfully created. i18n isn’t available yet for everywhere -so if you're comfortable with English, everything should work fine for now.
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
We’ve added a few resources, such as our website and GitHub, to help you explore the AUTH system.
|
||||
We're a small, quiet corner of the internet built by a few of us who still believe the
|
||||
web can be yours again.
|
||||
</p>
|
||||
<div style="height:1px;background:#e6e9ef;margin:20px 0;"></div>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
This query was received for the email address:
|
||||
<a href="mailto:{$email}" style="color:#0f172a;font-family:'Lexend Deca', -apple-system;">{$email}</a>.
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system, BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;">
|
||||
🪢 <strong style="color:#0f172a;">GitHub:</strong> {$github}<br>
|
||||
♻️ <strong style="color:#0f172a;">Website:</strong> {$website}
|
||||
</p>
|
||||
<div style="height:1px;background:#e6e9ef;margin:20px 0;"></div>
|
||||
<p style="font-size:14px;line-height:20px;color:#6b7280;margin:0 0 12px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
If you did not make this query, or believe we contacted the wrong person, kindly ignore this email.
|
||||
However, if you have any new queries, requests, or wish to contribute, feel free to reach out to us at this email address.
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:18px;color:#475569;margin:18px 0 0 0;font-family:'Lexend Deca', -apple-system;">
|
||||
Sincerely,
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:18px;color:#0f172a;font-weight:600;margin:6px 0 0 0;font-family:'Lexend Deca', -apple-system;">
|
||||
{$domain} Support
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:14px 12px 28px 12px;font-family:'Lexend Deca', -apple-system;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="background:#f8fafc;padding:12px;border-radius:6px;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td style="text-align:center;font-size:12px;color:#6b7280;line-height:20px;font-family:'Lexend Deca', -apple-system;">
|
||||
<div style="margin-bottom:4px;font-family:'Lexend Deca', -apple-system;">
|
||||
<a href="https://{$domain}.in" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">{$domain}</a> |
|
||||
<a href="https://{$domain}.in/support" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">Support</a> |
|
||||
<a href="https://{$domain}.in/privacy-policy" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">Privacy Policy</a>
|
||||
</div>
|
||||
<div style="font-size:12px;color:#475569;font-family:'Lexend Deca', -apple-system;">
|
||||
A small community under {$company}.<br>
|
||||
© {$yearText} {$company}. All rights reserved.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
|
||||
return [
|
||||
'label' => "{$domain} Query Response",
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
];
|
||||
103
htdocs/app/mail/templates/thanks-template.php
Normal file
103
htdocs/app/mail/templates/thanks-template.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
$email = $ctx['email'] ?? '';
|
||||
$username = $ctx['username'] ?? '';
|
||||
$domain = $ctx['domain'] ?? '';
|
||||
$company = $ctx['company'] ?? '';
|
||||
$github = $ctx['github'] ?? '';
|
||||
$website = $ctx['website'] ?? '';
|
||||
$yearText = $ctx['yearText'] ?? '';
|
||||
|
||||
$subject = "{$domain} - Thanks For Reaching Out";
|
||||
|
||||
$body = <<<HTML
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@300;400;600;700&display=swap" rel="stylesheet" />
|
||||
<style type="text/css">
|
||||
@import url('https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@300;400;600;700&display=swap');
|
||||
.body-font { font-family: 'Lexend Deca', -apple-system !important; }
|
||||
.heading-font { font-family: 'Lexend Deca', -apple-system !important; font-weight: 600; }
|
||||
a { color: #1d4ed8; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="body-font" style="margin:0;padding:0;background-color:#ffffff;color:#0f172a;font-family:'Lexend Deca', -apple-system;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="background:#ffffff;padding:24px 12px;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="640" style="max-width:640px;width:100%;border-collapse:collapse;text-align:left;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td style="padding-top:28px;padding-bottom:8px;text-align:center;">
|
||||
<h1 class="heading-font" style="font-size:28px;line-height:34px;margin:0;font-weight:600;color:#0f172a;text-align:center;font-family:'Lexend Deca', -apple-system;">
|
||||
Thanks For Reaching Out
|
||||
</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:18px 16px 8px 16px;background:#ffffff;font-family:'Lexend Deca', -apple-system;">
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
Hi <strong style="color:#0f172a;">{$username}</strong>,
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
<strong style="color:#0f172a;">{$domain}</strong> has received the query and our team has started reviewing the details you’ve shared.
|
||||
We're pleased to thank you for spending your precious time with <strong style="color:#0f172a;">{$domain}</strong>
|
||||
. i18n support isn’t available in all regions yet, so we’re sorry if you experienced any issues.
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
We’ll review your recommendation and let you know whether we can use it or not.
|
||||
We're a small, quiet corner of the internet built by a few of us who still believe the
|
||||
web can be yours again.
|
||||
</p>
|
||||
<div style="height:1px;background:#e6e9ef;margin:20px 0;"></div>
|
||||
<p style="font-size:16px;line-height:22px;color:#475569;margin:0 0 16px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
This query was received by the email address:
|
||||
<a href="mailto:{$email}" style="color:#0f172a;font-family:'Lexend Deca', -apple-system;">{$email}</a>.
|
||||
</p>
|
||||
<div style="height:1px;background:#e6e9ef;margin:20px 0;"></div>
|
||||
<p style="font-size:14px;line-height:20px;color:#6b7280;margin:0 0 12px 0;font-family:'Lexend Deca', -apple-system;">
|
||||
If you did not make this query, or believe we contacted the wrong person, kidnly ignore this email.
|
||||
However, if you have any new quaries, requests, or wish to contribute, feel free to reach out to us at this email address.
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:18px;color:#475569;margin:18px 0 0 0;font-family:'Lexend Deca', -apple-system;">
|
||||
Sincerely,
|
||||
</p>
|
||||
<p style="font-size:16px;line-height:18px;color:#0f172a;font-weight:600;margin:6px 0 0 0;font-family:'Lexend Deca', -apple-system;">
|
||||
{$domain} Support
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:14px 12px 28px 12px;font-family:'Lexend Deca', -apple-system;">
|
||||
<table role="presentation" cellpadding="0" cellspacing="0" width="100%" style="background:#f8fafc;padding:12px;border-radius:6px;font-family:'Lexend Deca', -apple-system;">
|
||||
<tr>
|
||||
<td style="text-align:center;font-size:12px;color:#6b7280;line-height:20px;font-family:'Lexend Deca', -apple-system;">
|
||||
<div style="margin-bottom:4px;font-family:'Lexend Deca', -apple-system;">
|
||||
<a href="https://{$domain}.in" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">{$domain}</a> |
|
||||
<a href="https://{$domain}.in/support" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">Support</a> |
|
||||
<a href="https://{$domain}.in/privacy-policy" style="color:inherit;text-decoration:none;font-family:'Lexend Deca', -apple-system;">Privacy Policy</a>
|
||||
</div>
|
||||
<div style="font-size:12px;color:#475569;font-family:'Lexend Deca', -apple-system;">
|
||||
A small community under {$company}.<br>
|
||||
© {$yearText} {$company}. All rights reserved.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
HTML;
|
||||
|
||||
return [
|
||||
'label' => "{$domain} Thanks Reaching",
|
||||
'subject' => $subject,
|
||||
'body' => $body,
|
||||
];
|
||||
538
htdocs/app/preview/outbox.php
Normal file
538
htdocs/app/preview/outbox.php
Normal file
@@ -0,0 +1,538 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../mail/mailer.php';
|
||||
require_once __DIR__ . '/../mail/templates/loader.php';
|
||||
|
||||
$selectedTemplate = $_POST['tpl'] ?? $_GET['tpl'] ?? 'quary';
|
||||
|
||||
$validTemplates = array_keys(mailTemplateMap());
|
||||
if (!in_array($selectedTemplate, $validTemplates, true)) {
|
||||
$selectedTemplate = 'quary';
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$sent = sendAccountCreationMail(false, $selectedTemplate);
|
||||
$code = $sent ? 'ok' : 'fail';
|
||||
header('Location: ' . $_SERVER['PHP_SELF'] . '?status=' . $code . '&tpl=' . urlencode($selectedTemplate));
|
||||
exit;
|
||||
}
|
||||
|
||||
sendAccountCreationMail(true, $selectedTemplate);
|
||||
|
||||
$subject = $GLOBALS['MAIL_PREVIEW_SUBJECT'] ?? '(no subject)';
|
||||
$body = $GLOBALS['MAIL_PREVIEW_BODY'] ?? '';
|
||||
$to = $GLOBALS['MAIL_PREVIEW_TO'] ?? '';
|
||||
$username = $GLOBALS['MAIL_PREVIEW_USERNAME'] ?? '';
|
||||
$domain = $GLOBALS['MAIL_PREVIEW_DOMAIN'] ?? '';
|
||||
$company = $GLOBALS['MAIL_PREVIEW_COMPANY'] ?? '';
|
||||
$github = $GLOBALS['MAIL_PREVIEW_GITHUB'] ?? '';
|
||||
$website = $GLOBALS['MAIL_PREVIEW_WEBSITE'] ?? '';
|
||||
$fromEmail = $GLOBALS['MAIL_PREVIEW_FROM'] ?? '';
|
||||
$fromName = $GLOBALS['MAIL_PREVIEW_FROM_NAME'] ?? '';
|
||||
|
||||
$smtpHost = $GLOBALS['MAIL_PREVIEW_MAIL_HOST'] ?? '';
|
||||
$smtpUser = $GLOBALS['MAIL_PREVIEW_MAIL_USERNAME'] ?? '';
|
||||
$smtpPort = $GLOBALS['MAIL_PREVIEW_MAIL_PORT'] ?? '';
|
||||
$smtpEncryption = $GLOBALS['MAIL_PREVIEW_MAIL_ENCRYPTION'] ?? '';
|
||||
$smtpPassword = $GLOBALS['MAIL_PREVIEW_MAIL_PASSWORD'] ?? '';
|
||||
$projectStart = $GLOBALS['MAIL_PREVIEW_PROJECT_START'] ?? '';
|
||||
$currentYear = $GLOBALS['MAIL_PREVIEW_CURRENT_YEAR'] ?? '';
|
||||
|
||||
$templateIndex = $GLOBALS['MAIL_PREVIEW_TEMPLATES_INDEX'] ?? [];
|
||||
$templateLabel = $GLOBALS['MAIL_PREVIEW_TEMPLATE_LABEL'] ?? '';
|
||||
$templateKey = $GLOBALS['MAIL_PREVIEW_TEMPLATE_KEY'] ?? $selectedTemplate;
|
||||
|
||||
/**
|
||||
* Build a full state snapshot so ANY change (template or mail config)
|
||||
* will change the hash and trigger a live refresh.
|
||||
*/
|
||||
$previewState = [
|
||||
'body' => $body,
|
||||
'subject' => $subject,
|
||||
'to' => $to,
|
||||
'username' => $username,
|
||||
'domain' => $domain,
|
||||
'company' => $company,
|
||||
'fromName' => $fromName,
|
||||
'fromEmail' => $fromEmail,
|
||||
'smtpHost' => $smtpHost,
|
||||
'smtpUser' => $smtpUser,
|
||||
'smtpPort' => $smtpPort,
|
||||
'smtpEncryption' => $smtpEncryption,
|
||||
// we only care if a password exists, not the value itself
|
||||
'hasSmtpPassword' => (bool) $smtpPassword,
|
||||
'projectStart' => $projectStart,
|
||||
'currentYear' => $currentYear,
|
||||
];
|
||||
|
||||
$bodyHash = sha1(json_encode($previewState, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
|
||||
if (isset($_GET['mode']) && $_GET['mode'] === 'json') {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode([
|
||||
'hash' => $bodyHash,
|
||||
'state' => $previewState,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Status banner state
|
||||
*/
|
||||
$statusKind = null; // 'success' | 'error' | null
|
||||
$statusMessage = null;
|
||||
|
||||
if (isset($_GET['status'])) {
|
||||
if ($_GET['status'] === 'ok') {
|
||||
$statusKind = 'success';
|
||||
$statusMessage = "Email sent successfully to {$to}";
|
||||
} elseif ($_GET['status'] === 'fail') {
|
||||
$statusKind = 'error';
|
||||
$statusMessage = "Failed to send email. Check error logs.";
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Mail Renderer - Xovae</title>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lexend+Deca:wght@300;400;500;600;700&display=swap"
|
||||
rel="stylesheet" />
|
||||
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['"Lexend Deca"', 'system-ui', 'sans-serif'],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.grid-pattern {
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(148, 163, 184, 0.08) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, rgba(148, 163, 184, 0.08) 1px, transparent 1px);
|
||||
background-size: 24px 24px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="bg-slate-50 text-slate-900 min-h-screen font-sans antialiased">
|
||||
<div class="min-h-screen flex flex-col">
|
||||
<header class="bg-slate-900 text-white border-b border-slate-700">
|
||||
<div class="max-w-[1800px] mx-auto px-6 py-6">
|
||||
<div class="flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div
|
||||
class="flex items-center justify-center w-12 h-12 bg-blue-600 text-white font-bold text-lg rounded-md">
|
||||
<?= strtoupper(substr($domain ?: 'UD', 0, 2)) ?>
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-2xl font-semibold tracking-tight" id="domainTitle">
|
||||
<?= htmlspecialchars($domain ?: 'MAIL PREVIEW CONSOLE') ?>
|
||||
</h1>
|
||||
<p class="text-xs font-medium text-slate-400 tracking-[0.18em] mt-0.5">
|
||||
Mail Template Review & Delivery Workspace
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col sm:flex-row items-start sm:items-center gap-3">
|
||||
<form method="get"
|
||||
class="flex items-center gap-3 bg-slate-800/90 border border-slate-700 px-4 py-2.5 rounded-lg">
|
||||
<label class="text-xs font-semibold text-slate-400 uppercase tracking-wider">
|
||||
TEMPLATE
|
||||
</label>
|
||||
<div class="relative">
|
||||
<select name="tpl" class="appearance-none bg-slate-700 border border-slate-600 text-white text-sm
|
||||
pl-3 pr-8 py-1.5 rounded-md
|
||||
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500
|
||||
cursor-pointer" onchange="this.form.submit()">
|
||||
<?php foreach ($templateIndex as $tpl): ?>
|
||||
<option value="<?= htmlspecialchars($tpl['key']) ?>"
|
||||
<?= $tpl['key'] === $templateKey ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($tpl['label']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<svg class="pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-300"
|
||||
fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form method="post" onsubmit="handleSendEmail(event)">
|
||||
<input type="hidden" name="tpl" value="<?= htmlspecialchars($templateKey) ?>" />
|
||||
<button type="submit" name="send" id="sendButton" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2.5 rounded font-medium text-sm
|
||||
transition-colors focus:outline-none focus:ring-2
|
||||
focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-slate-900
|
||||
flex items-center gap-2">
|
||||
<svg id="sendIcon" class="w-4 h-4" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
|
||||
</svg>
|
||||
<svg id="loadingIcon" class="w-4 h-4 animate-spin hidden" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke-width="4" />
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0
|
||||
3.042 1.135 5.824 3 7.938l3-2.647z" />
|
||||
</svg>
|
||||
<span id="sendText">Send Email</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<?php if ($statusKind && $statusMessage): ?>
|
||||
<div class="bg-white border-b border-slate-200">
|
||||
<div class="max-w-[1800px] mx-auto px-6 py-3">
|
||||
<div
|
||||
class="flex items-center gap-3 <?= $statusKind === 'success' ? 'text-green-700' : 'text-red-700' ?>">
|
||||
<div
|
||||
class="flex items-center justify-center w-8 h-8 rounded-full <?= $statusKind === 'success' ? 'bg-green-100' : 'bg-red-100' ?>">
|
||||
<span class="text-lg font-bold">
|
||||
<?= $statusKind === 'success' ? '✓' : '✕' ?>
|
||||
</span>
|
||||
</div>
|
||||
<p class="font-medium text-sm"><?= htmlspecialchars($statusMessage) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="flex-1 max-w-[1800px] mx-auto w-full px-6 py-6">
|
||||
<div class="grid grid-cols-1 xl:grid-cols-[380px,minmax(0,1fr)] gap-6 h-full">
|
||||
<section class="bg-white border border-slate-200 shadow-sm rounded-lg overflow-hidden flex flex-col">
|
||||
<div class="bg-slate-800 text-white px-5 py-4 border-b border-slate-700">
|
||||
<h2 class="font-semibold text-sm tracking-wider">
|
||||
Message Configurations From .env
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto px-5 py-5 space-y-6 text-sm">
|
||||
<div>
|
||||
<h3
|
||||
class="text-xs font-semibold uppercase tracking-wider text-slate-500 mb-3 pb-2 border-b border-slate-200">
|
||||
Message Details
|
||||
</h3>
|
||||
<dl class="space-y-3">
|
||||
<div>
|
||||
<dt class="text-[11px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
|
||||
From
|
||||
</dt>
|
||||
<dd class="text-slate-900">
|
||||
<div class="font-medium" id="fromNameValue">
|
||||
<?= htmlspecialchars($fromName ?: 'N/A') ?>
|
||||
</div>
|
||||
<div class="text-xs text-slate-500" id="fromEmailValue">
|
||||
<?= htmlspecialchars($fromEmail ?: 'N/A') ?>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
|
||||
To
|
||||
</dt>
|
||||
<dd class="text-slate-900">
|
||||
<div class="font-medium" id="toNameValue">
|
||||
<?= htmlspecialchars($username ?: 'User') ?>
|
||||
</div>
|
||||
<div class="text-xs text-slate-500" id="toEmailValue">
|
||||
<?= htmlspecialchars($to ?: 'N/A') ?>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
|
||||
Subject
|
||||
</dt>
|
||||
<dd class="font-medium text-slate-900" id="subjectValue">
|
||||
<?= htmlspecialchars($subject) ?>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3
|
||||
class="text-xs font-semibold uppercase tracking-wider text-slate-500 mb-3 pb-2 border-b border-slate-200">
|
||||
Organization
|
||||
</h3>
|
||||
<dl class="space-y-3">
|
||||
<div>
|
||||
<dt class="text-[11px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
|
||||
Domain
|
||||
</dt>
|
||||
<dd class="font-medium text-slate-900" id="domainValue">
|
||||
<?= htmlspecialchars($domain ?: '—') ?>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
|
||||
Company
|
||||
</dt>
|
||||
<dd class="font-medium text-slate-900" id="companyValue">
|
||||
<?= htmlspecialchars($company ?: '—') ?>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3
|
||||
class="text-xs font-semibold uppercase tracking-wider text-slate-500 mb-3 pb-2 border-slate-200 border-b">
|
||||
SMTP Configuration
|
||||
</h3>
|
||||
<dl class="space-y-3">
|
||||
<div>
|
||||
<dt class="text-[11px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
|
||||
Host
|
||||
</dt>
|
||||
<dd class="text-xs text-slate-900" id="smtpHostValue">
|
||||
<?= htmlspecialchars($smtpHost ?: '—') ?>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
|
||||
Username
|
||||
</dt>
|
||||
<dd class="text-xs text-slate-900" id="smtpUserValue">
|
||||
<?= htmlspecialchars($smtpUser ?: '—') ?>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
|
||||
Port
|
||||
</dt>
|
||||
<dd class="text-xs text-slate-900" id="smtpPortValue">
|
||||
<?= htmlspecialchars($smtpPort ?: '—') ?>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
|
||||
Encryption
|
||||
</dt>
|
||||
<dd class="text-xs text-slate-900" id="smtpEncValue">
|
||||
<?= htmlspecialchars($smtpEncryption ?: '—') ?>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
|
||||
Password
|
||||
</dt>
|
||||
<dd class="text-xs text-slate-500" id="smtpPwdValue">
|
||||
<?= htmlspecialchars($smtpPassword ? '••••••••' : '—') ?>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-[11px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
|
||||
Project Timeline
|
||||
</dt>
|
||||
<dd class="text-xs text-slate-900" id="projectTimelineValue">
|
||||
<?php if ($projectStart && $currentYear): ?>
|
||||
<?= htmlspecialchars($projectStart) ?> – <?= htmlspecialchars($currentYear) ?>
|
||||
<?php else: ?>
|
||||
<span class="text-slate-400">—</span>
|
||||
<?php endif; ?>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="bg-slate-50 border border-slate-200 rounded p-4">
|
||||
<h3 class="text-xs font-semibold uppercase tracking-wider text-slate-700 mb-2">
|
||||
Documentation
|
||||
</h3>
|
||||
<p class="text-xs leading-relaxed text-slate-600">
|
||||
Preview live-rendered HTML email templates are generated by PHPMailer.
|
||||
To modify a template, edit
|
||||
<code class="text-[10px] bg-white px-1.5 py-0.5 border border-slate-300 rounded">
|
||||
app/mail/mailer.php
|
||||
</code>
|
||||
and manage template definitions in
|
||||
<code class="text-[10px] bg-white px-1.5 py-0.5 border border-slate-300 rounded">
|
||||
app/mail/templates/loader.php
|
||||
</code>.
|
||||
For more, visit
|
||||
<a href="https://github.com/xodivorce/mailer-dev-console" target="_blank"
|
||||
class="text-[#0f172a] hover:text-[#0f172acb] underline">
|
||||
xodivorce/mailer-dev-console
|
||||
</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="bg-white border border-slate-200 shadow-sm rounded-lg overflow-hidden flex flex-col min-h-[700px]">
|
||||
<div class="bg-slate-100 border-b border-slate-200 px-5 py-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div id="livePreviewBadge"
|
||||
class="flex items-center justify-center w-10 h-8 bg-slate-700 text-white text-xs rounded transition-colors duration-200">
|
||||
HTML
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="font-semibold text-slate-900 text-base">
|
||||
Rendering Console
|
||||
</h2>
|
||||
<p id="previewSubjectLine" class="text-xs text-slate-500 mt-0.5 leading-snug">
|
||||
<?= htmlspecialchars($subject) ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs font-semibold text-slate-600 tracking-wider">
|
||||
iFRAME RENDERING..
|
||||
</span>
|
||||
<span class="relative flex h-2 w-2">
|
||||
<span
|
||||
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 grid-pattern bg-slate-50 p-2">
|
||||
<iframe id="previewFrame"
|
||||
class="w-full h-full bg-white border border-slate-200 rounded shadow-inner"
|
||||
srcdoc="<?= htmlspecialchars($body, ENT_QUOTES, 'UTF-8') ?>"></iframe>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const MAILER_INITIAL_HASH = "<?= htmlspecialchars($bodyHash, ENT_QUOTES, 'UTF-8') ?>";
|
||||
const MAILER_TPL_KEY = "<?= htmlspecialchars($templateKey, ENT_QUOTES, 'UTF-8') ?>";
|
||||
const MAILER_PHP_SELF = "<?= htmlspecialchars($_SERVER['PHP_SELF'], ENT_QUOTES, 'UTF-8') ?>";
|
||||
|
||||
let currentHash = MAILER_INITIAL_HASH;
|
||||
|
||||
function handleSendEmail(event) {
|
||||
const button = document.getElementById('sendButton');
|
||||
const sendIcon = document.getElementById('sendIcon');
|
||||
const loadingIcon = document.getElementById('loadingIcon');
|
||||
const sendText = document.getElementById('sendText');
|
||||
|
||||
button.disabled = true;
|
||||
button.classList.add('opacity-75', 'cursor-not-allowed');
|
||||
button.classList.remove('hover:bg-blue-700');
|
||||
|
||||
sendIcon.classList.add('hidden');
|
||||
loadingIcon.classList.remove('hidden');
|
||||
sendText.textContent = 'Processing...';
|
||||
}
|
||||
|
||||
function flashLivePreviewBadge() {
|
||||
const badge = document.getElementById('livePreviewBadge');
|
||||
if (!badge) return;
|
||||
badge.classList.remove('bg-slate-700');
|
||||
badge.classList.add('bg-green-600');
|
||||
setTimeout(() => {
|
||||
badge.classList.remove('bg-green-600');
|
||||
badge.classList.add('bg-slate-700');
|
||||
}, 600);
|
||||
}
|
||||
|
||||
function setText(id, value) {
|
||||
const el = document.getElementById(id);
|
||||
if (el) {
|
||||
el.textContent = value;
|
||||
}
|
||||
}
|
||||
|
||||
async function pollTemplateChanges() {
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
tpl: MAILER_TPL_KEY,
|
||||
mode: 'json',
|
||||
_: Date.now().toString(),
|
||||
});
|
||||
|
||||
const res = await fetch(MAILER_PHP_SELF + '?' + params.toString(), {
|
||||
cache: 'no-store',
|
||||
});
|
||||
|
||||
if (!res.ok) throw new Error('Preview poll failed');
|
||||
|
||||
const data = await res.json();
|
||||
if (!data || !data.hash || !data.state) return;
|
||||
|
||||
if (data.hash !== currentHash) {
|
||||
currentHash = data.hash;
|
||||
|
||||
const s = data.state;
|
||||
|
||||
// iframe + subject
|
||||
const iframe = document.getElementById('previewFrame');
|
||||
const subjectLine = document.getElementById('previewSubjectLine');
|
||||
|
||||
if (iframe && typeof s.body === 'string') {
|
||||
iframe.srcdoc = s.body;
|
||||
}
|
||||
if (subjectLine && typeof s.subject === 'string') {
|
||||
subjectLine.textContent = s.subject || '(no subject)';
|
||||
}
|
||||
setText('subjectValue', s.subject || '(no subject)');
|
||||
|
||||
// message details
|
||||
setText('fromNameValue', s.fromName || 'N/A');
|
||||
setText('fromEmailValue', s.fromEmail || 'N/A');
|
||||
setText('toNameValue', s.username || 'User');
|
||||
setText('toEmailValue', s.to || 'N/A');
|
||||
|
||||
// org
|
||||
setText('domainValue', s.domain || '—');
|
||||
setText('companyValue', s.company || '—');
|
||||
|
||||
// header title domain
|
||||
const domainTitle = document.getElementById('domainTitle');
|
||||
if (domainTitle) {
|
||||
domainTitle.textContent = s.domain || 'MAIL PREVIEW CONSOLE';
|
||||
}
|
||||
|
||||
// smtp
|
||||
setText('smtpHostValue', s.smtpHost || '—');
|
||||
setText('smtpUserValue', s.smtpUser || '—');
|
||||
setText('smtpPortValue', s.smtpPort || '—');
|
||||
setText('smtpEncValue', s.smtpEncryption || '—');
|
||||
setText('smtpPwdValue', s.hasSmtpPassword ? '••••••••' : '—');
|
||||
|
||||
// project timeline
|
||||
const timeline = (s.projectStart && s.currentYear)
|
||||
? (s.projectStart + ' – ' + s.currentYear)
|
||||
: '—';
|
||||
setText('projectTimelineValue', timeline);
|
||||
|
||||
flashLivePreviewBadge();
|
||||
}
|
||||
} catch (err) {
|
||||
// silent fail, retry
|
||||
} finally {
|
||||
setTimeout(pollTemplateChanges, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
pollTemplateChanges();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user