Decision now support auto mailing
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\EmailLog;
|
||||
use App\Models\EmailTemplate;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class EmailLogController extends Controller
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$this->authorize('viewAny', EmailTemplate::class); // reuse same permission gate for admin area
|
||||
|
||||
$query = EmailLog::query()
|
||||
->with(['template:id,name'])
|
||||
->orderByDesc('created_at');
|
||||
|
||||
$status = trim((string) $request->input('status', ''));
|
||||
if ($status !== '') {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
if ($email = trim((string) $request->input('to'))) {
|
||||
$query->where('to_email', 'like', '%'.str_replace(['%', '_'], ['\%', '\_'], $email).'%');
|
||||
}
|
||||
if ($subject = trim((string) $request->input('subject'))) {
|
||||
$query->where('subject', 'like', '%'.str_replace(['%', '_'], ['\%', '\_'], $subject).'%');
|
||||
}
|
||||
if ($templateId = (int) $request->input('template_id')) {
|
||||
$query->where('template_id', $templateId);
|
||||
}
|
||||
if ($from = $request->date('date_from')) {
|
||||
$query->whereDate('created_at', '>=', $from);
|
||||
}
|
||||
if ($to = $request->date('date_to')) {
|
||||
$query->whereDate('created_at', '<=', $to);
|
||||
}
|
||||
|
||||
$logs = $query->paginate(20)->withQueryString();
|
||||
$templates = EmailTemplate::query()->orderBy('name')->get(['id', 'name']);
|
||||
|
||||
return Inertia::render('Admin/EmailLogs/Index', [
|
||||
'logs' => $logs,
|
||||
'filters' => [
|
||||
'status' => $status,
|
||||
'to' => $email ?? '',
|
||||
'subject' => $subject ?? '',
|
||||
'template_id' => $templateId ?: null,
|
||||
'date_from' => $request->input('date_from'),
|
||||
'date_to' => $request->input('date_to'),
|
||||
],
|
||||
'templates' => $templates,
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(EmailLog $emailLog): Response
|
||||
{
|
||||
$this->authorize('viewAny', EmailTemplate::class);
|
||||
|
||||
$emailLog->load(['template:id,name', 'body']);
|
||||
|
||||
return Inertia::render('Admin/EmailLogs/Show', [
|
||||
'log' => $emailLog,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -4,26 +4,24 @@
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\StoreEmailTemplateRequest;
|
||||
use App\Http\Requests\UpdateEmailTemplateRequest;
|
||||
use App\Jobs\SendEmailTemplateJob;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientCase;
|
||||
use App\Models\Contract;
|
||||
use App\Models\Document;
|
||||
use App\Models\EmailLog;
|
||||
use App\Models\EmailLogStatus;
|
||||
use App\Models\EmailTemplate;
|
||||
use App\Models\MailProfile;
|
||||
use App\Models\Person\Person;
|
||||
use App\Services\EmailTemplateRenderer;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
use Symfony\Component\Mailer\Mailer as SymfonyMailer;
|
||||
use Symfony\Component\Mailer\Transport;
|
||||
use Symfony\Component\Mime\Address;
|
||||
use Symfony\Component\Mime\Email;
|
||||
use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles;
|
||||
|
||||
@@ -31,6 +29,19 @@ class EmailTemplateController extends Controller
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public function update(\App\Http\Requests\UpdateEmailTemplateRequest $request, EmailTemplate $emailTemplate)
|
||||
{
|
||||
$this->authorize('update', $emailTemplate);
|
||||
$data = $request->validated();
|
||||
$emailTemplate->fill($data)->save();
|
||||
// Move any tmp images referenced in HTML into permanent storage and attach as documents
|
||||
$this->adoptTmpImages($emailTemplate);
|
||||
|
||||
return redirect()->route('admin.email-templates.edit', $emailTemplate)->with('success', 'Template updated');
|
||||
}
|
||||
|
||||
use AuthorizesRequests;
|
||||
|
||||
public function index(): Response
|
||||
{
|
||||
$this->authorize('viewAny', EmailTemplate::class);
|
||||
@@ -59,126 +70,45 @@ public function store(StoreEmailTemplateRequest $request)
|
||||
return redirect()->route('admin.email-templates.edit', $tpl)->with('success', 'Template created');
|
||||
}
|
||||
|
||||
public function edit(EmailTemplate $emailTemplate): Response
|
||||
{
|
||||
$this->authorize('update', $emailTemplate);
|
||||
$emailTemplate->load(['documents' => function ($q) {
|
||||
$q->select(['id', 'documentable_id', 'documentable_type', 'name', 'path', 'size', 'created_at']);
|
||||
}]);
|
||||
|
||||
return Inertia::render('Admin/EmailTemplates/Edit', [
|
||||
'template' => $emailTemplate,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(UpdateEmailTemplateRequest $request, EmailTemplate $emailTemplate)
|
||||
{
|
||||
$data = $request->validated();
|
||||
$emailTemplate->update($data);
|
||||
$this->adoptTmpImages($emailTemplate);
|
||||
|
||||
return back()->with('success', 'Template updated');
|
||||
}
|
||||
|
||||
public function destroy(EmailTemplate $emailTemplate)
|
||||
{
|
||||
$this->authorize('delete', $emailTemplate);
|
||||
$emailTemplate->delete();
|
||||
|
||||
return redirect()->route('admin.email-templates.index')->with('success', 'Template deleted');
|
||||
}
|
||||
|
||||
public function preview(Request $request, EmailTemplate $emailTemplate)
|
||||
/**
|
||||
* Render a quick preview of the email template with the provided context.
|
||||
* Does not persist any changes or inline CSS; intended for fast editor feedback.
|
||||
*/
|
||||
public function preview(Request $request, EmailTemplate $emailTemplate): JsonResponse
|
||||
{
|
||||
$this->authorize('view', $emailTemplate);
|
||||
|
||||
$renderer = app(EmailTemplateRenderer::class);
|
||||
$subject = (string) $request->input('subject', $emailTemplate->subject_template);
|
||||
$html = (string) $request->input('html', $emailTemplate->html_template);
|
||||
$text = (string) $request->input('text', $emailTemplate->text_template);
|
||||
|
||||
// Resolve sample entities by ID if given
|
||||
$ctx = [];
|
||||
// Prefer contract -> case -> client for deriving person
|
||||
if ($id = $request->integer('contract_id')) {
|
||||
$contract = Contract::query()->with(['clientCase.client.person'])->find($id);
|
||||
if ($contract) {
|
||||
$ctx['contract'] = $contract;
|
||||
if ($contract->clientCase) {
|
||||
$ctx['client_case'] = $contract->clientCase;
|
||||
if ($contract->clientCase->client) {
|
||||
$ctx['client'] = $contract->clientCase->client;
|
||||
$ctx['person'] = optional($contract->clientCase->client)->person;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! isset($ctx['client_case']) && ($id = $request->integer('case_id'))) {
|
||||
$case = ClientCase::query()->with(['client.person'])->find($id);
|
||||
if ($case) {
|
||||
$ctx['client_case'] = $case;
|
||||
if ($case->client) {
|
||||
$ctx['client'] = $case->client;
|
||||
$ctx['person'] = optional($case->client)->person;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! isset($ctx['client']) && ($id = $request->integer('client_id'))) {
|
||||
$client = Client::query()->with(['person'])->find($id);
|
||||
if ($client) {
|
||||
$ctx['client'] = $client;
|
||||
$ctx['person'] = optional($client)->person;
|
||||
}
|
||||
}
|
||||
$ctx['extra'] = (array) $request->input('extra', []);
|
||||
|
||||
$result = $renderer->render([
|
||||
'subject' => $subject,
|
||||
'html' => $html,
|
||||
'text' => $text,
|
||||
], $ctx);
|
||||
|
||||
// Repair and attach images, then embed as requested
|
||||
if (! empty($result['html'])) {
|
||||
$result['html'] = $this->repairImgWithoutSrc($result['html']);
|
||||
$result['html'] = $this->attachSrcFromTemplateDocuments($emailTemplate, $result['html']);
|
||||
|
||||
$embed = (string) $request->input('embed', 'base64'); // hosted | base64
|
||||
if ($embed === 'base64') {
|
||||
try {
|
||||
$imageInliner = app(\App\Services\EmailImageInliner::class);
|
||||
$result['html'] = $imageInliner->inline($result['html']);
|
||||
} catch (\Throwable $e) {
|
||||
// ignore preview image inlining errors
|
||||
}
|
||||
} else {
|
||||
$result['html'] = $this->absolutizeStorageUrls($request, $result['html']);
|
||||
}
|
||||
|
||||
try {
|
||||
$inliner = new CssToInlineStyles;
|
||||
$result['html'] = $inliner->convert($result['html']);
|
||||
} catch (\Throwable $e) {
|
||||
// ignore preview inlining errors
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json($result);
|
||||
}
|
||||
|
||||
public function sendTest(Request $request, EmailTemplate $emailTemplate)
|
||||
{
|
||||
$this->authorize('send', $emailTemplate);
|
||||
|
||||
$renderer = app(EmailTemplateRenderer::class);
|
||||
$subject = (string) ($request->input('subject') ?? $emailTemplate->subject_template);
|
||||
$html = (string) ($request->input('html') ?? $emailTemplate->html_template);
|
||||
$text = (string) ($request->input('text') ?? $emailTemplate->text_template);
|
||||
|
||||
// Adopt tmp images (tmp/email-images) so test email can display images; also persist
|
||||
$html = $this->adoptTmpImagesInHtml($emailTemplate, $html, true);
|
||||
// Do not persist tmp images for preview, but allow showing them if already accessible
|
||||
// Optionally repair missing img src and attach from template documents for a better preview
|
||||
if (! empty($html)) {
|
||||
$html = $this->repairImgWithoutSrc($html);
|
||||
$html = $this->attachSrcFromTemplateDocuments($emailTemplate, $html);
|
||||
}
|
||||
|
||||
// Context resolution (shared logic with renderFinalHtml)
|
||||
$ctx = [];
|
||||
if ($id = $request->integer('activity_id')) {
|
||||
$activity = Activity::query()->with(['action', 'decision', 'contract.clientCase.client.person', 'clientCase.client.person'])->find($id);
|
||||
if ($activity) {
|
||||
$ctx['activity'] = $activity;
|
||||
// Derive base entities from activity when not explicitly provided
|
||||
if ($activity->contract && ! isset($ctx['contract'])) {
|
||||
$ctx['contract'] = $activity->contract;
|
||||
}
|
||||
if ($activity->clientCase && ! isset($ctx['client_case'])) {
|
||||
$ctx['client_case'] = $activity->clientCase;
|
||||
}
|
||||
if (($ctx['client_case'] ?? null) && ! isset($ctx['client'])) {
|
||||
$ctx['client'] = $ctx['client_case']->client;
|
||||
$ctx['person'] = optional($ctx['client'])->person;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($id = $request->integer('contract_id')) {
|
||||
$contract = Contract::query()->with(['clientCase.client.person'])->find($id);
|
||||
if ($contract) {
|
||||
@@ -217,107 +147,131 @@ public function sendTest(Request $request, EmailTemplate $emailTemplate)
|
||||
'text' => $text,
|
||||
], $ctx);
|
||||
|
||||
return response()->json([
|
||||
'subject' => $rendered['subject'] ?? $subject,
|
||||
'html' => (string) ($rendered['html'] ?? $html ?? ''),
|
||||
'text' => (string) ($rendered['text'] ?? $text ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(EmailTemplate $emailTemplate): Response
|
||||
{
|
||||
$this->authorize('update', $emailTemplate);
|
||||
$emailTemplate->load(['documents' => function ($q) {
|
||||
$q->select(['id', 'documentable_id', 'documentable_type', 'name', 'path', 'size', 'created_at']);
|
||||
}]);
|
||||
|
||||
return Inertia::render('Admin/EmailTemplates/Edit', [
|
||||
'template' => $emailTemplate,
|
||||
]);
|
||||
}
|
||||
|
||||
public function sendTest(Request $request, EmailTemplate $emailTemplate)
|
||||
{
|
||||
$this->authorize('send', $emailTemplate);
|
||||
|
||||
$renderer = app(EmailTemplateRenderer::class);
|
||||
$subject = (string) ($request->input('subject') ?? $emailTemplate->subject_template);
|
||||
$html = (string) ($request->input('html') ?? $emailTemplate->html_template);
|
||||
$text = (string) ($request->input('text') ?? $emailTemplate->text_template);
|
||||
|
||||
// Adopt tmp images (tmp/email-images) so test email can display images; also persist
|
||||
$html = $this->adoptTmpImagesInHtml($emailTemplate, $html, true);
|
||||
|
||||
// Context resolution
|
||||
$ctx = [];
|
||||
if ($id = $request->integer('activity_id')) {
|
||||
$activity = Activity::query()->with(['action', 'decision', 'contract.clientCase.client.person', 'clientCase.client.person'])->find($id);
|
||||
if ($activity) {
|
||||
$ctx['activity'] = $activity;
|
||||
if ($activity->contract && ! isset($ctx['contract'])) {
|
||||
$ctx['contract'] = $activity->contract;
|
||||
}
|
||||
if ($activity->clientCase && ! isset($ctx['client_case'])) {
|
||||
$ctx['client_case'] = $activity->clientCase;
|
||||
}
|
||||
if (($ctx['client_case'] ?? null) && ! isset($ctx['client'])) {
|
||||
$ctx['client'] = $ctx['client_case']->client;
|
||||
$ctx['person'] = optional($ctx['client'])->person;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($id = $request->integer('contract_id')) {
|
||||
$contract = Contract::query()->with(['clientCase.client.person'])->find($id);
|
||||
if ($contract) {
|
||||
$ctx['contract'] = $contract;
|
||||
if ($contract->clientCase) {
|
||||
$ctx['client_case'] = $contract->clientCase;
|
||||
if ($contract->clientCase->client) {
|
||||
$ctx['client'] = $contract->clientCase->client;
|
||||
$ctx['person'] = optional($contract->clientCase->client)->person;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! isset($ctx['client_case']) && ($id = $request->integer('case_id'))) {
|
||||
$case = ClientCase::query()->with(['client.person'])->find($id);
|
||||
if ($case) {
|
||||
$ctx['client_case'] = $case;
|
||||
if ($case->client) {
|
||||
$ctx['client'] = $case->client;
|
||||
$ctx['person'] = optional($case->client)->person;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! isset($ctx['client']) && ($id = $request->integer('client_id'))) {
|
||||
$client = Client::query()->with(['person'])->find($id);
|
||||
if ($client) {
|
||||
$ctx['client'] = $client;
|
||||
$ctx['person'] = optional($client)->person;
|
||||
}
|
||||
}
|
||||
$ctx['extra'] = (array) $request->input('extra', []);
|
||||
|
||||
// Render preview values; we store a minimal snapshot on the log
|
||||
$rendered = $renderer->render([
|
||||
'subject' => $subject,
|
||||
'html' => $html,
|
||||
'text' => $text,
|
||||
], $ctx);
|
||||
|
||||
$to = (string) $request->input('to');
|
||||
if ($to === '' || ! filter_var($to, FILTER_VALIDATE_EMAIL)) {
|
||||
return back()->with('error', 'Invalid target email');
|
||||
}
|
||||
|
||||
// First repair images missing src if they are followed by a URL (editor artifact)
|
||||
if (! empty($rendered['html'])) {
|
||||
$rendered['html'] = $this->repairImgWithoutSrc($rendered['html']);
|
||||
$rendered['html'] = $this->attachSrcFromTemplateDocuments($emailTemplate, $rendered['html']);
|
||||
}
|
||||
// Embed images as requested (default hosted for Gmail compatibility)
|
||||
$htmlForSend = $rendered['html'] ?? '';
|
||||
$embed = (string) $request->input('embed', 'base64');
|
||||
// Prepare EmailLog record with queued status
|
||||
$log = new EmailLog;
|
||||
$log->fill([
|
||||
'uuid' => (string) \Str::uuid(),
|
||||
'template_id' => $emailTemplate->id,
|
||||
'to_email' => $to,
|
||||
'to_name' => null,
|
||||
'subject' => (string) ($rendered['subject'] ?? $subject ?? ''),
|
||||
'body_html_hash' => $rendered['html'] ? hash('sha256', $rendered['html']) : null,
|
||||
'body_text_preview' => isset($rendered['text']) ? mb_strimwidth((string) $rendered['text'], 0, 4096) : null,
|
||||
'embed_mode' => (string) $request->input('embed', 'base64'),
|
||||
'status' => EmailLogStatus::Queued,
|
||||
'queued_at' => now(),
|
||||
'client_id' => $ctx['client']->id ?? null,
|
||||
'client_case_id' => $ctx['client_case']->id ?? null,
|
||||
'contract_id' => $ctx['contract']->id ?? null,
|
||||
'extra_context' => $ctx['extra'] ?? null,
|
||||
'ip' => $request->ip(),
|
||||
]);
|
||||
$log->save();
|
||||
|
||||
// Prefer the active Mail Profile for sending test emails
|
||||
$subject = $rendered['subject'] ?? '';
|
||||
$profile = MailProfile::query()
|
||||
->where('active', true)
|
||||
->orderBy('priority')
|
||||
->orderBy('id')
|
||||
->first();
|
||||
// Store bodies in companion table (optional, enabled here)
|
||||
$log->body()->create([
|
||||
'body_html' => (string) ($rendered['html'] ?? ''),
|
||||
'body_text' => (string) ($rendered['text'] ?? ''),
|
||||
'inline_css' => true,
|
||||
]);
|
||||
|
||||
try {
|
||||
if ($profile) {
|
||||
$host = $profile->host;
|
||||
$port = (int) ($profile->port ?: 587);
|
||||
$encryption = $profile->encryption ?: 'tls';
|
||||
$username = $profile->username ?: '';
|
||||
$password = (string) ($profile->decryptPassword() ?? '');
|
||||
// Dispatch the queued job
|
||||
dispatch(new SendEmailTemplateJob($log->id));
|
||||
|
||||
$scheme = $encryption === 'ssl' ? 'smtps' : 'smtp';
|
||||
$query = $encryption === 'tls' ? '?encryption=tls' : '';
|
||||
$dsn = sprintf('%s://%s:%s@%s:%d%s', $scheme, rawurlencode($username), rawurlencode($password), $host, $port, $query);
|
||||
|
||||
$transport = Transport::fromDsn($dsn);
|
||||
$mailer = new SymfonyMailer($transport);
|
||||
|
||||
$fromAddr = $profile->from_address ?: ($username ?: (config('mail.from.address') ?? ''));
|
||||
$fromName = $profile->from_name ?: (config('mail.from.name') ?? config('app.name'));
|
||||
|
||||
$email = (new Email)
|
||||
->from(new Address($fromAddr ?: $to, $fromName ?: null))
|
||||
->to($to)
|
||||
->subject($subject);
|
||||
|
||||
if (! empty($rendered['text'])) {
|
||||
$email->text($rendered['text']);
|
||||
}
|
||||
if (! empty($htmlForSend)) {
|
||||
if ($embed === 'base64') {
|
||||
try {
|
||||
$imageInliner = app(\App\Services\EmailImageInliner::class);
|
||||
$htmlForSend = $imageInliner->inline($htmlForSend);
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
} else {
|
||||
$htmlForSend = $this->absolutizeStorageUrls($request, $htmlForSend);
|
||||
}
|
||||
try {
|
||||
$inliner = new CssToInlineStyles;
|
||||
$htmlForSend = $inliner->convert($htmlForSend);
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
$email->html($htmlForSend);
|
||||
}
|
||||
|
||||
$mailer->send($email);
|
||||
} else {
|
||||
// Fallback to default Laravel mailer
|
||||
if (! empty($htmlForSend)) {
|
||||
if ($embed === 'base64') {
|
||||
try {
|
||||
$imageInliner = app(\App\Services\EmailImageInliner::class);
|
||||
$htmlForSend = $imageInliner->inline($htmlForSend);
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
} else {
|
||||
$htmlForSend = $this->absolutizeStorageUrls($request, $htmlForSend);
|
||||
}
|
||||
try {
|
||||
$inliner = new CssToInlineStyles;
|
||||
$htmlForSend = $inliner->convert($htmlForSend);
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
Mail::html($htmlForSend, function ($message) use ($to, $subject, $rendered) {
|
||||
$message->to($to)->subject($subject);
|
||||
if (! empty($rendered['text'])) {
|
||||
$message->text('mail::raw', ['slot' => $rendered['text']]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Mail::raw($rendered['text'] ?? '', function ($message) use ($to, $subject) {
|
||||
$message->to($to)->subject($subject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return back()->with('success', 'Test email sent to '.$to);
|
||||
} catch (\Throwable $e) {
|
||||
return back()->with('error', 'Failed to send test email: '.$e->getMessage());
|
||||
}
|
||||
return back()->with('success', 'Test email queued for '.$to);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -338,6 +292,22 @@ public function renderFinalHtml(Request $request, EmailTemplate $emailTemplate)
|
||||
|
||||
// Context resolution (same as sendTest)
|
||||
$ctx = [];
|
||||
if ($id = $request->integer('activity_id')) {
|
||||
$activity = Activity::query()->with(['action', 'decision', 'contract.clientCase.client.person', 'clientCase.client.person'])->find($id);
|
||||
if ($activity) {
|
||||
$ctx['activity'] = $activity;
|
||||
if ($activity->contract && ! isset($ctx['contract'])) {
|
||||
$ctx['contract'] = $activity->contract;
|
||||
}
|
||||
if ($activity->clientCase && ! isset($ctx['client_case'])) {
|
||||
$ctx['client_case'] = $activity->clientCase;
|
||||
}
|
||||
if (($ctx['client_case'] ?? null) && ! isset($ctx['client'])) {
|
||||
$ctx['client'] = $ctx['client_case']->client;
|
||||
$ctx['person'] = optional($ctx['client'])->person;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($id = $request->integer('contract_id')) {
|
||||
$contract = Contract::query()->with(['clientCase.client.person'])->find($id);
|
||||
if ($contract) {
|
||||
@@ -712,6 +682,28 @@ public function replaceImage(Request $request, EmailTemplate $emailTemplate)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an attached image Document from the given email template.
|
||||
*/
|
||||
public function deleteImage(Request $request, EmailTemplate $emailTemplate, Document $document)
|
||||
{
|
||||
$this->authorize('update', $emailTemplate);
|
||||
|
||||
// Ensure the document belongs to this template (polymorphic relation)
|
||||
if ((int) $document->documentable_id !== (int) $emailTemplate->id || $document->documentable_type !== EmailTemplate::class) {
|
||||
return response()->json(['message' => 'Document does not belong to this template.'], 422);
|
||||
}
|
||||
|
||||
try {
|
||||
// Force delete to remove underlying file as well (Document model handles file deletion on force delete)
|
||||
$document->forceDelete();
|
||||
} catch (\Throwable $e) {
|
||||
return response()->json(['message' => 'Failed to delete image: '.$e->getMessage()], 500);
|
||||
}
|
||||
|
||||
return response()->json(['deleted' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan HTML for images stored in /storage/tmp/email-images and move them into a permanent
|
||||
* location under /storage/email-images, create Document records and update the HTML.
|
||||
|
||||
@@ -126,14 +126,15 @@ public function sendTest(Request $request, MailProfile $mailProfile)
|
||||
$mailer = new SymfonyMailer($transport);
|
||||
|
||||
$fromAddr = $mailProfile->from_address ?: $username;
|
||||
$fromName = $mailProfile->from_name ?: config('app.name');
|
||||
$fromName = (string) ($mailProfile->from_name ?: (config('mail.from.name') ?? config('app.name') ?? ''));
|
||||
|
||||
$html = '<p>This is a <strong>test email</strong> from profile <code>'.e($mailProfile->name).'</code> at '.e(now()->toDateTimeString()).'.</p>';
|
||||
$text = 'This is a test email from profile "'.$mailProfile->name.'" at '.now()->toDateTimeString().'.';
|
||||
|
||||
// Build email
|
||||
$fromAddress = $fromName !== '' ? new Address($fromAddr, $fromName) : new Address($fromAddr);
|
||||
$email = (new Email)
|
||||
->from(new Address($fromAddr, $fromName))
|
||||
->from($fromAddress)
|
||||
->to($to)
|
||||
->subject('Test email - '.$mailProfile->name)
|
||||
->text($text)
|
||||
|
||||
Reference in New Issue
Block a user