CluPilotCloud/app/Livewire/Admin/MailPreview.php

107 lines
4.2 KiB
PHP

<?php
namespace App\Livewire\Admin;
use App\Services\Mail\MailPreviews;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Livewire\Attributes\Layout;
use Livewire\Component;
use Throwable;
/**
* Look at a mail before a customer does.
*
* There was no way to. An invoice mail needs an invoice, a maintenance
* announcement needs a window, and "register an account to see whether the
* confirmation reads well on a phone" is not a workflow — which is how every
* mail went out with a hard 600px table until somebody opened one on a
* telephone and had to scroll sideways to read a sentence.
*
* Two ways to look: in the browser, which answers the desktop question at once,
* and as a real mail to the operator's OWN address, which is the only way to
* answer the phone question. Never to a customer: a preview is for us.
*/
#[Layout('layouts.admin')]
class MailPreview extends Component
{
public function mount(): void
{
$this->authorize('mail.manage');
}
/**
* Send one to the signed-in operator.
*
* To themselves and nobody else — no address field. A preview that can be
* addressed is a form for sending mail to strangers from inside the console,
* which is not what this is for and not something to leave lying about.
*/
public function sendToMe(string $key): void
{
$this->authorize('mail.manage');
$operator = Auth::guard('operator')->user();
$mailable = app(MailPreviews::class)->make($key);
if ($operator === null || $mailable === null) {
return;
}
try {
// sendNow(), not send(): every mailable here implements ShouldQueue,
// and send() silently defers to queue() for those — which would put
// a failure in a worker's log while the button reported success. The
// person pressing it is waiting for the mail, so the send happens in
// their request and its failure reaches them.
//
// Through the mailable's OWN mailer. Mail::to() hands back a pending
// mail bound to the DEFAULT mailer, and sendNow() then ignores the
// mailer the mailable asked for — so a support mail addressed
// support@ went out over the no-reply@ login and the server answered
// "553 Sender address rejected: not owned by user". Every purpose
// mailbox here has its own SMTP account, and the From has to belong
// to the account that authenticates.
Mail::mailer($mailable->mailer ?: null)
->to($operator->email)
->sendNow($mailable);
} catch (Throwable $e) {
$this->dispatch('notify', message: __('mail_preview.failed', [
'reason' => mb_substr($e->getMessage(), 0, 160),
]));
return;
}
$this->dispatch('notify', message: __('mail_preview.sent', ['email' => $operator->email]));
}
public function render()
{
$this->authorize('mail.manage');
$previews = app(MailPreviews::class)->all();
return view('livewire.admin.mail-preview', [
'previews' => $previews,
// Read off the mailables rather than kept as a second list: the
// sender is decided by the purpose mailbox, and a table here would
// be a copy that drifts.
// From the envelope, not from the mailable's `from` property: that
// property stays empty until the mail is built, so reading it here
// showed nothing at all.
'senders' => collect($previews)
->keys()
->mapWithKeys(fn (string $key) => [
$key => app(MailPreviews::class)->make($key)?->envelope()->from?->address,
])
->all(),
'me' => Auth::guard('operator')->user()?->email ?? '',
// A preview that goes nowhere is worse than none: with a
// non-delivering mailer the browser view still works and the send
// silently writes to a file.
'delivers' => ! in_array(config('mail.default'), ['log', 'array', null], true),
]);
}
}