(string) ($parent['parent_name'] ?? 'Parent/Guardian'), '{{student_name}}' => $studentName, '{{incident_date}}' => $incident, '{{school_phone}}' => '978-364-0219', '{{voicemail_phone}}' => (string) ($parent['phone'] ?? '—'), '{{class_time}}' => '10:00 AM', ]; } public function renderTemplate(string $code, string $variant, array $context): ?array { $row = null; if (method_exists($this->attendanceEmailTemplateModel, 'getTemplate')) { $row = $this->attendanceEmailTemplateModel->getTemplate($code, $variant) ?: $this->attendanceEmailTemplateModel->getTemplate($code, 'default'); } else { $row = $this->attendanceEmailTemplateModel->query() ->where('code', $code) ->where('variant', $variant) ->where('is_active', 1) ->first(); if (!$row) { $row = $this->attendanceEmailTemplateModel->query() ->where('code', $code) ->where('variant', 'default') ->where('is_active', 1) ->first(); } $row = $row?->toArray(); } if (!$row) { return null; } $subject = strtr($row['subject'], $context); $body = strtr($row['body_html'], $context); return [$subject, $body]; } public function renderWithEmailLayout(string $subject, string $bodyHtml): string { return MailHtml::document($subject, $bodyHtml); } public function pickVariant(string $code, ?string $globalVariantOverride = null): string { if (!empty($globalVariantOverride)) { return $globalVariantOverride; } return match ($code) { 'ABS_2', 'ABS_2_IN3W' => 'no_answer', default => 'default', }; } public function buildFallbackAutoEmail(string $code, array $violation, string $parentName = ''): array { $subject = match ($code) { 'ABS_1' => 'Attendance Notice: Unreported Absence', 'LATE_2' => 'Attendance Notice: Repeated Lateness', default => 'Attendance Notice', }; $studentName = (string) ($violation['name'] ?? ''); $date = (string) ($violation['last_date'] ?? date('Y-m-d')); $body = "

Dear " . ($parentName ?: 'Parent/Guardian') . "

" . "

We'd like to inform you about your student's recent attendance:

" . "" . "

If you have any questions, please contact the school office.

"; return [$subject, $body]; } public function normalizeBodyToHtml(string $bodyInput): string { if ($bodyInput !== '' && preg_match('/<[^>]+>/', $bodyInput)) { return $bodyInput; } if ($bodyInput !== '' && stripos($bodyInput, '<') !== false) { $decoded = html_entity_decode($bodyInput, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); if (preg_match('/<[^>]+>/', $decoded)) { return $decoded; } } return nl2br(e($bodyInput), false); } public function plainTextToHtml(string $text): string { $text = preg_replace("/\r\n?/", "\n", trim($text)); if ($text === '') { return '

'; } $escaped = htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); $escaped = preg_replace_callback( '#\bhttps?://[^\s<]+#i', static function ($m) { $url = $m[0]; $urlAttr = htmlspecialchars($url, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); return '' . $url . ''; }, $escaped ); $paragraphs = preg_split("/\n{2,}/", $escaped); $paragraphs = array_map(static function ($p) { $p = preg_replace("/\n/", "
", $p); return '

' . $p . '

'; }, $paragraphs); return implode("\n", $paragraphs); } public function renderAutoEmailFor(array $violation, ?array $parent = null): ?array { $code = (string) ($violation['violation_code'] ?? $violation['code'] ?? ''); $ctx = $this->buildTemplateContext($violation, $parent); $tpl = $this->renderTemplate($code, 'default', $ctx); if (!$tpl) { return $this->buildFallbackAutoEmail($code, $violation, (string) ($parent['parent_name'] ?? '')); } return $tpl; } public function composePreview( int $studentId, array $student, ?array $primaryParent, ?array $secondaryParent, string $code, string $variant, ?string $incidentDate = null ): array { $lastDate = preg_match('/^\d{4}-\d{2}-\d{2}$/', (string) $incidentDate) ? $incidentDate : date('Y-m-d'); $violation = [ 'id' => $studentId, 'name' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')), 'last_date' => $lastDate, 'violation' => $code, 'violation_code' => $code, ]; $ctx = $this->buildTemplateContext($violation, $primaryParent); $rendered = $this->renderTemplate($code, $variant, $ctx); if (!$rendered) { return [ 'success' => false, 'message' => "Template not found for {$code} ({$variant}).", 'status' => 404, ]; } [$subject, $body] = $rendered; return [ 'success' => true, 'data' => [ 'student_id' => $studentId, 'student_name' => $violation['name'], 'parent_email' => (string) ($primaryParent['email'] ?? ''), 'parent_name' => (string) ($primaryParent['parent_name'] ?? ''), 'secondary_email' => (string) ($secondaryParent['email'] ?? ''), 'secondary_name' => (string) ($secondaryParent['parent_name'] ?? ''), 'code' => $code, 'variant' => $variant, 'subject' => $subject, 'body_html' => $body, 'incident_date' => $lastDate, ], ]; } }