add email sent for event
This commit is contained in:
@@ -170,6 +170,163 @@ class EmailController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send one email with a single To recipient and many CC recipients.
|
||||||
|
*
|
||||||
|
* @param string $recipient
|
||||||
|
* @param string[] $ccRecipients
|
||||||
|
* @param string $subject
|
||||||
|
* @param string $htmlMessage
|
||||||
|
* @param string|null $profile
|
||||||
|
* @param string|null $replyToEmail
|
||||||
|
* @param string|null $replyToName
|
||||||
|
* @param array $attachments
|
||||||
|
*/
|
||||||
|
public function sendEmailWithCc(
|
||||||
|
string $recipient,
|
||||||
|
array $ccRecipients,
|
||||||
|
string $subject,
|
||||||
|
string $htmlMessage,
|
||||||
|
?string $profile = null,
|
||||||
|
?string $replyToEmail = null,
|
||||||
|
?string $replyToName = null,
|
||||||
|
array $attachments = []
|
||||||
|
): bool {
|
||||||
|
$ccRecipients = array_values(array_unique(array_filter(array_map(static function ($email) {
|
||||||
|
$email = trim((string) $email);
|
||||||
|
return filter_var($email, FILTER_VALIDATE_EMAIL) ? $email : null;
|
||||||
|
}, $ccRecipients))));
|
||||||
|
|
||||||
|
if ($ccRecipients === []) {
|
||||||
|
return $this->sendEmail($recipient, $subject, $htmlMessage, $profile, $replyToEmail, $replyToName, $attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
$autoload = APPPATH . '../vendor/autoload.php';
|
||||||
|
if (is_file($autoload)) {
|
||||||
|
require_once $autoload;
|
||||||
|
}
|
||||||
|
|
||||||
|
$profile = $this->resolveProfile($profile);
|
||||||
|
$cfg = $this->getProfileConfig($profile);
|
||||||
|
|
||||||
|
if (empty($cfg['host']) || empty($cfg['user']) || $cfg['pass'] === '') {
|
||||||
|
log_message('error', "[mail:$profile] Missing SMTP config (host/user/pass). Check .env MAIL_{$this->envKeyFromProfile($profile)}_* or MAIL_DEFAULT_*.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$debugEnabled = (bool) env('MAIL_DEBUG', false);
|
||||||
|
$timeout = (int) env('MAIL_TIMEOUT', 15);
|
||||||
|
$keepAlive = (bool) env('MAIL_KEEPALIVE', false);
|
||||||
|
$verifyPeer = filter_var(env('MAIL_VERIFY_PEER', 'true'), FILTER_VALIDATE_BOOLEAN);
|
||||||
|
|
||||||
|
$targetHost = $cfg['host'];
|
||||||
|
$targetPort = (int) $cfg['port'];
|
||||||
|
|
||||||
|
$resolved = @gethostbyname($targetHost);
|
||||||
|
if (!$resolved || $resolved === $targetHost) {
|
||||||
|
log_message('debug', "[mail:$profile] DNS resolve note: host=$targetHost, resolved=$resolved");
|
||||||
|
}
|
||||||
|
|
||||||
|
$sockOk = @fsockopen($targetHost, $targetPort, $errno, $errstr, 5);
|
||||||
|
if (!$sockOk) {
|
||||||
|
log_message('error', "[mail:$profile] Socket preflight failed to {$targetHost}:{$targetPort} (errno=$errno, err=$errstr). Likely firewall/port/encryption mismatch or wrong host.");
|
||||||
|
} else {
|
||||||
|
fclose($sockOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail = new PHPMailer(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($debugEnabled) {
|
||||||
|
ob_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->isSMTP();
|
||||||
|
$mail->Host = $cfg['host'];
|
||||||
|
$mail->Port = $cfg['port'];
|
||||||
|
$mail->SMTPAuth = true;
|
||||||
|
$mail->Username = $cfg['user'];
|
||||||
|
$mail->Password = $cfg['pass'];
|
||||||
|
$mail->CharSet = 'UTF-8';
|
||||||
|
$mail->Timeout = $timeout;
|
||||||
|
$mail->SMTPKeepAlive = $keepAlive;
|
||||||
|
$mail->SMTPAutoTLS = true;
|
||||||
|
|
||||||
|
if ($cfg['encryption'] === 'ssl') {
|
||||||
|
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
|
||||||
|
} else {
|
||||||
|
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->SMTPOptions = [
|
||||||
|
'ssl' => [
|
||||||
|
'verify_peer' => $verifyPeer,
|
||||||
|
'verify_peer_name' => $verifyPeer,
|
||||||
|
'allow_self_signed' => !$verifyPeer,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$mail->SMTPDebug = $debugEnabled ? 3 : 0;
|
||||||
|
$mail->Debugoutput = 'error_log';
|
||||||
|
|
||||||
|
$mail->setFrom($cfg['fromEmail'], $cfg['fromName']);
|
||||||
|
if (!empty($cfg['returnPath'])) {
|
||||||
|
$mail->Sender = $cfg['returnPath'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->clearReplyTos();
|
||||||
|
$rtEmail = env('MAIL_DEFAULT_REPLY_TO');
|
||||||
|
$rtName = env('MAIL_DEFAULT_REPLY_TO_NAME');
|
||||||
|
if (!$rtEmail || !filter_var($rtEmail, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$rtEmail = $replyToEmail ?: ($cfg['replyTo'] ?: $cfg['fromEmail']);
|
||||||
|
}
|
||||||
|
if (!$rtName) {
|
||||||
|
$rtName = $replyToName ?: ($cfg['replyToName'] ?: $cfg['fromName']);
|
||||||
|
}
|
||||||
|
$rtName = $this->sanitizeReplyToName($rtName, $cfg['fromName']);
|
||||||
|
if ($rtEmail) {
|
||||||
|
$mail->addReplyTo($rtEmail, $rtName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($cfg['dkim']['domain']) && !empty($cfg['dkim']['private']) && !empty($cfg['dkim']['selector'])) {
|
||||||
|
$mail->DKIM_domain = $cfg['dkim']['domain'];
|
||||||
|
$mail->DKIM_private = $cfg['dkim']['private'];
|
||||||
|
$mail->DKIM_selector = $cfg['dkim']['selector'];
|
||||||
|
$mail->DKIM_identity = $cfg['fromEmail'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->addAddress($recipient);
|
||||||
|
foreach ($ccRecipients as $ccRecipient) {
|
||||||
|
$mail->addCC($ccRecipient);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($attachments as $att) {
|
||||||
|
if (!empty($att['path']) && is_file($att['path'])) {
|
||||||
|
$mail->addAttachment($att['path'], $att['name'] ?? '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$mail->isHTML(true);
|
||||||
|
$mail->Subject = $subject;
|
||||||
|
$mail->Body = $htmlMessage;
|
||||||
|
|
||||||
|
$ok = $mail->send();
|
||||||
|
|
||||||
|
$dbg = $debugEnabled ? (ob_get_clean() ?: '') : '';
|
||||||
|
if ($ok) {
|
||||||
|
log_message('info', "[mail:$profile] Sent to {$recipient} with " . count($ccRecipients) . " CC recipient(s), subj='{$subject}' via {$cfg['host']}:{$cfg['port']}/{$cfg['encryption']}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_message('error', "[mail:$profile] Failed: {$mail->ErrorInfo}. Debug: {$dbg}");
|
||||||
|
return false;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$dbg = $debugEnabled ? (ob_get_clean() ?: '') : '';
|
||||||
|
log_message('error', "[mail:$profile] Exception: {$e->getMessage()} | PHPMailer: {$mail->ErrorInfo} | Debug: {$dbg}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Resolve null/alias profile names to a canonical env prefix. */
|
/** Resolve null/alias profile names to a canonical env prefix. */
|
||||||
private function resolveProfile(?string $profile): string
|
private function resolveProfile(?string $profile): string
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ use App\Models\EnrollmentModel;
|
|||||||
use App\Models\InvoiceModel;
|
use App\Models\InvoiceModel;
|
||||||
use App\Models\InvoiceEventModel;
|
use App\Models\InvoiceEventModel;
|
||||||
use App\Models\ClassSectionModel;
|
use App\Models\ClassSectionModel;
|
||||||
|
use App\Models\ParentModel;
|
||||||
use App\Models\PaymentModel;
|
use App\Models\PaymentModel;
|
||||||
use App\Models\CalendarModel;
|
use App\Models\CalendarModel;
|
||||||
|
use App\Services\EmailService;
|
||||||
use Config\Database;
|
use Config\Database;
|
||||||
#use ???
|
#use ???
|
||||||
use App\Controllers\View\InvoiceController;
|
use App\Controllers\View\InvoiceController;
|
||||||
@@ -32,6 +34,8 @@ class EventController extends ResourceController
|
|||||||
protected $studentClassModel;
|
protected $studentClassModel;
|
||||||
protected $classSectionModel;
|
protected $classSectionModel;
|
||||||
protected $paymentModel;
|
protected $paymentModel;
|
||||||
|
protected $parentModel;
|
||||||
|
protected $emailService;
|
||||||
protected $schoolYear;
|
protected $schoolYear;
|
||||||
protected $semester;
|
protected $semester;
|
||||||
protected $categories;
|
protected $categories;
|
||||||
@@ -52,6 +56,8 @@ class EventController extends ResourceController
|
|||||||
$this->classSectionModel = new ClassSectionModel();
|
$this->classSectionModel = new ClassSectionModel();
|
||||||
$this->paymentModel = new PaymentModel();
|
$this->paymentModel = new PaymentModel();
|
||||||
$this->enrollmentModel = new EnrollmentModel();
|
$this->enrollmentModel = new EnrollmentModel();
|
||||||
|
$this->parentModel = new ParentModel();
|
||||||
|
$this->emailService = new EmailService();
|
||||||
|
|
||||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||||
$this->semester = $this->configModel->getConfig('semester');
|
$this->semester = $this->configModel->getConfig('semester');
|
||||||
@@ -167,7 +173,13 @@ class EventController extends ResourceController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->to('/administrator/events')->with('success', 'Event created successfully');
|
$redirect = redirect()->to('/administrator/events')->with('success', 'Event created successfully');
|
||||||
|
if ($eventId && $this->request->getPost('send_email_parent')) {
|
||||||
|
$emailStatus = $this->broadcastEventToParents((int) $eventId);
|
||||||
|
$redirect = $this->applyEventEmailFlash($redirect, $emailStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $redirect;
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('administrator/events/create_event', [
|
return view('administrator/events/create_event', [
|
||||||
@@ -208,7 +220,12 @@ class EventController extends ResourceController
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
if ($updated) {
|
if ($updated) {
|
||||||
return redirect()->to('/administrator/events')->with('success', 'Event updated successfully');
|
$redirect = redirect()->to('/administrator/events')->with('success', 'Event updated successfully');
|
||||||
|
if ($this->request->getPost('send_email_parent')) {
|
||||||
|
$emailStatus = $this->broadcastEventToParents((int) $id);
|
||||||
|
$redirect = $this->applyEventEmailFlash($redirect, $emailStatus);
|
||||||
|
}
|
||||||
|
return $redirect;
|
||||||
} else {
|
} else {
|
||||||
log_message('debug', 'GET detected');
|
log_message('debug', 'GET detected');
|
||||||
return redirect()->back()->with('error', 'Failed to update event');
|
return redirect()->back()->with('error', 'Failed to update event');
|
||||||
@@ -221,6 +238,106 @@ class EventController extends ResourceController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function broadcastEventToParents(int $eventId): array
|
||||||
|
{
|
||||||
|
$event = $this->eventModel->find($eventId);
|
||||||
|
if (!$event) {
|
||||||
|
return ['ok' => false, 'count' => 0, 'message' => 'Event email was skipped because the event could not be loaded.'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$emails = $this->collectParentBroadcastEmails((string) ($event['school_year'] ?? ''));
|
||||||
|
if ($emails === []) {
|
||||||
|
return ['ok' => false, 'count' => 0, 'message' => 'Event email was skipped because no parent email addresses were found.'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$subject = 'School Event: ' . trim((string) ($event['event_name'] ?? 'Upcoming Event'));
|
||||||
|
$innerBody = view('emails/event_broadcast', [
|
||||||
|
'event' => $event,
|
||||||
|
'eventUrl' => base_url('parent/events'),
|
||||||
|
'flyerUrl' => !empty($event['flyer']) ? base_url('uploads/' . ltrim((string) $event['flyer'], '/')) : null,
|
||||||
|
]);
|
||||||
|
$body = view('emails/_wrap_layout', [
|
||||||
|
'title' => $subject,
|
||||||
|
'subject' => $subject,
|
||||||
|
'body_html' => $innerBody,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$ok = $this->emailService->sendCc($emails, $subject, $body, 'general');
|
||||||
|
$batchCount = $this->emailService->getLastCcBatchCount();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ok' => $ok,
|
||||||
|
'count' => count($emails),
|
||||||
|
'batches' => $batchCount,
|
||||||
|
'message' => $ok
|
||||||
|
? 'Event email was sent in ' . $batchCount . ' CC batch(es) for ' . count($emails) . ' recipient(s).'
|
||||||
|
: 'The event was saved, but sending the parent CC email failed.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function collectParentBroadcastEmails(string $schoolYear = ''): array
|
||||||
|
{
|
||||||
|
$emails = [];
|
||||||
|
|
||||||
|
$db = Database::connect();
|
||||||
|
|
||||||
|
$parentRole = $db->table('roles')
|
||||||
|
->select('id')
|
||||||
|
->where('LOWER(name)', 'parent')
|
||||||
|
->get()
|
||||||
|
->getRow();
|
||||||
|
|
||||||
|
if ($parentRole) {
|
||||||
|
$firstParentQuery = $db->table('users')
|
||||||
|
->select('users.email')
|
||||||
|
->join('user_roles', 'user_roles.user_id = users.id')
|
||||||
|
->where('user_roles.role_id', (int) $parentRole->id)
|
||||||
|
->where('users.email IS NOT NULL', null, false)
|
||||||
|
->where('users.email !=', '');
|
||||||
|
|
||||||
|
if ($schoolYear !== '' && $db->fieldExists('school_year', 'users')) {
|
||||||
|
$firstParentQuery->where('users.school_year', $schoolYear);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($firstParentQuery->get()->getResultArray() as $row) {
|
||||||
|
$email = trim((string) ($row['email'] ?? ''));
|
||||||
|
if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$emails[strtolower($email)] = $email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$secondParentQuery = $db->table('parents')
|
||||||
|
->select('secondparent_email')
|
||||||
|
->where('secondparent_email IS NOT NULL', null, false)
|
||||||
|
->where('secondparent_email !=', '');
|
||||||
|
|
||||||
|
if ($schoolYear !== '' && $db->fieldExists('school_year', 'parents')) {
|
||||||
|
$secondParentQuery->where('school_year', $schoolYear);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($secondParentQuery->get()->getResultArray() as $row) {
|
||||||
|
$email = trim((string) ($row['secondparent_email'] ?? ''));
|
||||||
|
if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$emails[strtolower($email)] = $email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values($emails);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyEventEmailFlash($redirect, array $emailStatus)
|
||||||
|
{
|
||||||
|
if (!empty($emailStatus['message'])) {
|
||||||
|
return $redirect->with(
|
||||||
|
($emailStatus['ok'] ?? false) ? 'email_status' : 'email_error',
|
||||||
|
$emailStatus['message']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $redirect;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public function delete($id = null)
|
public function delete($id = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class EmailService
|
|||||||
{
|
{
|
||||||
protected array $senders;
|
protected array $senders;
|
||||||
protected EmailController $mailer;
|
protected EmailController $mailer;
|
||||||
|
protected int $lastCcBatchCount = 0;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@@ -73,6 +74,45 @@ class EmailService
|
|||||||
return $allOk;
|
return $allOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send one email with all recipients in CC.
|
||||||
|
*/
|
||||||
|
public function sendCc(array $ccList, string $subject, string $message, string $fromKey = 'general', array $attachments = [], array $headers = []): bool
|
||||||
|
{
|
||||||
|
$this->lastCcBatchCount = 0;
|
||||||
|
|
||||||
|
if (empty($ccList)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sender = $this->getSenderDetails($fromKey);
|
||||||
|
$to = trim((string) ($sender['email'] ?? ''));
|
||||||
|
if ($to === '' || !filter_var($to, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$fallback = trim((string) (getenv('SMTP_USER') ?: ''));
|
||||||
|
$to = filter_var($fallback, FILTER_VALIDATE_EMAIL) ? $fallback : 'no-reply@alrahmaisgl.org';
|
||||||
|
}
|
||||||
|
|
||||||
|
$attachmentsPayload = $this->normalizeAttachments($attachments);
|
||||||
|
$chunks = array_chunk(array_values($ccList), 10);
|
||||||
|
$this->lastCcBatchCount = count($chunks);
|
||||||
|
$allOk = true;
|
||||||
|
|
||||||
|
foreach ($chunks as $index => $chunk) {
|
||||||
|
$ok = $this->mailer->sendEmailWithCc($to, $chunk, $subject, $message, $fromKey, null, null, $attachmentsPayload);
|
||||||
|
if (!$ok) {
|
||||||
|
$allOk = false;
|
||||||
|
log_message('error', 'EmailService::sendCc failed for batch ' . ($index + 1) . ' of ' . count($chunks));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $allOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLastCcBatchCount(): int
|
||||||
|
{
|
||||||
|
return $this->lastCcBatchCount;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize various attachment inputs to the format expected by EmailController.
|
* Normalize various attachment inputs to the format expected by EmailController.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -87,6 +87,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header">Parent Email Broadcast</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="send_email_parent" name="send_email_parent" value="1">
|
||||||
|
<label class="form-check-label" for="send_email_parent">
|
||||||
|
Send one event email to all parents and add all parent emails in CC
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-text">The email is sent once. The school sender address stays in To, and all parent emails are placed in CC.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-success">Create Event</button>
|
<button type="submit" class="btn btn-success">Create Event</button>
|
||||||
<a href="<?= site_url('administrator/events') ?>" class="btn btn-secondary">Cancel</a>
|
<a href="<?= site_url('administrator/events') ?>" class="btn btn-secondary">Cancel</a>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -70,6 +70,19 @@
|
|||||||
<input type="text" name="school_year" class="form-control" value="<?= esc($event['school_year']) ?>" required>
|
<input type="text" name="school_year" class="form-control" value="<?= esc($event['school_year']) ?>" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-header">Parent Email Broadcast</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="send_email_parent" name="send_email_parent" value="1">
|
||||||
|
<label class="form-check-label" for="send_email_parent">
|
||||||
|
Send one event email to all parents and add all parent emails in CC
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-text">Use this when you want to rebroadcast the updated event to all parents in a single email.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Update Event</button>
|
<button type="submit" class="btn btn-primary">Update Event</button>
|
||||||
<a href="<?= site_url('administrator/events') ?>" class="btn btn-secondary">Cancel</a>
|
<a href="<?= site_url('administrator/events') ?>" class="btn btn-secondary">Cancel</a>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -12,6 +12,14 @@
|
|||||||
<div class="alert alert-danger"><?= session()->getFlashdata('error') ?></div>
|
<div class="alert alert-danger"><?= session()->getFlashdata('error') ?></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (session()->getFlashdata('email_status')): ?>
|
||||||
|
<div class="alert alert-info"><?= session()->getFlashdata('email_status') ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (session()->getFlashdata('email_error')): ?>
|
||||||
|
<div class="alert alert-warning"><?= session()->getFlashdata('email_error') ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (empty($events)): ?>
|
<?php if (empty($events)): ?>
|
||||||
<div class="alert alert-info mb-3 d-inline-block">No events found.</div>
|
<div class="alert alert-info mb-3 d-inline-block">No events found.</div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
$eventName = trim((string) ($event['event_name'] ?? 'Upcoming Event'));
|
||||||
|
$eventCategory = trim((string) ($event['event_category'] ?? ''));
|
||||||
|
$eventDateRaw = trim((string) ($event['expiration_date'] ?? ''));
|
||||||
|
$eventDate = $eventDateRaw !== '' ? date('F j, Y', strtotime($eventDateRaw)) : 'TBD';
|
||||||
|
$amountRaw = $event['amount'] ?? null;
|
||||||
|
$amount = ($amountRaw !== null && $amountRaw !== '') ? number_format((float) $amountRaw, 2) : null;
|
||||||
|
?>
|
||||||
|
<div style="font-family: sans-serif; line-height: 1.6;">
|
||||||
|
<h1 style="font-size: 1.5rem; margin-bottom: 0.5rem;"><?= esc($eventName) ?></h1>
|
||||||
|
<p style="margin:0.25rem 0;"><strong>Date:</strong> <?= esc($eventDate) ?></p>
|
||||||
|
<?php if ($eventCategory !== ''): ?>
|
||||||
|
<p style="margin:0.25rem 0;"><strong>Category:</strong> <?= esc($eventCategory) ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($amount !== null): ?>
|
||||||
|
<p style="margin:0.25rem 0;"><strong>Amount:</strong> $<?= esc($amount) ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (!empty($event['description'])): ?>
|
||||||
|
<p style="margin:0.75rem 0;"><?= nl2br(esc((string) $event['description'])) ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (!empty($flyerUrl)): ?>
|
||||||
|
<div style="margin:1rem 0;">
|
||||||
|
<img
|
||||||
|
src="<?= esc((string) $flyerUrl) ?>"
|
||||||
|
alt="Event flyer for <?= esc($eventName) ?>"
|
||||||
|
style="display:block; max-width:100%; height:auto; border:1px solid #ddd; border-radius:8px;"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<p style="margin:0.75rem 0;">
|
||||||
|
Flyer:
|
||||||
|
<a href="<?= esc((string) $flyerUrl) ?>"><?= esc((string) $flyerUrl) ?></a>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<p style="margin:0.75rem 0;">For more details, please contact the school office or review the parent event page.</p>
|
||||||
|
<p style="margin:0.25rem 0;">
|
||||||
|
<a href="<?= esc((string) $eventUrl) ?>"><?= esc((string) $eventUrl) ?></a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user