add controllers, servoices

This commit is contained in:
root
2026-03-09 02:52:13 -04:00
parent c8de5f7edc
commit d76c871cb7
501 changed files with 34439 additions and 21843 deletions
@@ -0,0 +1,45 @@
<?php
namespace App\Services\BroadcastEmail;
use Illuminate\Support\Facades\View;
class BroadcastEmailComposerService
{
public function sanitizeHtml(string $html): string
{
$html = preg_replace('#<(script|iframe)[^>]*>.*?</\\1>#is', '', $html);
$html = preg_replace('/\\son\\w+="[^"]*"/i', '', $html);
$html = preg_replace("/\\son\\w+='[^']*'/i", '', $html);
return $html ?? '';
}
public function compose(
bool $wrapLayout,
string $subject,
string $body,
string $recipientName,
string $preheader = '',
string $ctaText = '',
string $ctaUrl = '',
bool $personalize = true
): string {
$content = $personalize ? str_replace('{{name}}', $recipientName, $body) : $body;
if (!$wrapLayout) {
return $content;
}
if (!View::exists('emails.broadcast_wrapper')) {
return $content;
}
return view('emails.broadcast_wrapper', [
'subject' => $subject,
'content' => $content,
'preheader' => $preheader,
'cta_text' => $ctaText,
'cta_url' => $ctaUrl,
])->render();
}
}
@@ -0,0 +1,81 @@
<?php
namespace App\Services\BroadcastEmail;
use App\Services\EmailService;
class BroadcastEmailDispatchService
{
private EmailService $mailer;
private BroadcastEmailComposerService $composer;
public function __construct(EmailService $mailer, BroadcastEmailComposerService $composer)
{
$this->mailer = $mailer;
$this->composer = $composer;
}
public function sendTest(array $payload): array
{
$html = $this->composer->compose(
(bool) $payload['wrap_layout'],
$payload['subject'],
$payload['body_html'],
$payload['recipient_name'],
$payload['preheader'],
$payload['cta_text'],
$payload['cta_url'],
(bool) $payload['personalize']
);
$ok = $this->mailer->send(
$payload['test_email'],
'[TEST] ' . $payload['subject'],
$html,
$payload['from_key']
);
return [
'ok' => $ok,
'message' => $ok
? "Test email sent to {$payload['test_email']}."
: 'Test email failed (mailer->send() returned false). Check logs.',
];
}
public function sendBroadcast(array $payload, array $recipients): array
{
$stats = [
'attempted' => 0,
'sent' => 0,
'failed' => 0,
'mode' => $payload['mode'],
];
foreach ($recipients as $recipient) {
$stats['attempted']++;
$recipientName = $recipient['name'] ?: 'Parent';
$html = $this->composer->compose(
(bool) $payload['wrap_layout'],
$payload['subject'],
$payload['body_html'],
$recipientName,
$payload['preheader'],
$payload['cta_text'],
$payload['cta_url'],
(bool) $payload['personalize']
);
$ok = $this->mailer->send(
(string) $recipient['email'],
$payload['subject'],
$html,
$payload['from_key']
);
$ok ? $stats['sent']++ : $stats['failed']++;
}
return $stats;
}
}
@@ -0,0 +1,51 @@
<?php
namespace App\Services\BroadcastEmail;
use Illuminate\Http\UploadedFile;
class BroadcastEmailImageService
{
private const MAX_SIZE_BYTES = 5242880;
private array $allowedTypes = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'image/gif' => 'gif',
'image/webp' => 'webp',
];
public function store(UploadedFile $file): array
{
$mime = strtolower((string) $file->getMimeType());
if (!isset($this->allowedTypes[$mime])) {
return [
'ok' => false,
'status' => 415,
'error' => 'Unsupported image type',
];
}
if ($file->getSize() > self::MAX_SIZE_BYTES) {
return [
'ok' => false,
'status' => 413,
'error' => 'Image too large (max 5MB)',
];
}
$targetDir = public_path('uploads/email');
if (!is_dir($targetDir)) {
@mkdir($targetDir, 0755, true);
}
$ext = $this->allowedTypes[$mime];
$newName = uniqid('em_', true) . '.' . $ext;
$file->move($targetDir, $newName);
return [
'ok' => true,
'url' => asset('uploads/email/' . $newName),
];
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Services\BroadcastEmail;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class BroadcastEmailRecipientService
{
public function parentsWithEmails(): array
{
$parents = User::getParents();
return array_values(array_filter($parents, static function ($parent) {
$email = $parent['email'] ?? '';
return is_string($email) && trim($email) !== '';
}));
}
public function recipientsByIds(array $ids): array
{
$ids = array_values(array_unique(array_filter(array_map('intval', $ids))));
if (empty($ids)) {
return [];
}
return DB::table('users')
->select('users.id', 'users.email', DB::raw('CONCAT(users.firstname, " ", users.lastname) AS name'))
->whereIn('users.id', $ids)
->whereNotNull('users.email')
->where('users.email', '!=', '')
->get()
->map(fn ($row) => (array) $row)
->all();
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Services\BroadcastEmail;
class BroadcastEmailSenderOptionsService
{
public function listOptions(): array
{
$json = env('MAIL_SENDERS', '{}');
$arr = json_decode($json, true);
if (!is_array($arr) || empty($arr)) {
$smtpUser = getenv('SMTP_USER') ?: '';
$name = 'Al Rahma Sunday School';
return [[
'key' => 'general',
'label' => $name . ($smtpUser ? " <{$smtpUser}>" : ''),
]];
}
$out = [];
foreach ($arr as $key => $info) {
$name = $info['name'] ?? 'Sender';
$email = $info['email'] ?? '';
$out[] = [
'key' => (string) $key,
'label' => trim($name . ($email ? " <{$email}>" : '')),
];
}
return $out;
}
}