Mailing system improvements

This commit is contained in:
Pinga 2024-01-16 10:23:38 +02:00
parent 27442060e1
commit 6b905c8073
6 changed files with 727 additions and 6 deletions

View file

@ -90,6 +90,7 @@ Additionally, we extend our gratitude to:
- **ChatGPT** for invaluable assistance with code and text writing.
- [Slim Framework 4 Starter App](https://github.com/hezecom/slim-starter) which served as the foundation for our control panel.
- [Tabler](https://tabler.io/), whose elegant and intuitive interface design has greatly influenced the user experience of Namingo.
- [leemunroe/responsive-html-email-template](https://github.com/leemunroe/responsive-html-email-template), for providing a great email template for our mailing system.
## Licensing

View file

@ -43,6 +43,15 @@ if ($row) {
$registryName = 'Example Registry LLC';
}
$stmt = $pdo->prepare("SELECT value FROM settings WHERE name = :name");
$stmt->execute(['name' => 'currency']);
$row = $stmt->fetch();
if ($row) {
$currency = $row['value'];
} else {
$currency = 'USD';
}
// Define the query
$sql = "SELECT id, clid, name, accountBalance, creditThreshold, creditLimit, email FROM registrar";
@ -52,13 +61,13 @@ try {
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
if ($row['accountBalance'] < $row['creditThreshold']) {
// Case 1: accountBalance is less than creditThreshold
sendEmail($row, 'low_balance', $log, $supportEmail, $supportPhoneNumber, $registryName);
sendEmail($row, 'low_balance', $log, $supportEmail, $supportPhoneNumber, $registryName, $currency);
} elseif ($row['accountBalance'] == 0) {
// Case 2: accountBalance is 0
sendEmail($row, 'zero_balance', $log, $supportEmail, $supportPhoneNumber, $registryName);
sendEmail($row, 'zero_balance', $log, $supportEmail, $supportPhoneNumber, $registryName, $currency);
} elseif (($row['accountBalance'] + $row['creditLimit']) < 0) {
// Case 3: accountBalance + creditLimit is less than 0
sendEmail($row, 'over_limit', $log, $supportEmail, $supportPhoneNumber, $registryName);
sendEmail($row, 'over_limit', $log, $supportEmail, $supportPhoneNumber, $registryName, $currency);
}
}
@ -70,13 +79,13 @@ try {
}
// Function to send email
function sendEmail($data, $case, $log, $supportEmail, $supportPhoneNumber, $registryName) {
function sendEmail($data, $case, $log, $supportEmail, $supportPhoneNumber, $registryName, $currency) {
$message = "Dear ".$data['name'].",\n\n";
switch ($case) {
case 'low_balance':
$subject = "Low balance alert for registrar: " . $data['clid'];
$message .= "We are writing to inform you that your account with us currently has a low balance. As of now, your account balance is {$data['accountBalance']}, which is below the minimum credit threshold of {$data['creditThreshold']}.\n\n";
$message .= "We are writing to inform you that your account with us currently has a low balance. As of now, your account balance is $currency {$data['accountBalance']}, which is below the minimum credit threshold of $currency {$data['creditThreshold']}.\n\n";
break;
case 'zero_balance':
$subject = "Zero balance alert for registrar: " . $data['clid'];

View file

@ -3,6 +3,7 @@
namespace App\Controllers;
use App\Models\Tickets;
use App\Lib\Mail;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Container\ContainerInterface;
@ -65,6 +66,20 @@ class SupportController extends Controller
$ticket_id = $db->getLastInsertId();
$db->commit();
$link = envi('APP_URL').'/ticket/'.$ticket_id;
$email = $db->selectValue('SELECT email FROM users WHERE id = ?', [$_SESSION['auth_user_id']]);
$registry = $db->selectValue('SELECT value FROM settings WHERE name = ?', ['company_name']);
$crdate = $currentDateTime->format('Y-m-d H:i:s.v');
$message = file_get_contents(__DIR__.'/../../resources/views/mail/ticket.html');
$placeholders = ['{registry}', '{link}', '{app_name}', '{app_url}', '{crdate}', '{ticket_id}', '{subject}'];
$replacements = [$registry, $link, envi('APP_NAME'), envi('APP_URL'), $crdate, $ticket_id, $subject];
$message = str_replace($placeholders, $replacements, $message);
$mailsubject = '[' . envi('APP_NAME') . '] New Support Ticket Created';
$from = ['email'=>envi('MAIL_FROM_ADDRESS'), 'name'=>envi('MAIL_FROM_NAME')];
$to = ['email'=>$email, 'name'=>''];
// send message
Mail::send($mailsubject, $message, $from, $to);
} catch (Exception $e) {
$db->rollBack();
return view($response, 'admin/support/newticket.twig', [
@ -72,7 +87,7 @@ class SupportController extends Controller
'categories' => $categories
]);
}
$this->container->get('flash')->addMessage('success', 'Support ticket ' . $subject . ' has been created successfully!');
return $response->withHeader('Location', '/support')->withStatus(302);
}
@ -158,6 +173,23 @@ class SupportController extends Controller
]
);
$link = envi('APP_URL').'/ticket/'.$ticket_id;
$email = $db->selectValue('SELECT email FROM users WHERE id = ?', [$_SESSION['auth_user_id']]);
$registry = $db->selectValue('SELECT value FROM settings WHERE name = ?', ['company_name']);
$responseBrief = mb_substr($responseText, 0, 100);
if (mb_strlen($responseText) > 100) {
$responseBrief .= "...";
}
$message = file_get_contents(__DIR__.'/../../resources/views/mail/ticket-reply.html');
$placeholders = ['{registry}', '{link}', '{app_name}', '{app_url}', '{latest}', '{ticket_id}'];
$replacements = [$registry, $link, envi('APP_NAME'), envi('APP_URL'), $responseBrief, $ticket_id];
$message = str_replace($placeholders, $replacements, $message);
$mailsubject = '[' . envi('APP_NAME') . '] Update on Your Support Ticket';
$from = ['email'=>envi('MAIL_FROM_ADDRESS'), 'name'=>envi('MAIL_FROM_NAME')];
$to = ['email'=>$email, 'name'=>''];
// send message
Mail::send($mailsubject, $message, $from, $to);
$this->container->get('flash')->addMessage('success', 'Reply has been created successfully on ' . $crdate);
return $response->withHeader('Location', '/ticket/'.$ticket_id)->withStatus(302);
} catch (Exception $e) {

View file

@ -0,0 +1,332 @@
<!doctype html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Update on Your Support Ticket</title>
<style media="all" type="text/css">
body {
font-family: Helvetica, sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 16px;
line-height: 1.3;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
table {
border-collapse: separate;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
width: 100%;
}
table td {
font-family: Helvetica, sans-serif;
font-size: 16px;
vertical-align: top;
}
body {
background-color: #f4f5f6;
margin: 0;
padding: 0;
}
.body {
background-color: #f4f5f6;
width: 100%;
}
.container {
margin: 0 auto !important;
max-width: 600px;
padding: 0;
padding-top: 24px;
width: 600px;
}
.content {
box-sizing: border-box;
display: block;
margin: 0 auto;
max-width: 600px;
padding: 0;
}
.main {
background: #ffffff;
border: 1px solid #eaebed;
border-radius: 16px;
width: 100%;
}
.wrapper {
box-sizing: border-box;
padding: 24px;
}
.footer {
clear: both;
padding-top: 24px;
text-align: center;
width: 100%;
}
.footer td,
.footer p,
.footer span,
.footer a {
color: #9a9ea6;
font-size: 16px;
text-align: center;
}
p {
font-family: Helvetica, sans-serif;
font-size: 16px;
font-weight: normal;
margin: 0;
margin-bottom: 16px;
}
a {
color: #0867ec;
text-decoration: underline;
}
.btn {
box-sizing: border-box;
min-width: 100% !important;
width: 100%;
}
.btn > tbody > tr > td {
padding-bottom: 16px;
}
.btn table {
width: auto;
}
.btn table td {
background-color: #ffffff;
border-radius: 4px;
text-align: center;
}
.btn a {
background-color: #ffffff;
border: solid 2px #0867ec;
border-radius: 4px;
box-sizing: border-box;
color: #0867ec;
cursor: pointer;
display: inline-block;
font-size: 16px;
font-weight: bold;
margin: 0;
padding: 12px 24px;
text-decoration: none;
text-transform: capitalize;
}
.btn-primary table td {
background-color: #0867ec;
}
.btn-primary a {
background-color: #0867ec;
border-color: #0867ec;
color: #ffffff;
}
@media all {
.btn-primary table td:hover {
background-color: #ec0867 !important;
}
.btn-primary a:hover {
background-color: #ec0867 !important;
border-color: #ec0867 !important;
}
}
.last {
margin-bottom: 0;
}
.first {
margin-top: 0;
}
.align-center {
text-align: center;
}
.align-right {
text-align: right;
}
.align-left {
text-align: left;
}
.text-link {
color: #0867ec !important;
text-decoration: underline !important;
}
.clear {
clear: both;
}
.mt0 {
margin-top: 0;
}
.mb0 {
margin-bottom: 0;
}
.preheader {
color: transparent;
display: none;
height: 0;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
mso-hide: all;
visibility: hidden;
width: 0;
}
.powered-by a {
text-decoration: none;
}
@media only screen and (max-width: 640px) {
.main p,
.main td,
.main span {
font-size: 16px !important;
}
.wrapper {
padding: 8px !important;
}
.content {
padding: 0 !important;
}
.container {
padding: 0 !important;
padding-top: 8px !important;
width: 100% !important;
}
.main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
.btn table {
max-width: 100% !important;
width: 100% !important;
}
.btn a {
font-size: 16px !important;
max-width: 100% !important;
width: 100% !important;
}
}
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
}
</style>
</head>
<body>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
<tr>
<td>&nbsp;</td>
<td class="container">
<div class="content">
<span class="preheader">Update on Your Support Ticket.</span>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="main">
<tr>
<td class="wrapper">
<p>Hi there,</p>
<p>We've got an update on your support ticket. One of our team members has responded to your inquiry.</p>
<p><strong>Ticket Details:</strong></p>
<p>Ticket ID: {ticket_id}<br />Latest Reply: {latest}</p>
<p><strong>View Full Reply & Respond:</strong></p>
<p>To read the full reply and respond, please click the button below. We're here to help you resolve your issue as quickly as possible.</p>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
<tbody>
<tr>
<td align="left">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td> <a href="{link}" target="_blank">View Reply</a> </td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p>Your satisfaction is important to us, and we appreciate your patience as we work to address your concerns.</p>
<p>Should you have any further questions or need additional assistance, don't hesitate to reach out. Our support team is always here for you.</p>
<p>Thank you for choosing {registry} for your needs. We are here to help!</p>
</td>
</tr>
</table>
<div class="footer">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="content-block">
<span class="apple-link">{registry} Support Team</span>
</td>
</tr>
<tr>
<td class="content-block powered-by">
Powered by <a href="{app_url}" target="_blank">{app_name}</a>
</td>
</tr>
</table>
</div>
</div>
</td>
<td>&nbsp;</td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,331 @@
<!doctype html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>New Support Ticket Created</title>
<style media="all" type="text/css">
body {
font-family: Helvetica, sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 16px;
line-height: 1.3;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
table {
border-collapse: separate;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
width: 100%;
}
table td {
font-family: Helvetica, sans-serif;
font-size: 16px;
vertical-align: top;
}
body {
background-color: #f4f5f6;
margin: 0;
padding: 0;
}
.body {
background-color: #f4f5f6;
width: 100%;
}
.container {
margin: 0 auto !important;
max-width: 600px;
padding: 0;
padding-top: 24px;
width: 600px;
}
.content {
box-sizing: border-box;
display: block;
margin: 0 auto;
max-width: 600px;
padding: 0;
}
.main {
background: #ffffff;
border: 1px solid #eaebed;
border-radius: 16px;
width: 100%;
}
.wrapper {
box-sizing: border-box;
padding: 24px;
}
.footer {
clear: both;
padding-top: 24px;
text-align: center;
width: 100%;
}
.footer td,
.footer p,
.footer span,
.footer a {
color: #9a9ea6;
font-size: 16px;
text-align: center;
}
p {
font-family: Helvetica, sans-serif;
font-size: 16px;
font-weight: normal;
margin: 0;
margin-bottom: 16px;
}
a {
color: #0867ec;
text-decoration: underline;
}
.btn {
box-sizing: border-box;
min-width: 100% !important;
width: 100%;
}
.btn > tbody > tr > td {
padding-bottom: 16px;
}
.btn table {
width: auto;
}
.btn table td {
background-color: #ffffff;
border-radius: 4px;
text-align: center;
}
.btn a {
background-color: #ffffff;
border: solid 2px #0867ec;
border-radius: 4px;
box-sizing: border-box;
color: #0867ec;
cursor: pointer;
display: inline-block;
font-size: 16px;
font-weight: bold;
margin: 0;
padding: 12px 24px;
text-decoration: none;
text-transform: capitalize;
}
.btn-primary table td {
background-color: #0867ec;
}
.btn-primary a {
background-color: #0867ec;
border-color: #0867ec;
color: #ffffff;
}
@media all {
.btn-primary table td:hover {
background-color: #ec0867 !important;
}
.btn-primary a:hover {
background-color: #ec0867 !important;
border-color: #ec0867 !important;
}
}
.last {
margin-bottom: 0;
}
.first {
margin-top: 0;
}
.align-center {
text-align: center;
}
.align-right {
text-align: right;
}
.align-left {
text-align: left;
}
.text-link {
color: #0867ec !important;
text-decoration: underline !important;
}
.clear {
clear: both;
}
.mt0 {
margin-top: 0;
}
.mb0 {
margin-bottom: 0;
}
.preheader {
color: transparent;
display: none;
height: 0;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
mso-hide: all;
visibility: hidden;
width: 0;
}
.powered-by a {
text-decoration: none;
}
@media only screen and (max-width: 640px) {
.main p,
.main td,
.main span {
font-size: 16px !important;
}
.wrapper {
padding: 8px !important;
}
.content {
padding: 0 !important;
}
.container {
padding: 0 !important;
padding-top: 8px !important;
width: 100% !important;
}
.main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
.btn table {
max-width: 100% !important;
width: 100% !important;
}
.btn a {
font-size: 16px !important;
max-width: 100% !important;
width: 100% !important;
}
}
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
}
</style>
</head>
<body>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
<tr>
<td>&nbsp;</td>
<td class="container">
<div class="content">
<span class="preheader">New Support Ticket Created.</span>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="main">
<tr>
<td class="wrapper">
<p>Hi there,</p>
<p>Thank you for reaching out to our support team. A new support ticket has been successfully created and our team is ready to assist you.</p>
<p><strong>Ticket Details:</strong></p>
<p>Ticket ID: {ticket_id}<br />Subject: {subject}<br />Created At: {crdate}</p>
<p><strong>Next Steps:</strong></p>
<p>To view the details of your ticket or to add additional information, please click the button below. Our support team will review your ticket and respond promptly.</p>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
<tbody>
<tr>
<td align="left">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td> <a href="{link}" target="_blank">View Ticket</a> </td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<p>We understand the importance of resolving your issue quickly and efficiently, and we are committed to providing you with the best support possible.</p>
<p>Thank you for choosing {registry} for your needs. We are here to help!</p>
</td>
</tr>
</table>
<div class="footer">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="content-block">
<span class="apple-link">{registry} Support Team</span>
</td>
</tr>
<tr>
<td class="content-block powered-by">
Powered by <a href="{app_url}" target="_blank">{app_name}</a>
</td>
</tr>
</table>
</div>
</div>
</td>
<td>&nbsp;</td>
</tr>
</table>
</body>
</html>

View file

@ -300,6 +300,22 @@ composer install
This command will install the dependencies defined in your ```composer.json``` file, ensuring that your control panel has all the necessary components to operate effectively.
### Install Optional Dependencies:
Execute one of the following commands to install the optional dependencies:
```bash
composer require utopia-php/messaging
```
or
```bash
composer require phpmailer/phpmailer
```
This command will install one of the packages which are essential for the mailing system of the control panel to function correctly.
### Creating an Admin User:
1. Navigate to the 'bin' Directory: Change to the 'bin' subdirectory where the admin user creation script is located. (```create_admin_user.php```)