authorize('mail.manage'); $this->enabled = Settings::bool('mail.pace.enabled', true); $this->urgentCount = (int) Settings::get('mail.pace.urgent.count', 30); $this->urgentMinutes = (int) Settings::get('mail.pace.urgent.minutes', 5); $this->calmCount = (int) Settings::get('mail.pace.calm.count', 20); $this->calmMinutes = (int) Settings::get('mail.pace.calm.minutes', 10); } /** * Der Notschalter: nach dem Muster von Admin\Plans::toggleSales() — eine * Methode, eine Rückmeldung, kein Bestätigungsmodal. Er ist umkehrbar, * derselbe Klick legt ihn zurück. */ public function togglePace(): void { $this->authorize('mail.manage'); $this->enabled = ! $this->enabled; Settings::set('mail.pace.enabled', $this->enabled); $this->dispatch('notify', message: __($this->enabled ? 'mail_pace.enabled_on_notice' : 'mail_pace.enabled_off_notice')); } /** * Die beiden Kontingente speichern. * * `min:1` auf allen vier Feldern ist die zweite Hälfte der Untergrenze aus * MailPaceServiceProvider::mindestensEins() — die liest nur ab, was hier * gar nicht erst gespeichert werden darf. Ohne diese Prüfung käme eine * eingetippte 0 ungeprüft bis in die Einstellung durch und flöge erst beim * nächsten Auftrag an der Lesestelle auf, nachdem der Betreiber schon * „gespeichert" gesehen hat — ein Kontingent von 0 heißt dort: jede Mail * wird endlos zurückgelegt, ein Fenster von 0 Minuten schaltet die Drossel * still ab. */ public function savePace(): void { $this->authorize('mail.manage'); $data = $this->validate([ 'urgentCount' => ['required', 'integer', 'min:1'], 'urgentMinutes' => ['required', 'integer', 'min:1'], 'calmCount' => ['required', 'integer', 'min:1'], 'calmMinutes' => ['required', 'integer', 'min:1'], ]); Settings::set('mail.pace.urgent.count', $data['urgentCount']); Settings::set('mail.pace.urgent.minutes', $data['urgentMinutes']); Settings::set('mail.pace.calm.count', $data['calmCount']); Settings::set('mail.pace.calm.minutes', $data['calmMinutes']); $this->dispatch('notify', message: __('mail_pace.saved')); } /** * Eine Mailklasse in eine andere Spur verschieben. * * Ein Ziel außerhalb der drei bekannten Spuren kommt nur über einen * manuellen Aufruf zustande — kein bietet einer gesperrten Klasse eines an, // also nur ein manueller Aufruf — bekommt eine Meldung statt eines // Serverfehlers. $this->dispatch('notify', message: __('mail_pace.locked_notice')); return; } $this->dispatch('notify', message: __('mail_pace.moved')); } public function render() { // Je Spur: die Warteschlange, die App\Mail\Concerns\RidesALane // tatsächlich befüllt (Task 2) — derselbe Name, den auch der Arbeiter // in docker-compose.yml abhört (Task 4). Im echten Betrieb (Redis) // eine ehrliche Zahl aus der tatsächlichen Liste; unter der // Sync-Warteschlange, die die Testsuite erzwingt, immer 0, weil dort // nichts je wartet statt verschickt zu werden — dort ist die Zahl // ebenso ehrlich, nur bedeutungslos für einen Testfall, der einen // Rückstand nachstellen wollte. $lanes = [ [ 'key' => MailLane::DIRECT, 'throttled' => false, 'waiting' => Queue::size(MailLane::DIRECT), ], [ 'key' => MailLane::URGENT, 'throttled' => true, 'waiting' => Queue::size(MailLane::URGENT), 'countField' => 'urgentCount', 'minutesField' => 'urgentMinutes', ], [ 'key' => MailLane::CALM, 'throttled' => true, 'waiting' => Queue::size(MailLane::CALM), 'countField' => 'calmCount', 'minutesField' => 'calmMinutes', ], ]; return view('livewire.admin.mail-pace', [ 'lanes' => $lanes, 'laneOptions' => [MailLane::DIRECT, MailLane::URGENT, MailLane::CALM], 'assignments' => $this->assignmentRows(), ]); } /** * Die Zuordnung mit lesbaren Namen statt Klassennamen. * * Die Seite zeigte `class_basename()` — „DormantAccountWarningMail" steht * dort, wo der Betreiber „Konto ohne Paket wird gelöscht" sucht. Ein * Klassenname ist eine Auskunft über den Bauplan, keine über die Mail. * * Die Namen liegen in der Sprachdatei und NICHT in MailCatalogue, obwohl * der sehr ähnlich klingt: der Katalog zählt Mail*arten* (vier * Mahnstufen, vier Einträge, vier Absenderwege), diese Seite verteilt * Mail*klassen* auf Spuren — und alle vier Mahnstufen sind EINE Klasse, * die als eine Zeile eine Spur bekommt. Zwei Listen, weil es zwei * verschiedene Dinge sind; MailPaceLabelsTest hält fest, dass keine * Klasse ohne Namen bleibt. * * Fehlt einer doch, steht der Klassenname da wie bisher — unschön, aber * lesbar, statt einer leeren Zeile. * * @return array */ private function assignmentRows(): array { return collect(MailLane::all()) ->map(function (string $lane, string $class) { $kurz = class_basename($class); $schluessel = 'mail_pace.class.'.$kurz; return [ 'class' => $class, 'lane' => $lane, 'label' => Lang::has($schluessel) ? __($schluessel) : $kurz, 'locked' => MailLane::isLocked($class), ]; }) ->values() ->all(); } }