add projet
This commit is contained in:
+244
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Services\EmailService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class BroadcastEmailController extends BaseApiController
|
||||
{
|
||||
protected User $user;
|
||||
protected EmailService $mailer;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->user = model(User::class);
|
||||
$this->mailer = app(EmailService::class);
|
||||
}
|
||||
|
||||
public function metadata(): JsonResponse
|
||||
{
|
||||
$parents = array_values(array_filter(
|
||||
$this->user->getParents(),
|
||||
static fn ($parent) => !empty($parent['email'])
|
||||
));
|
||||
|
||||
return $this->success([
|
||||
'parents' => $parents,
|
||||
'fromOptions' => $this->senderOptionsFromEnv(),
|
||||
], 'Broadcast email metadata retrieved');
|
||||
}
|
||||
|
||||
public function send(): JsonResponse
|
||||
{
|
||||
$data = $this->payloadData();
|
||||
$rules = [
|
||||
'subject' => 'required|string',
|
||||
'body_html' => 'required|string',
|
||||
'mode' => 'permit_empty|string',
|
||||
'from_key' => 'permit_empty|string',
|
||||
'wrap_layout'=> 'permit_empty',
|
||||
'preheader' => 'permit_empty|string',
|
||||
'cta_text' => 'permit_empty|string',
|
||||
'cta_url' => 'permit_empty|string',
|
||||
];
|
||||
|
||||
$errors = $this->validateRequest($data, $rules);
|
||||
if (!empty($errors)) {
|
||||
return $this->respondValidationError($errors);
|
||||
}
|
||||
|
||||
$isTest = !empty($data['send_test_only']);
|
||||
$mode = (string) ($data['mode'] ?? 'standard');
|
||||
$subject = trim((string) $data['subject']);
|
||||
$fromKey = trim((string) ($data['from_key'] ?? 'general'));
|
||||
$wrapLayout = !empty($data['wrap_layout']);
|
||||
$preheader = (string) ($data['preheader'] ?? '');
|
||||
$ctaText = (string) ($data['cta_text'] ?? '');
|
||||
$ctaUrl = (string) ($data['cta_url'] ?? '');
|
||||
$body = $this->sanitizeEmailHtml((string) $data['body_html']);
|
||||
|
||||
if ($subject === '' || $body === '') {
|
||||
return $this->respondError('Subject and body are required.', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
if ($isTest) {
|
||||
$testEmail = trim((string) ($data['test_email'] ?? ''));
|
||||
if ($testEmail === '' || !filter_var($testEmail, FILTER_VALIDATE_EMAIL)) {
|
||||
return $this->respondError('Provide a valid test email address.', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$html = $this->composeEmailHtml(
|
||||
$wrapLayout,
|
||||
$subject,
|
||||
$body,
|
||||
'Parent',
|
||||
$preheader,
|
||||
$ctaText,
|
||||
$ctaUrl,
|
||||
$mode === 'personalized'
|
||||
);
|
||||
|
||||
$ok = $this->mailer->send($testEmail, '[TEST] ' . $subject, $html, $fromKey);
|
||||
return $ok
|
||||
? $this->success(['message' => "Test email sent to {$testEmail}."], 'Test email sent')
|
||||
: $this->respondError('Test email failed to send. Check logs.', Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$parentIds = $this->parseParentIds($data['parent_ids'] ?? []);
|
||||
if (empty($parentIds)) {
|
||||
return $this->respondError('Please select at least one parent.', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$rows = $this->user->newQuery()
|
||||
->select('users.id', 'users.email', 'users.firstname', 'users.lastname')
|
||||
->whereIn('users.id', $parentIds)
|
||||
->whereNotNull('users.email')
|
||||
->get();
|
||||
|
||||
if ($rows->isEmpty()) {
|
||||
return $this->respondError('No valid parent emails found.', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$stats = ['attempted' => 0, 'sent' => 0, 'failed' => 0, 'mode' => $mode];
|
||||
$personalized = ($mode === 'personalized');
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$stats['attempted']++;
|
||||
$recipientName = trim(($row->firstname ?? '') . ' ' . ($row->lastname ?? '')) ?: 'Parent';
|
||||
$html = $this->composeEmailHtml(
|
||||
$wrapLayout,
|
||||
$subject,
|
||||
$body,
|
||||
$recipientName,
|
||||
$preheader,
|
||||
$ctaText,
|
||||
$ctaUrl,
|
||||
$personalized
|
||||
);
|
||||
|
||||
$ok = $this->mailer->send($row->email, $subject, $html, $fromKey);
|
||||
$ok ? $stats['sent']++ : $stats['failed']++;
|
||||
}
|
||||
|
||||
$message = "Broadcast finished. Mode: {$stats['mode']}. Sent: {$stats['sent']}/{$stats['attempted']}. Failures: {$stats['failed']}.";
|
||||
$status = $stats['failed'] > 0 ? Response::HTTP_OK : Response::HTTP_OK;
|
||||
|
||||
return $this->success(array_merge($stats, ['message' => $message]), 'Broadcast completed', $status);
|
||||
}
|
||||
|
||||
public function uploadImage(): JsonResponse
|
||||
{
|
||||
if (!$this->request->is('post')) {
|
||||
return $this->respondError('Method not allowed', Response::HTTP_METHOD_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
$file = request()->file('image');
|
||||
if (!$file || !$file->isValid()) {
|
||||
return $this->respondError('No image uploaded.', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$mime = strtolower((string) $file->getMimeType());
|
||||
$allowed = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/gif' => 'gif', 'image/webp' => 'webp'];
|
||||
if (!isset($allowed[$mime])) {
|
||||
return $this->respondError('Unsupported image type.', Response::HTTP_UNSUPPORTED_MEDIA_TYPE);
|
||||
}
|
||||
if ($file->getSize() > 5 * 1024 * 1024) {
|
||||
return $this->respondError('Image too large (max 5MB).', Response::HTTP_REQUEST_ENTITY_TOO_LARGE);
|
||||
}
|
||||
|
||||
$targetDir = public_path('uploads/email');
|
||||
if (!is_dir($targetDir)) {
|
||||
@mkdir($targetDir, 0755, true);
|
||||
}
|
||||
|
||||
$newName = uniqid('em_', true) . '.' . $allowed[$mime];
|
||||
$file->move($targetDir, $newName);
|
||||
|
||||
$url = url('uploads/email/' . $newName);
|
||||
|
||||
return $this->success([
|
||||
'success' => true,
|
||||
'url' => $url,
|
||||
], 'Image uploaded successfully');
|
||||
}
|
||||
|
||||
private function composeEmailHtml(
|
||||
bool $wrap,
|
||||
string $subject,
|
||||
string $body,
|
||||
string $recipientName,
|
||||
string $preheader,
|
||||
string $ctaText,
|
||||
string $ctaUrl,
|
||||
bool $doPersonalize
|
||||
): string {
|
||||
$content = $doPersonalize ? str_replace('{{name}}', $recipientName, $body) : $body;
|
||||
|
||||
if (!$wrap) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
try {
|
||||
return view('emails/broadcast_wrapper', [
|
||||
'subject' => $subject,
|
||||
'content' => $content,
|
||||
'preheader' => $preheader,
|
||||
'cta_text' => $ctaText,
|
||||
'cta_url' => $ctaUrl,
|
||||
], ['saveData' => false]);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('broadcast_wrapper view missing: ' . $e->getMessage());
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
||||
private function sanitizeEmailHtml(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;
|
||||
}
|
||||
|
||||
private function senderOptionsFromEnv(): array
|
||||
{
|
||||
$json = env('MAIL_SENDERS', '{}');
|
||||
$arr = json_decode($json, true);
|
||||
if (!is_array($arr) || empty($arr)) {
|
||||
$smtpUser = env('SMTP_USER', '');
|
||||
$name = 'Al Rahma Sunday School';
|
||||
return [[
|
||||
'key' => 'general',
|
||||
'label' => trim($name . ($smtpUser ? " <{$smtpUser}>" : '')),
|
||||
]];
|
||||
}
|
||||
|
||||
$out = [];
|
||||
foreach ($arr as $key => $info) {
|
||||
$nm = $info['name'] ?? 'Sender';
|
||||
$em = $info['email'] ?? '';
|
||||
$out[] = ['key' => (string) $key, 'label' => trim($nm . ($em ? " <{$em}>" : ''))];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
private function parseParentIds($raw): array
|
||||
{
|
||||
$raw = is_array($raw) ? $raw : [$raw];
|
||||
$ids = [];
|
||||
foreach ($raw as $value) {
|
||||
if (is_string($value) && str_contains($value, ',')) {
|
||||
$ids = array_merge($ids, array_map('intval', explode(',', $value)));
|
||||
} else {
|
||||
$ids[] = (int) $value;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique(array_filter($ids)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user