studentModel = new StudentModel(); $this->familyModel = new FamilyModel(); $this->fsModel = new FamilyStudentModel(); $this->fgModel = new FamilyGuardianModel(); if (class_exists(FamilyCommPrefModel::class)) { $this->fcpModel = new FamilyCommPrefModel(); } $this->templateModel = new EmailTemplateModel(); $this->logModel = new CommunicationLogModel(); } public function index() { $students = $this->studentModel->orderBy('lastname, firstname', 'asc')->findAll(); $templates = $this->templateModel->getActiveTemplates(); return view('communications/index', compact('students','templates')); } // AJAX: families for a student public function families(int $studentId) { if (!$this->request->isAJAX()) return $this->response->setStatusCode(400)->setJSON(['error' => 'Bad request']); $families = $this->fsModel->getFamiliesForStudent($studentId); return $this->response->setJSON(['data' => $families]); } // AJAX: guardians for a family public function guardians(int $familyId) { if (!$this->request->isAJAX()) return $this->response->setStatusCode(400)->setJSON(['error' => 'Bad request']); $db = \Config\Database::connect(); $rows = $db->query("SELECT u.id as user_id, u.firstname, u.lastname, u.email, fg.relation, fg.is_primary, fg.receive_emails, fg.receive_sms\n FROM family_guardians fg\n JOIN users u ON u.id = fg.user_id\n WHERE fg.family_id = ?", [$familyId])->getResultArray(); return $this->response->setJSON(['data' => $rows]); } public function preview() { if (!$this->request->isAJAX()) return $this->response->setStatusCode(400)->setJSON(['error' => 'Bad request']); $templateKey = (string) $this->request->getPost('template_key'); $studentId = (int) $this->request->getPost('student_id'); $familyId = (int) $this->request->getPost('family_id'); $varsPost = $this->request->getPost('vars'); $vars = is_array($varsPost) ? $varsPost : json_decode((string)$varsPost, true) ?? []; $template = $this->templateModel->findByKey($templateKey); if (!$template) return $this->response->setStatusCode(404)->setJSON(['error' => 'Template not found']); $student = $this->studentModel->getStudentBasic($studentId); if (!$student) return $this->response->setStatusCode(404)->setJSON(['error' => 'Student not found']); // Build salutation from guardians in the chosen family $db = \Config\Database::connect(); $gs = $db->query("SELECT u.firstname, u.lastname\n FROM family_guardians fg\n JOIN users u ON u.id = fg.user_id\n WHERE fg.family_id = ? AND fg.receive_emails = 1\n ORDER BY fg.is_primary DESC, u.lastname, u.firstname", [$familyId])->getResultArray(); $sal = 'Parent/Guardian'; if ($gs) { $names = array_map(fn($r)=> trim(($r['firstname']??'').' '.($r['lastname']??'')), $gs); $sal = implode(' & ', $names); } $autoVars = [ 'student_fullname' => trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')), 'student_grade' => $student['grade'] ?? '', 'parent_salutation'=> $sal, 'date' => local_date(utc_now(), 'Y-m-d'), 'school_name' => 'Al Rahma Sunday School', 'teacher_name' => (string)(session('display_name') ?? 'Teacher'), ]; $all = array_merge($autoVars, $vars); $subject = $this->renderTwig($template['subject'], $all); $body = nl2br($this->renderTwig($template['body'], $all)); return $this->response->setJSON(['subject' => $subject, 'html' => $body]); } public function send() { $rules = [ 'student_id' => 'required|integer', 'family_id' => 'required|integer', 'template_key' => 'required|string', 'subject' => 'required|string', 'body' => 'required|string', 'recipients' => 'required|string' // JSON array ]; if (!$this->validate($rules)) { return redirect()->back()->with('error', 'Invalid form submission.'); } $studentId = (int) $this->request->getPost('student_id'); $familyId = (int) $this->request->getPost('family_id'); $templateKey = (string) $this->request->getPost('template_key'); $subject = (string) $this->request->getPost('subject'); $bodyHtml = (string) $this->request->getPost('body'); $recipients = json_decode((string)$this->request->getPost('recipients'), true) ?? []; $cc = json_decode((string)($this->request->getPost('cc') ?? '[]'), true) ?? []; $bcc = json_decode((string)($this->request->getPost('bcc') ?? '[]'), true) ?? []; $student = $this->studentModel->getStudentBasic($studentId); if (!$student) return redirect()->back()->with('error', 'Student not found'); // Send email (PHPMailer via service('mailer')) $sendOk = false; $error = null; try { $mailer = service('mailer'); foreach (array_unique($recipients) as $to) { if ($to) $mailer->addAddress($to); } foreach (array_unique($cc) as $c) { if ($c) $mailer->addCC($c); } foreach (array_unique($bcc) as $b){ if ($b) $mailer->addBCC($b); } $mailer->Subject = $subject; $mailer->isHTML(true); $mailer->Body = $bodyHtml; $mailer->AltBody = strip_tags($bodyHtml); $sendOk = $mailer->send(); } catch (\Throwable $t) { $error = $t->getMessage(); } // Log $this->logModel->insert([ 'student_id' => $studentId, 'family_id' => $familyId, 'student_name' => trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')), 'template_key' => $templateKey, 'subject' => $subject, 'body' => $bodyHtml, 'recipients' => json_encode(array_values($recipients)), 'cc' => json_encode(array_values($cc)), 'bcc' => json_encode(array_values($bcc)), 'status' => $sendOk ? 'sent' : 'failed', 'error_message'=> $error, 'sent_by' => (int)(session('user_id') ?? 0), 'metadata' => null, ]); return $sendOk ? redirect()->to('/communications')->with('success', 'Email sent successfully.') : redirect()->back()->with('error', 'Failed to send email: '.($error ?? 'Unknown error')); } private function renderTwig(string $template, array $vars): string { return preg_replace_callback('/\{\{\s*([a-zA-Z0-9_\.]+)\s*\}\}/', function($m) use ($vars) { $key = $m[1]; return htmlspecialchars((string)($vars[$key] ?? ''), ENT_QUOTES, 'UTF-8'); }, $template); } }