*/ public $domainMailUsers; // -------------------- Lifecycle -------------------- public function mount(?int $aliasId = null, ?int $domainId = null): void { $this->aliasId = $aliasId; $this->domainId = $domainId; if ($this->aliasId) { $alias = MailAlias::with(['domain', 'recipients.mailUser'])->findOrFail($this->aliasId); if ($alias->domain->is_system) abort(403, 'System-Domains sind für Aliasse gesperrt.'); $this->domain = $alias->domain; $this->domainId = $alias->domain_id; $this->fill([ 'local' => $alias->local, 'type' => $alias->type, 'group_name' => $alias->group_name, 'is_active' => $alias->is_active, 'notes' => $alias->notes, ]); // vorhandene Empfänger -> mit stabiler id versehen $this->recipients = $alias->recipients ->map(fn($r) => [ 'id' => (string)Str::uuid(), 'mail_user_id' => $r->mail_user_id, 'email' => $r->email, ]) ->all(); } else { $this->domain = $this->domainId ? Domain::where('is_system', false)->findOrFail($this->domainId) : Domain::where('is_system', false)->orderBy('domain')->first(); $this->domainId = $this->domain->id ?? null; $this->recipients = [[ 'id' => (string)Str::uuid(), 'mail_user_id' => null, 'email' => null, ]]; } $this->domainMailUsers = $this->domain ? MailUser::where('domain_id', $this->domain->id)->orderBy('localpart')->get() : collect(); $this->ensureAtLeastOneRow(); $this->recomputeUi(); $this->validateRecipientDuplicates(); } // -------------------- UI-Reaktionen -------------------- public function updated($name, $value): void { // XOR je Zeile if (preg_match('/^recipients\.(\d+)\.(mail_user_id|email)$/', $name, $m)) { $i = (int)$m[1]; if ($m[2] === 'mail_user_id' && !empty($value)) { $this->recipients[$i]['email'] = null; } elseif ($m[2] === 'email') { $this->recipients[$i]['mail_user_id'] = null; $this->recipients[$i]['email'] = Str::lower(trim((string)$this->recipients[$i]['email'])); } } // Self-Loop Live-Feedback (extern == alias) if (preg_match('/^recipients\.(\d+)\.email$/', $name, $m)) { $i = (int)$m[1]; $self = $this->currentAliasAddress(); $this->resetErrorBag("recipients.$i.email"); if ($self && Str::lower(trim((string)$this->recipients[$i]['email'])) === $self) { $this->addError("recipients.$i.email", 'Alias darf nicht an sich selbst weiterleiten.'); } } if ($name === 'type' && $this->type === 'single') { $first = $this->recipients[0] ?? ['id' => (string)Str::uuid(), 'mail_user_id' => null, 'email' => null]; $first['id'] = $first['id'] ?? (string)Str::uuid(); $this->recipients = [$first]; } $this->ensureAtLeastOneRow(); $this->recomputeUi(); $this->validateRecipientDuplicates(); } public function updatedType(string $value): void { if ($value === 'single') { $first = $this->recipients[0] ?? ['id' => (string)Str::uuid(), 'mail_user_id' => null, 'email' => null]; $first['id'] = $first['id'] ?? (string)Str::uuid(); $this->recipients = [$first]; $this->recomputeUi(); $this->validateRecipientDuplicates(); } } public function updatedDomainId(): void { $this->domain = $this->domainId ? Domain::where('is_system', false)->find($this->domainId) : null; $this->domainMailUsers = $this->domain ? MailUser::where('domain_id', $this->domain->id)->orderBy('localpart')->get() : collect(); // Zeilen resetten (neue Domain) $this->recipients = [[ 'id' => (string)Str::uuid(), 'mail_user_id' => null, 'email' => null, ]]; $this->ensureAtLeastOneRow(); $this->recomputeUi(); $this->validateRecipientDuplicates(); } public function addRecipientRow(): void { if ($this->type === 'single') return; if (count($this->recipients) >= $this->maxGroupRecipients) return; $this->recipients[] = ['id' => (string)Str::uuid(), 'mail_user_id' => null, 'email' => null]; $this->recomputeUi(); } public function removeRecipientRow(int $index): void { unset($this->recipients[$index]); $this->recipients = array_values($this->recipients); $this->ensureAtLeastOneRow(); $this->recomputeUi(); $this->validateRecipientDuplicates(); } // -------------------- Helpers -------------------- private function currentAliasAddress(): ?string { if (!$this->domainId || $this->local === '') return null; $domainName = $this->domain?->domain ?? Domain::find($this->domainId)?->domain; if (!$domainName) return null; return Str::lower(trim($this->local)) . '@' . Str::lower($domainName); } private function ensureAtLeastOneRow(): void { if (empty($this->recipients)) { $this->recipients = [[ 'id' => (string)Str::uuid(), 'mail_user_id' => null, 'email' => null, ]]; } } private function recomputeUi(): void { $count = count($this->recipients); $this->canAddRecipient = $this->type === 'group' && $count < $this->maxGroupRecipients; $usedMailboxIds = []; $usedExternal = []; foreach ($this->recipients as $r) { if (!empty($r['mail_user_id'])) $usedMailboxIds[] = (int)$r['mail_user_id']; if (!empty($r['email'])) $usedExternal[] = Str::lower(trim((string)$r['email'])); } $this->disabledMailboxIdsByRow = []; $this->rowState = []; $this->rowDuplicateError = []; $aliasAddr = $this->currentAliasAddress(); for ($i = 0; $i < $count; $i++) { $myId = (int)($this->recipients[$i]['mail_user_id'] ?? 0); $hasInternal = $myId > 0; $hasExternal = !empty($this->recipients[$i]['email']); $myExternal = Str::lower(trim((string)($this->recipients[$i]['email'] ?? ''))); // andere interne IDs sperren $disabled = array_values(array_unique(array_filter( $usedMailboxIds, fn($id) => $id && $id !== $myId ))); // interne IDs sperren, deren Adresse in externen Feldern vorkommt if ($this->domain) { foreach ($this->domainMailUsers as $mu) { $addr = Str::lower($mu->localpart . '@' . $this->domain->domain); if (in_array($addr, $usedExternal, true) && $mu->id !== $myId) { $disabled[] = (int)$mu->id; } } } $this->disabledMailboxIdsByRow[$i] = array_values(array_unique($disabled)); $this->rowState[$i] = [ 'disable_internal' => $hasExternal || ($aliasAddr && $aliasAddr === $myExternal), 'disable_external' => $hasInternal, 'can_remove' => !($this->type === 'single' && $count === 1), ]; } } private function validateRecipientDuplicates(): bool { $this->resetErrorBag(); $values = []; $byAddr = []; foreach ($this->recipients as $i => $r) { $addr = null; if (!empty($r['mail_user_id']) && $this->domain) { $u = MailUser::find((int)$r['mail_user_id']); if ($u) $addr = Str::lower($u->localpart . '@' . $this->domain->domain); } elseif (!empty($r['email'])) { $addr = Str::lower(trim((string)$r['email'])); } if ($addr) { $values[$i] = $addr; $byAddr[$addr] = $byAddr[$addr] ?? []; $byAddr[$addr][] = $i; } } $hasDupes = false; foreach ($byAddr as $addr => $idxs) { if (count($idxs) <= 1) continue; $hasDupes = true; foreach ($idxs as $i) { if (!empty($this->recipients[$i]['mail_user_id'])) { $this->addError("recipients.$i.mail_user_id", 'Dieser Empfänger ist bereits hinzugefügt.'); } else { $this->addError("recipients.$i.email", 'Dieser Empfänger ist bereits hinzugefügt.'); } } } return $hasDupes; } private function aliasConflictsWithMailbox(): bool { if (!$this->domainId || $this->local === '') return false; $local = Str::lower(trim($this->local)); return MailUser::query() ->where('domain_id', $this->domainId) ->whereRaw('LOWER(localpart) = ?', [$local]) ->exists(); } // -------------------- Save -------------------- public function save(): void { $this->validate($this->rules(), $this->messages()); foreach ($this->recipients as $i => $r) { if (empty($r['mail_user_id']) && empty($r['email'])) { $this->addError("recipients.$i.email", 'Wähle internes Postfach oder externe E-Mail.'); return; } } if ($this->validateRecipientDuplicates()) return; if ($aliasAddr = $this->currentAliasAddress()) { foreach ($this->recipients as $i => $r) { if (!empty($r['email']) && Str::lower(trim((string)$r['email'])) === $aliasAddr) { $this->addError("recipients.$i.email", 'Alias darf nicht an sich selbst weiterleiten.'); return; } if (!empty($r['mail_user_id'])) { $u = MailUser::find((int)$r['mail_user_id']); if ($u && $this->domain && Str::lower($u->localpart . '@' . $this->domain->domain) === $aliasAddr) { $this->addError("recipients.$i.mail_user_id", 'Alias darf nicht an sich selbst weiterleiten.'); return; } } } } if ($this->aliasConflictsWithMailbox()) { $this->addError('local', 'Diese Adresse ist bereits als Postfach vergeben.'); return; } $rows = collect($this->recipients) ->map(fn($r) => [ 'mail_user_id' => $r['mail_user_id'] ?: null, 'email' => isset($r['email']) && $r['email'] !== '' ? Str::lower(trim($r['email'])) : null, ]) ->filter(fn($r) => $r['mail_user_id'] || $r['email']) ->values(); $isUpdate = (bool) $this->aliasId; DB::transaction(function () use ($rows) { /** @var MailAlias $alias */ $alias = MailAlias::updateOrCreate( ['id' => $this->aliasId], [ 'domain_id' => $this->domainId, 'local' => $this->local, 'type' => $this->type, 'group_name' => $this->type === 'group' ? ($this->group_name ?: null) : null, 'is_active' => $this->is_active, 'notes' => $this->notes, ] ); $alias->recipients()->delete(); foreach ($rows as $i => $r) { $alias->recipients()->create([ 'mail_user_id' => $r['mail_user_id'], 'email' => $r['email'], 'position' => $i, ]); } $this->aliasId = $alias->id; }); $email = $this->currentAliasAddress(); if (!$email) { $domainName = $this->domain?->domain ?? optional(\App\Models\Domain::find($this->domainId))->domain; $email = trim(strtolower($this->local)) . '@' . trim(strtolower((string) $domainName)); } $this->dispatch('alias:' . ($this->aliasId ? 'updated' : 'created')); $this->dispatch('closeModal'); $typeLabel = $this->type === 'single' ? 'Single-Alias' : 'Gruppen-Alias'; $action = $this->aliasId ? 'aktualisiert' : 'erstellt'; $this->dispatch('toast', type: 'done', badge: 'Alias', title: "$typeLabel $action", text: "Der $typeLabel " . e($email) . " wurde erfolgreich $action.", duration: 6000 ); } // -------------------- Validation -------------------- protected function rules(): array { $occupied = $this->domainId ? MailUser::query() ->where('domain_id', $this->domainId) ->pluck('localpart') ->map(fn($l) => Str::lower($l)) ->all() : []; return [ 'domainId' => ['required', 'exists:domains,id'], 'local' => [ 'required', 'regex:/^[A-Za-z0-9._%+-]+$/', Rule::unique('mail_aliases', 'local') ->ignore($this->aliasId) ->where(fn($q) => $q->where('domain_id', $this->domainId)), Rule::notIn($occupied), ], 'type' => ['required', 'in:single,group'], 'group_name' => ['nullable','string','max:80','required_if:type,group'], 'recipients' => ['array', 'min:1', 'max:' . ($this->type === 'single' ? 1 : $this->maxGroupRecipients)], 'recipients.*.mail_user_id' => ['nullable', 'exists:mail_users,id'], 'recipients.*.email' => ['nullable', 'email:rfc'], ]; } protected function messages(): array { return [ 'local.required' => 'Alias-Adresse ist erforderlich.', 'local.regex' => 'Erlaubt sind Buchstaben, Zahlen und ._%+-', 'local.unique' => 'Dieser Alias existiert in dieser Domain bereits.', 'local.not_in' => 'Diese Adresse ist bereits als Postfach vergeben.', 'recipients.min' => 'Mindestens ein Empfänger ist erforderlich.', 'recipients.max' => $this->type === 'single' ? 'Bei „Single“ ist nur ein Empfänger zulässig.' : 'Maximal ' . $this->maxGroupRecipients . ' Empfänger erlaubt.', ]; } public function render() { return view('livewire.ui.mail.modal.alias-form-modal', [ 'domains' => Domain::where('is_system', false)->orderBy('domain')->get(['id', 'domain']), ]); } } //namespace App\Livewire\Ui\Mail\Modal; // //use App\Models\Domain; //use App\Models\MailAlias; //use App\Models\MailUser; //use Illuminate\Support\Facades\DB; //use Illuminate\Support\Str; //use Illuminate\Validation\Rule; //use LivewireUI\Modal\ModalComponent; // //class AliasFormModal extends ModalComponent //{ // // Eingabe / State // public ?int $aliasId = null; // public ?int $domainId = null; // public string $local = ''; // public string $type = 'single'; // single|group // public bool $is_active = true; // public ?string $notes = null; // // // Empfänger-Zeilen (each: ['mail_user_id'=>?int, 'email'=>?string]) // public array $recipients = []; // // // Stabile Keys je Zeile (für wire:key im Blade) // public array $rowKeys = []; // // // UI-Steuerung (nur Ausgabe im Blade) // public int $maxGroupRecipients = 20; // public bool $canAddRecipient = false; // „+ Empfänger“ erlauben // public array $rowState = []; // [i => ['disable_internal'=>bool,'disable_external'=>bool,'can_remove'=>bool]] // public array $disabledMailboxIdsByRow = []; // [i => [ids...]] // // // Lookup // public ?Domain $domain = null; // /** @var \Illuminate\Support\Collection */ // public $domainMailUsers; // // // -------------------- Lifecycle -------------------- // // public function mount(?int $aliasId = null, ?int $domainId = null): void // { // $this->aliasId = $aliasId; // $this->domainId = $domainId; // // if ($this->aliasId) { // $alias = MailAlias::with(['domain', 'recipients.mailUser'])->findOrFail($this->aliasId); // if ($alias->domain->is_system) { // abort(403, 'System-Domains sind für Aliasse gesperrt.'); // } // $this->domain = $alias->domain; // $this->domainId = $alias->domain_id; // // $this->fill([ // 'local' => $alias->local, // 'type' => $alias->type, // 'is_active' => $alias->is_active, // 'notes' => $alias->notes, // ]); // // $this->recipients = $alias->recipients // ->map(fn($r) => ['mail_user_id' => $r->mail_user_id, 'email' => $r->email]) // ->all(); // } else { // // erste Nicht-System-Domain // $this->domain = $this->domainId // ? Domain::where('is_system', false)->findOrFail($this->domainId) // : Domain::where('is_system', false)->orderBy('domain')->first(); // // $this->domainId = $this->domain->id ?? null; // $this->recipients = [['mail_user_id' => null, 'email' => null]]; // } // // $this->domainMailUsers = $this->domain // ? MailUser::where('domain_id', $this->domain->id)->orderBy('localpart')->get() // : collect(); // // $this->ensureAtLeastOneRow(); // $this->syncRowKeysToRecipients(); // // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // sofortige Feldfehler bei vorbefüllten Daten // } // // // -------------------- UI-Reaktionen -------------------- // // public function updated($name, $value): void // { // // XOR: wenn intern gesetzt -> extern leeren, und umgekehrt // if (preg_match('/^recipients\.(\d+)\.(mail_user_id|email)$/', $name, $m)) { // $i = (int)$m[1]; // if ($m[2] === 'mail_user_id' && !empty($value)) { // $this->recipients[$i]['email'] = null; // } elseif ($m[2] === 'email') { // $this->recipients[$i]['mail_user_id'] = null; // $this->recipients[$i]['email'] = Str::lower(trim((string)$this->recipients[$i]['email'])); // } // } // // // Self-Loop Live-Feedback: extern == alias // if (preg_match('/^recipients\.(\d+)\.email$/', $name, $m)) { // $i = (int)$m[1]; // $self = $this->currentAliasAddress(); // $this->resetErrorBag("recipients.$i.email"); // if ($self && Str::lower(trim((string)$this->recipients[$i]['email'])) === $self) { // $this->addError("recipients.$i.email", 'Alias darf nicht an sich selbst weiterleiten.'); // } // } // // if ($name === 'type' && $this->type === 'single') { // $this->recipients = [$this->recipients[0] ?? ['mail_user_id' => null, 'email' => null]]; // } // // $this->ensureAtLeastOneRow(); // $this->syncRowKeysToRecipients(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // feldgenaues Feedback live // } // // public function updatedType(string $value): void // { // if ($value === 'single') { // $first = $this->recipients[0] ?? ['mail_user_id' => null, 'email' => null]; // $this->recipients = [$first]; // $this->syncRowKeysToRecipients(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // } // } // // public function updatedDomainId(): void // { // $this->domain = $this->domainId // ? Domain::where('is_system', false)->find($this->domainId) // : null; // // $this->domainMailUsers = $this->domain // ? MailUser::where('domain_id', $this->domain->id)->orderBy('localpart')->get() // : collect(); // // // Zeilen resetten, weil Empfänger domaingebunden sind // $this->recipients = [['mail_user_id' => null, 'email' => null]]; // // $this->ensureAtLeastOneRow(); // $this->syncRowKeysToRecipients(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // } // // public function addRecipientRow(): void // { // if ($this->type === 'single') return; // if (count($this->recipients) >= $this->maxGroupRecipients) return; // // $this->recipients[] = ['mail_user_id' => null, 'email' => null]; // $this->rowKeys[] = (string)Str::uuid(); // stabiler Key für neue Zeile // // $this->recomputeUi(); // // kein Duplicate hier, weil leer // } // // public function removeRecipientRow(int $index): void // { // unset($this->recipients[$index], $this->rowKeys[$index]); // $this->recipients = array_values($this->recipients); // $this->rowKeys = array_values($this->rowKeys); // // $this->ensureAtLeastOneRow(); // $this->syncRowKeysToRecipients(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // } // // // -------------------- Helpers -------------------- // // // vollständige Alias-Adresse (z.B. "info@pixio.at") oder null // private function currentAliasAddress(): ?string // { // if (!$this->domainId || $this->local === '') return null; // // $domainName = $this->domain?->domain ?? Domain::find($this->domainId)?->domain; // if (!$domainName) return null; // // return Str::lower(trim($this->local)) . '@' . Str::lower($domainName); // } // // private function ensureAtLeastOneRow(): void // { // if (empty($this->recipients)) { // $this->recipients = [['mail_user_id' => null, 'email' => null]]; // } // } // // // rowKeys-Liste immer an recipients-Länge anpassen (stabile wire:key Werte) // private function syncRowKeysToRecipients(): void // { // $need = count($this->recipients); // $have = count($this->rowKeys); // // // auffüllen // while ($have < $need) { // $this->rowKeys[] = (string)Str::uuid(); // $have++; // } // // kürzen // if ($have > $need) { // $this->rowKeys = array_slice($this->rowKeys, 0, $need); // } // } // // // UI neu berechnen (inkl. Self-Loop-Schutz) // private function recomputeUi(): void // { // $count = count($this->recipients); // $this->canAddRecipient = $this->type === 'group' && $count < $this->maxGroupRecipients; // // // bereits gewählte interne IDs und externe Adressen einsammeln // $usedMailboxIds = []; // $usedExternalAddr = []; // // foreach ($this->recipients as $r) { // if (!empty($r['mail_user_id'])) $usedMailboxIds[] = (int)$r['mail_user_id']; // if (!empty($r['email'])) $usedExternalAddr[] = Str::lower(trim((string)$r['email'])); // } // // $this->disabledMailboxIdsByRow = []; // $this->rowState = []; // // // Aktuelle Alias-Adresse für Self-Loop-Checks // $aliasAddr = $this->currentAliasAddress(); // // for ($i = 0; $i < $count; $i++) { // $myId = (int)($this->recipients[$i]['mail_user_id'] ?? 0); // $hasInternal = $myId > 0; // $hasExternal = !empty($this->recipients[$i]['email']); // $myExternal = Str::lower(trim((string)($this->recipients[$i]['email'] ?? ''))); // // // andere bereits gewählte interne IDs sperren // $disabled = array_values(array_unique(array_filter( // $usedMailboxIds, fn($id) => $id && $id !== $myId // ))); // // // interne IDs zusätzlich sperren, deren Adresse in irgendeinem externen Feld steht // if ($this->domain) { // foreach ($this->domainMailUsers as $mu) { // $addr = Str::lower($mu->localpart . '@' . $this->domain->domain); // if (in_array($addr, $usedExternalAddr, true) && $mu->id !== $myId) { // $disabled[] = (int)$mu->id; // } // } // } // // $this->disabledMailboxIdsByRow[$i] = array_values(array_unique($disabled)); // // // Self-Loop: wenn extern == alias, internen Picker sperren // $disableInternal = $hasExternal || ($aliasAddr && $aliasAddr === $myExternal); // // $this->rowState[$i] = [ // 'disable_internal' => $disableInternal, // XOR + Self-Loop // 'disable_external' => $hasInternal, // XOR // 'can_remove' => !($this->type === 'single' && $count === 1), // ]; // } // } // // // Duplikate feldgenau markieren (keine Exceptions) // private function validateRecipientDuplicates(): bool // { // // komplette Empfänger-Fehler zurücksetzen // $this->resetErrorBag(); // // // Map Zeile -> normalisierte Zieladresse (intern => local@domain, extern => email) // $values = []; // [i => 'addr'] // $byAddrIndices = []; // 'addr' => [i1,i2,...] // // foreach ($this->recipients as $i => $r) { // $addr = null; // // if (!empty($r['mail_user_id'])) { // if ($this->domain) { // $u = MailUser::find((int)$r['mail_user_id']); // if ($u) $addr = Str::lower($u->localpart . '@' . $this->domain->domain); // } // } elseif (!empty($r['email'])) { // $addr = Str::lower(trim((string)$r['email'])); // } // // if ($addr) { // $values[$i] = $addr; // $byAddrIndices[$addr] = $byAddrIndices[$addr] ?? []; // $byAddrIndices[$addr][] = $i; // } // } // // $hasDupes = false; // // foreach ($byAddrIndices as $addr => $indices) { // if (count($indices) <= 1) continue; // $hasDupes = true; // // alle betroffenen Felder markieren (feldgenau) // foreach ($indices as $i) { // if (!empty($this->recipients[$i]['mail_user_id'])) { // $this->addError("recipients.$i.mail_user_id", 'Dieser Empfänger ist bereits hinzugefügt.'); // } else { // $this->addError("recipients.$i.email", 'Dieser Empfänger ist bereits hinzugefügt.'); // } // } // } // // return $hasDupes; // } // // private function aliasConflictsWithMailbox(): bool // { // if (!$this->domainId || $this->local === '') return false; // // $local = Str::lower(trim($this->local)); // return MailUser::query() // ->where('domain_id', $this->domainId) // ->whereRaw('LOWER(localpart) = ?', [$local]) // ->exists(); // } // // // -------------------- Save -------------------- // // public function save(): void // { // // 1) Basisregeln // $this->validate($this->rules(), $this->messages()); // // // 2) Zeilen dürfen nicht leer sein (weder intern noch extern) // foreach ($this->recipients as $i => $r) { // if (empty($r['mail_user_id']) && empty($r['email'])) { // $this->addError("recipients.$i.email", 'Wähle internes Postfach oder externe E-Mail.'); // return; // } // } // // // 3) Duplikate feldgenau prüfen // if ($this->validateRecipientDuplicates()) { // return; // } // // // 3b) Self-Loop final abfangen // if ($aliasAddr = $this->currentAliasAddress()) { // foreach ($this->recipients as $i => $r) { // // extern // if (!empty($r['email']) && Str::lower(trim((string)$r['email'])) === $aliasAddr) { // $this->addError("recipients.$i.email", 'Alias darf nicht an sich selbst weiterleiten.'); // return; // } // // intern (falls je relevant) // if (!empty($r['mail_user_id'])) { // $u = MailUser::find((int)$r['mail_user_id']); // if ($u && $this->domain && Str::lower($u->localpart . '@' . $this->domain->domain) === $aliasAddr) { // $this->addError("recipients.$i.mail_user_id", 'Alias darf nicht an sich selbst weiterleiten.'); // return; // } // } // } // } // // // 4) Alias-Adresse darf kein bestehendes Postfach sein // if ($this->aliasConflictsWithMailbox()) { // $this->addError('local', 'Diese Adresse ist bereits als Postfach vergeben.'); // return; // } // // // 5) Persistieren // $rows = collect($this->recipients) // ->map(fn($r) => [ // 'mail_user_id' => $r['mail_user_id'] ?: null, // 'email' => isset($r['email']) && $r['email'] !== '' ? Str::lower(trim($r['email'])) : null, // ]) // ->filter(fn($r) => $r['mail_user_id'] || $r['email']) // ->values(); // // DB::transaction(function () use ($rows) { // /** @var MailAlias $alias */ // $alias = MailAlias::updateOrCreate( // ['id' => $this->aliasId], // [ // 'domain_id' => $this->domainId, // 'local' => $this->local, // 'type' => $this->type, // 'is_active' => $this->is_active, // 'notes' => $this->notes, // ] // ); // // $alias->recipients()->delete(); // foreach ($rows as $i => $r) { // $alias->recipients()->create([ // 'mail_user_id' => $r['mail_user_id'], // 'email' => $r['email'], // 'position' => $i, // ]); // } // // $this->aliasId = $alias->id; // }); // // $this->dispatch('alias:' . ($this->aliasId ? 'updated' : 'created')); // $this->dispatch('closeModal'); // } // // // -------------------- Validation -------------------- // // protected function rules(): array // { // // blocke offensichtliche Kollisionen mit Mailboxen (gleiche Domain) // $occupied = $this->domainId // ? MailUser::query() // ->where('domain_id', $this->domainId) // ->pluck('localpart') // ->map(fn($l) => Str::lower($l)) // ->all() // : []; // // return [ // 'domainId' => ['required', 'exists:domains,id'], // 'local' => [ // 'required', // 'regex:/^[A-Za-z0-9._%+-]+$/', // Rule::unique('mail_aliases', 'local') // ->ignore($this->aliasId) // ->where(fn($q) => $q->where('domain_id', $this->domainId)), // Rule::notIn($occupied), // ], // 'type' => ['required', 'in:single,group'], // // 'recipients' => ['array', 'min:1', 'max:' . ($this->type === 'single' ? 1 : $this->maxGroupRecipients)], // 'recipients.*.mail_user_id' => ['nullable', 'exists:mail_users,id'], // 'recipients.*.email' => ['nullable', 'email:rfc'], // ]; // } // // protected function messages(): array // { // return [ // 'local.required' => 'Alias-Adresse ist erforderlich.', // 'local.regex' => 'Erlaubt sind Buchstaben, Zahlen und ._%+-', // 'local.unique' => 'Dieser Alias existiert in dieser Domain bereits.', // 'local.not_in' => 'Diese Adresse ist bereits als Postfach vergeben.', // 'recipients.min' => 'Mindestens ein Empfänger ist erforderlich.', // 'recipients.max' => $this->type === 'single' // ? 'Bei „Single“ ist nur ein Empfänger zulässig.' // : 'Maximal ' . $this->maxGroupRecipients . ' Empfänger erlaubt.', // ]; // } // // public function render() // { // return view('livewire.ui.mail.modal.alias-form-modal', [ // 'domains' => Domain::where('is_system', false) // ->orderBy('domain') // ->get(['id', 'domain']), // ]); // } //} //namespace App\Livewire\Ui\Mail\Modal; // //use App\Models\Domain; //use App\Models\MailAlias; //use App\Models\MailUser; //use Illuminate\Support\Facades\DB; //use Illuminate\Support\Str; //use Illuminate\Validation\Rule; //use LivewireUI\Modal\ModalComponent; // //class AliasFormModal extends ModalComponent //{ // // Eingabe / State // public ?int $aliasId = null; // public ?int $domainId = null; // public string $local = ''; // public string $type = 'single'; // single|group // public bool $is_active = true; // public ?string $notes = null; // // // Empfänger-Zeilen (each: ['mail_user_id'=>?int, 'email'=>?string]) // public array $recipients = []; // // // UI-Steuerung (nur Ausgabe im Blade) // public int $maxGroupRecipients = 20; // public bool $canAddRecipient = false; // „+ Empfänger“ erlauben // public array $rowState = []; // [i => ['disable_internal'=>bool,'disable_external'=>bool,'can_remove'=>bool]] // public array $disabledMailboxIdsByRow = []; // [i => [ids...]] // // // Lookup // public ?Domain $domain = null; // /** @var \Illuminate\Support\Collection */ // public $domainMailUsers; // // // -------------------- Lifecycle -------------------- // // public function mount(?int $aliasId = null, ?int $domainId = null): void // { // $this->aliasId = $aliasId; // $this->domainId = $domainId; // // if ($this->aliasId) { // $alias = MailAlias::with(['domain', 'recipients.mailUser'])->findOrFail($this->aliasId); // if ($alias->domain->is_system) { // abort(403, 'System-Domains sind für Aliasse gesperrt.'); // } // $this->domain = $alias->domain; // $this->domainId = $alias->domain_id; // // $this->fill([ // 'local' => $alias->local, // 'type' => $alias->type, // 'is_active' => $alias->is_active, // 'notes' => $alias->notes, // ]); // // $this->recipients = $alias->recipients // ->map(fn($r) => ['mail_user_id' => $r->mail_user_id, 'email' => $r->email]) // ->all(); // } else { // // erste Nicht-System-Domain // $this->domain = $this->domainId // ? Domain::where('is_system', false)->findOrFail($this->domainId) // : Domain::where('is_system', false)->orderBy('domain')->first(); // // $this->domainId = $this->domain->id ?? null; // $this->recipients = [['mail_user_id' => null, 'email' => null]]; // } // // $this->domainMailUsers = $this->domain // ? MailUser::where('domain_id', $this->domain->id)->orderBy('localpart')->get() // : collect(); // // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // sofortige Feldfehler bei vorbefüllten Daten // } // // // -------------------- UI-Reaktionen -------------------- // // public function updated($name, $value): void // { // // XOR: wenn intern gesetzt -> extern leeren, und umgekehrt // if (preg_match('/^recipients\.(\d+)\.(mail_user_id|email)$/', $name, $m)) { // $i = (int)$m[1]; // if ($m[2] === 'mail_user_id' && !empty($value)) { // $this->recipients[$i]['email'] = null; // } elseif ($m[2] === 'email') { // $this->recipients[$i]['mail_user_id'] = null; // $this->recipients[$i]['email'] = Str::lower(trim((string)$this->recipients[$i]['email'])); // } // } // // // Self-Loop Live-Feedback: extern == alias // if (preg_match('/^recipients\.(\d+)\.email$/', $name, $m)) { // $i = (int)$m[1]; // $self = $this->currentAliasAddress(); // $this->resetErrorBag("recipients.$i.email"); // if ($self && Str::lower(trim((string)$this->recipients[$i]['email'])) === $self) { // $this->addError("recipients.$i.email", 'Alias darf nicht an sich selbst weiterleiten.'); // } // } // // if ($name === 'type' && $this->type === 'single') { // $this->recipients = [$this->recipients[0] ?? ['mail_user_id' => null, 'email' => null]]; // } // // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // feldgenaues Feedback live // } // // public function updatedType(string $value): void // { // if ($value === 'single') { // $first = $this->recipients[0] ?? ['mail_user_id' => null, 'email' => null]; // $this->recipients = [$first]; // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // } // } // // public function updatedDomainId(): void // { // $this->domain = $this->domainId // ? Domain::where('is_system', false)->find($this->domainId) // : null; // // $this->domainMailUsers = $this->domain // ? MailUser::where('domain_id', $this->domain->id)->orderBy('localpart')->get() // : collect(); // // // Zeilen resetten, weil Empfänger domaingebunden sind // $this->recipients = [['mail_user_id' => null, 'email' => null]]; // // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // } // // public function addRecipientRow(): void // { // if ($this->type === 'single') return; // if (count($this->recipients) >= $this->maxGroupRecipients) return; // // $this->recipients[] = ['mail_user_id' => null, 'email' => null]; // $this->recomputeUi(); // } // // public function removeRecipientRow(int $index): void // { // unset($this->recipients[$index]); // $this->recipients = array_values($this->recipients); // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // } // // // -------------------- Helpers -------------------- // // // vollständige Alias-Adresse (z.B. "info@pixio.at") oder null // private function currentAliasAddress(): ?string // { // if (!$this->domainId || $this->local === '') return null; // // $domainName = $this->domain?->domain ?? Domain::find($this->domainId)?->domain; // if (!$domainName) return null; // // return Str::lower(trim($this->local)) . '@' . Str::lower($domainName); // } // // private function ensureAtLeastOneRow(): void // { // if (empty($this->recipients)) { // $this->recipients = [['mail_user_id' => null, 'email' => null]]; // } // } // // // UI neu berechnen (inkl. Self-Loop-Schutz) // private function recomputeUi(): void // { // $count = count($this->recipients); // $this->canAddRecipient = $this->type === 'group' && $count < $this->maxGroupRecipients; // // // bereits gewählte interne IDs und externe Adressen einsammeln // $usedMailboxIds = []; // $usedExternalAddr = []; // // foreach ($this->recipients as $r) { // if (!empty($r['mail_user_id'])) $usedMailboxIds[] = (int)$r['mail_user_id']; // if (!empty($r['email'])) $usedExternalAddr[] = Str::lower(trim((string)$r['email'])); // } // // $this->disabledMailboxIdsByRow = []; // $this->rowState = []; // // // Aktuelle Alias-Adresse für Self-Loop-Checks // $aliasAddr = $this->currentAliasAddress(); // // for ($i = 0; $i < $count; $i++) { // $myId = (int)($this->recipients[$i]['mail_user_id'] ?? 0); // $hasInternal = $myId > 0; // $hasExternal = !empty($this->recipients[$i]['email']); // $myExternal = Str::lower(trim((string)($this->recipients[$i]['email'] ?? ''))); // // // andere bereits gewählte internen IDs sperren // $disabled = array_values(array_unique(array_filter( // $usedMailboxIds, fn($id) => $id && $id !== $myId // ))); // // // interne IDs zusätzlich sperren, deren Adresse in irgendeinem externen Feld steht // if ($this->domain) { // foreach ($this->domainMailUsers as $mu) { // $addr = Str::lower($mu->localpart . '@' . $this->domain->domain); // if (in_array($addr, $usedExternalAddr, true) && $mu->id !== $myId) { // $disabled[] = (int)$mu->id; // } // } // } // // $this->disabledMailboxIdsByRow[$i] = array_values(array_unique($disabled)); // // // Self-Loop verhindern: wenn extern == alias, internen Picker sperren // $disableInternal = $hasExternal || ($aliasAddr && $aliasAddr === $myExternal); // // $this->rowState[$i] = [ // 'disable_internal' => $disableInternal, // XOR + Self-Loop // 'disable_external' => $hasInternal, // XOR // 'can_remove' => !($this->type === 'single' && $count === 1), // ]; // } // } // // // Duplikate feldgenau markieren (keine Exceptions) // private function validateRecipientDuplicates(): bool // { // // komplette Empfänger-Fehler zurücksetzen // $this->resetErrorBag(); // // // Map Zeile -> normalisierte Zieladresse (intern => local@domain, extern => email) // $values = []; // [i => 'addr'] // $byAddrIndices = []; // 'addr' => [i1,i2,...] // // foreach ($this->recipients as $i => $r) { // $addr = null; // // if (!empty($r['mail_user_id'])) { // if ($this->domain) { // $u = MailUser::find((int)$r['mail_user_id']); // if ($u) $addr = Str::lower($u->localpart . '@' . $this->domain->domain); // } // } elseif (!empty($r['email'])) { // $addr = Str::lower(trim((string)$r['email'])); // } // // if ($addr) { // $values[$i] = $addr; // $byAddrIndices[$addr] = $byAddrIndices[$addr] ?? []; // $byAddrIndices[$addr][] = $i; // } // } // // $hasDupes = false; // // foreach ($byAddrIndices as $addr => $indices) { // if (count($indices) <= 1) continue; // $hasDupes = true; // // alle betroffenen Felder markieren (feldgenau) // foreach ($indices as $i) { // if (!empty($this->recipients[$i]['mail_user_id'])) { // $this->addError("recipients.$i.mail_user_id", 'Dieser Empfänger ist bereits hinzugefügt.'); // } else { // $this->addError("recipients.$i.email", 'Dieser Empfänger ist bereits hinzugefügt.'); // } // } // } // // return $hasDupes; // } // // private function aliasConflictsWithMailbox(): bool // { // if (!$this->domainId || $this->local === '') return false; // // $local = Str::lower(trim($this->local)); // return MailUser::query() // ->where('domain_id', $this->domainId) // ->whereRaw('LOWER(localpart) = ?', [$local]) // ->exists(); // } // // // -------------------- Save -------------------- // // public function save(): void // { // // 1) Basisregeln // $this->validate($this->rules(), $this->messages()); // // // 2) Zeilen dürfen nicht leer sein (weder intern noch extern) // foreach ($this->recipients as $i => $r) { // if (empty($r['mail_user_id']) && empty($r['email'])) { // $this->addError("recipients.$i.email", 'Wähle internes Postfach oder externe E-Mail.'); // return; // } // } // // // 3) Duplikate feldgenau prüfen // if ($this->validateRecipientDuplicates()) { // return; // } // // // 3b) Self-Loop final abfangen // if ($aliasAddr = $this->currentAliasAddress()) { // foreach ($this->recipients as $i => $r) { // // extern // if (!empty($r['email']) && Str::lower(trim((string)$r['email'])) === $aliasAddr) { // $this->addError("recipients.$i.email", 'Alias darf nicht an sich selbst weiterleiten.'); // return; // } // // intern (falls je relevant) // if (!empty($r['mail_user_id'])) { // $u = MailUser::find((int)$r['mail_user_id']); // if ($u && $this->domain && Str::lower($u->localpart . '@' . $this->domain->domain) === $aliasAddr) { // $this->addError("recipients.$i.mail_user_id", 'Alias darf nicht an sich selbst weiterleiten.'); // return; // } // } // } // } // // // 4) Alias-Adresse darf kein bestehendes Postfach sein // if ($this->aliasConflictsWithMailbox()) { // $this->addError('local', 'Diese Adresse ist bereits als Postfach vergeben.'); // return; // } // // // 5) Persistieren // $rows = collect($this->recipients) // ->map(fn($r) => [ // 'mail_user_id' => $r['mail_user_id'] ?: null, // 'email' => isset($r['email']) && $r['email'] !== '' ? Str::lower(trim($r['email'])) : null, // ]) // ->filter(fn($r) => $r['mail_user_id'] || $r['email']) // ->values(); // // DB::transaction(function () use ($rows) { // /** @var MailAlias $alias */ // $alias = MailAlias::updateOrCreate( // ['id' => $this->aliasId], // [ // 'domain_id' => $this->domainId, // 'local' => $this->local, // 'type' => $this->type, // 'is_active' => $this->is_active, // 'notes' => $this->notes, // ] // ); // // $alias->recipients()->delete(); // foreach ($rows as $i => $r) { // $alias->recipients()->create([ // 'mail_user_id' => $r['mail_user_id'], // 'email' => $r['email'], // 'position' => $i, // ]); // } // // $this->aliasId = $alias->id; // }); // // $this->dispatch('alias:' . ($this->aliasId ? 'updated' : 'created')); // $this->dispatch('closeModal'); // } // // // -------------------- Validation -------------------- // // protected function rules(): array // { // // blocke offensichtliche Kollisionen mit Mailboxen (gleiche Domain) // $occupied = $this->domainId // ? MailUser::query() // ->where('domain_id', $this->domainId) // ->pluck('localpart') // ->map(fn($l) => Str::lower($l)) // ->all() // : []; // // return [ // 'domainId' => ['required', 'exists:domains,id'], // 'local' => [ // 'required', // 'regex:/^[A-Za-z0-9._%+-]+$/', // Rule::unique('mail_aliases', 'local') // ->ignore($this->aliasId) // ->where(fn($q) => $q->where('domain_id', $this->domainId)), // Rule::notIn($occupied), // ], // 'type' => ['required', 'in:single,group'], // // 'recipients' => ['array', 'min:1', 'max:' . ($this->type === 'single' ? 1 : $this->maxGroupRecipients)], // 'recipients.*.mail_user_id' => ['nullable', 'exists:mail_users,id'], // 'recipients.*.email' => ['nullable', 'email:rfc'], // ]; // } // // protected function messages(): array // { // return [ // 'local.required' => 'Alias-Adresse ist erforderlich.', // 'local.regex' => 'Erlaubt sind Buchstaben, Zahlen und ._%+-', // 'local.unique' => 'Dieser Alias existiert in dieser Domain bereits.', // 'local.not_in' => 'Diese Adresse ist bereits als Postfach vergeben.', // 'recipients.min' => 'Mindestens ein Empfänger ist erforderlich.', // 'recipients.max' => $this->type === 'single' // ? 'Bei „Single“ ist nur ein Empfänger zulässig.' // : 'Maximal ' . $this->maxGroupRecipients . ' Empfänger erlaubt.', // ]; // } // // public function render() // { // return view('livewire.ui.mail.modal.alias-form-modal', [ // 'domains' => Domain::where('is_system', false) // ->orderBy('domain') // ->get(['id', 'domain']), // ]); // } //} //namespace App\Livewire\Ui\Mail\Modal; // //use App\Models\Domain; //use App\Models\MailAlias; //use App\Models\MailUser; //use Illuminate\Support\Facades\DB; //use Illuminate\Support\Str; //use Illuminate\Validation\Rule; //use LivewireUI\Modal\ModalComponent; // //class AliasFormModal extends ModalComponent //{ // // Eingabe / State // public ?int $aliasId = null; // public ?int $domainId = null; // public string $local = ''; // public string $type = 'single'; // single|group // public bool $is_active = true; // public ?string $notes = null; // // // Empfänger-Zeilen (each: ['mail_user_id'=>?int, 'email'=>?string]) // public array $recipients = []; // // // UI-Steuerung (nur Ausgabe im Blade) // public int $maxGroupRecipients = 20; // public bool $canAddRecipient = false; // „+ Empfänger“ erlauben // public array $rowState = []; // [i => ['disable_internal'=>bool,'disable_external'=>bool,'can_remove'=>bool]] // public array $disabledMailboxIdsByRow = []; // [i => [ids...]] // // // Lookup // public ?Domain $domain = null; // /** @var \Illuminate\Support\Collection */ // public $domainMailUsers; // // // -------------------- Lifecycle -------------------- // // public function mount(?int $aliasId = null, ?int $domainId = null): void // { // $this->aliasId = $aliasId; // $this->domainId = $domainId; // // if ($this->aliasId) { // $alias = MailAlias::with(['domain','recipients.mailUser'])->findOrFail($this->aliasId); // if ($alias->domain->is_system) { // abort(403, 'System-Domains sind für Aliasse gesperrt.'); // } // $this->domain = $alias->domain; // $this->domainId = $alias->domain_id; // // $this->fill([ // 'local' => $alias->local, // 'type' => $alias->type, // 'is_active' => $alias->is_active, // 'notes' => $alias->notes, // ]); // // $this->recipients = $alias->recipients // ->map(fn($r) => ['mail_user_id' => $r->mail_user_id, 'email' => $r->email]) // ->all(); // } else { // // erste Nicht-System-Domain // $this->domain = $this->domainId // ? Domain::where('is_system', false)->findOrFail($this->domainId) // : Domain::where('is_system', false)->orderBy('domain')->first(); // // $this->domainId = $this->domain->id ?? null; // $this->recipients = [['mail_user_id' => null, 'email' => null]]; // } // // $this->domainMailUsers = $this->domain // ? MailUser::where('domain_id', $this->domain->id)->orderBy('localpart')->get() // : collect(); // // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // sofortige Feldfehler bei vorbefüllten Daten // } // // // -------------------- UI-Reaktionen -------------------- // // public function updated($name, $value): void // { // // XOR: wenn intern gesetzt -> extern leeren, und umgekehrt // if (preg_match('/^recipients\.(\d+)\.(mail_user_id|email)$/', $name, $m)) { // $i = (int)$m[1]; // if ($m[2] === 'mail_user_id' && !empty($value)) { // $this->recipients[$i]['email'] = null; // } elseif ($m[2] === 'email') { // $this->recipients[$i]['mail_user_id'] = null; // $this->recipients[$i]['email'] = Str::lower(trim((string)$this->recipients[$i]['email'])); // } // } // // if (preg_match('/^recipients\.(\d+)\.email$/', $name, $m)) { // $i = (int)$m[1]; // $self = $this->currentAliasAddress(); // $this->resetErrorBag("recipients.$i.email"); // if ($self && \Illuminate\Support\Str::lower(trim((string)$this->recipients[$i]['email'])) === $self) { // $this->addError("recipients.$i.email", 'Alias darf nicht an sich selbst weiterleiten.'); // } // } // // if ($name === 'type' && $this->type === 'single') { // $this->recipients = [ $this->recipients[0] ?? ['mail_user_id'=>null,'email'=>null] ]; // } // // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // feldgenaues Feedback live // } // // public function updatedType(string $value): void // { // if ($value === 'single') { // $first = $this->recipients[0] ?? ['mail_user_id'=>null,'email'=>null]; // $this->recipients = [ $first ]; // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // } // } // // public function updatedDomainId(): void // { // $this->domain = $this->domainId // ? Domain::where('is_system', false)->find($this->domainId) // : null; // // $this->domainMailUsers = $this->domain // ? MailUser::where('domain_id', $this->domain->id)->orderBy('localpart')->get() // : collect(); // // // Zeilen resetten, weil Empfänger domaingebunden sind // $this->recipients = [['mail_user_id' => null, 'email' => null]]; // // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // } // // public function addRecipientRow(): void // { // if ($this->type === 'single') return; // if (count($this->recipients) >= $this->maxGroupRecipients) return; // // $this->recipients[] = ['mail_user_id'=>null,'email'=>null]; // $this->recomputeUi(); // // kein Duplicate hier, weil leer // } // // public function removeRecipientRow(int $index): void // { // unset($this->recipients[$index]); // $this->recipients = array_values($this->recipients); // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // $this->validateRecipientDuplicates(); // } // // // -------------------- Helpers -------------------- // // private function currentAliasAddress(): ?string // { // if (!$this->domainId || $this->local === '' || !$this->domain) return null; // return \Illuminate\Support\Str::lower(trim($this->local).'@'.$this->domain->domain); // } // // private function ensureAtLeastOneRow(): void // { // if (empty($this->recipients)) { // $this->recipients = [['mail_user_id'=>null,'email'=>null]]; // } // } // // private function recomputeUi(): void // { // $count = count($this->recipients); // $this->canAddRecipient = $this->type === 'group' && $count < $this->maxGroupRecipients; // // // bereits gewählte interne IDs und externe Adressen einsammeln // $usedMailboxIds = []; // $usedExternalAddrs = []; // ['a@b.tld', ...] normalisiert // // foreach ($this->recipients as $r) { // if (!empty($r['mail_user_id'])) $usedMailboxIds[] = (int)$r['mail_user_id']; // if (!empty($r['email'])) $usedExternalAddrs[] = Str::lower(trim((string)$r['email'])); // } // // $this->disabledMailboxIdsByRow = []; // $this->rowState = []; // // // Helper: internes Postfach -> vollständige Adresse // $mailboxEmail = function (?int $id): ?string { // if (!$id || !$this->domain) return null; // $u = MailUser::find($id); // return $u ? Str::lower($u->localpart.'@'.$this->domain->domain) : null; // }; // // for ($i = 0; $i < $count; $i++) { // $myId = (int)($this->recipients[$i]['mail_user_id'] ?? 0); // $hasInternal = $myId > 0; // $hasExternal = !empty($this->recipients[$i]['email']); // // // Basis: andere bereits gewählte interne IDs sperren // $disabled = array_values(array_unique(array_filter( // $usedMailboxIds, fn($id) => $id && $id !== $myId // ))); // // // Zusätzlich: interne IDs sperren, deren Adresse in irgendeinem externen Feld steht // if ($this->domain) { // foreach ($this->domainMailUsers as $mu) { // $addr = Str::lower($mu->localpart.'@'.$this->domain->domain); // if (in_array($addr, $usedExternalAddrs, true) && $mu->id !== $myId) { // $disabled[] = (int)$mu->id; // } // } // } // // $this->disabledMailboxIdsByRow[$i] = array_values(array_unique($disabled)); // // $this->rowState[$i] = [ // 'disable_internal' => $hasExternal, // XOR // 'disable_external' => $hasInternal, // XOR // 'can_remove' => !($this->type === 'single' && $count === 1), // ]; // } // } // // // // Duplikate feldgenau markieren (keine Exceptions) // private function validateRecipientDuplicates(): bool // { // // komplette Empfänger-Fehler zurücksetzen // $this->resetErrorBag(); // // // Map Zeile -> normalisierte Zieladresse (intern => local@domain, extern => email) // $values = []; // [i => 'addr'] // $byAddrIndices = []; // 'addr' => [i1,i2,...] // // foreach ($this->recipients as $i => $r) { // $addr = null; // // if (!empty($r['mail_user_id'])) { // if ($this->domain) { // $u = MailUser::find((int)$r['mail_user_id']); // if ($u) $addr = Str::lower($u->localpart.'@'.$this->domain->domain); // } // } elseif (!empty($r['email'])) { // $addr = Str::lower(trim((string)$r['email'])); // } // // if ($addr) { // $values[$i] = $addr; // $byAddrIndices[$addr] = $byAddrIndices[$addr] ?? []; // $byAddrIndices[$addr][] = $i; // } // } // // $hasDupes = false; // // foreach ($byAddrIndices as $addr => $indices) { // if (count($indices) <= 1) continue; // $hasDupes = true; // // alle betroffenen Felder markieren (feldgenau) // foreach ($indices as $i) { // if (!empty($this->recipients[$i]['mail_user_id'])) { // $this->addError("recipients.$i.mail_user_id", 'Dieser Empfänger ist bereits hinzugefügt.'); // } else { // $this->addError("recipients.$i.email", 'Dieser Empfänger ist bereits hinzugefügt.'); // } // } // } // // return $hasDupes; // } // // private function aliasConflictsWithMailbox(): bool // { // if (!$this->domainId || $this->local === '') return false; // // $local = Str::lower(trim($this->local)); // return MailUser::query() // ->where('domain_id', $this->domainId) // ->whereRaw('LOWER(localpart) = ?', [$local]) // ->exists(); // } // // // -------------------- Save -------------------- // // public function save(): void // { // // 1) Basisregeln // $this->validate($this->rules(), $this->messages()); // // // 2) Zeilen dürfen nicht leer sein (weder intern noch extern) // foreach ($this->recipients as $i => $r) { // if (empty($r['mail_user_id']) && empty($r['email'])) { // $this->addError("recipients.$i.email", 'Wähle internes Postfach oder externe E-Mail.'); // return; // } // } // // // 3) Duplikate feldgenau prüfen // if ($this->validateRecipientDuplicates()) { // return; // } // // if ($aliasAddr = $this->currentAliasAddress()) { // foreach ($this->recipients as $i => $r) { // // extern // if (!empty($r['email']) && \Illuminate\Support\Str::lower(trim((string)$r['email'])) === $aliasAddr) { // $this->addError("recipients.$i.email", 'Alias darf nicht an sich selbst weiterleiten.'); // return; // } // // intern (falls es später mal interne Empfänger ohne existierendes Postfach geben sollte) // if (!empty($r['mail_user_id'])) { // $u = \App\Models\MailUser::find((int)$r['mail_user_id']); // if ($u && $this->domain && \Illuminate\Support\Str::lower($u->localpart.'@'.$this->domain->domain) === $aliasAddr) { // $this->addError("recipients.$i.mail_user_id", 'Alias darf nicht an sich selbst weiterleiten.'); // return; // } // } // } // } // // // 4) Alias-Adresse darf kein bestehendes Postfach sein // if ($this->aliasConflictsWithMailbox()) { // $this->addError('local', 'Diese Adresse ist bereits als Postfach vergeben.'); // return; // } // // // 5) Persistieren // $rows = collect($this->recipients) // ->map(fn($r) => [ // 'mail_user_id' => $r['mail_user_id'] ?: null, // 'email' => isset($r['email']) && $r['email'] !== '' ? Str::lower(trim($r['email'])) : null, // ]) // ->filter(fn($r) => $r['mail_user_id'] || $r['email']) // ->values(); // // DB::transaction(function () use ($rows) { // /** @var MailAlias $alias */ // $alias = MailAlias::updateOrCreate( // ['id' => $this->aliasId], // [ // 'domain_id' => $this->domainId, // 'local' => $this->local, // 'type' => $this->type, // 'is_active' => $this->is_active, // 'notes' => $this->notes, // ] // ); // // $alias->recipients()->delete(); // foreach ($rows as $i => $r) { // $alias->recipients()->create([ // 'mail_user_id' => $r['mail_user_id'], // 'email' => $r['email'], // 'position' => $i, // ]); // } // // $this->aliasId = $alias->id; // }); // // $this->dispatch('alias:' . ($this->aliasId ? 'updated' : 'created')); // $this->dispatch('closeModal'); // } // // // -------------------- Validation -------------------- // // protected function rules(): array // { // // blocke offensichtliche Kollisionen mit Mailboxen (gleiche Domain) // $occupied = $this->domainId // ? MailUser::query() // ->where('domain_id', $this->domainId) // ->pluck('localpart') // ->map(fn($l) => Str::lower($l)) // ->all() // : []; // // return [ // 'domainId' => ['required','exists:domains,id'], // 'local' => [ // 'required', // 'regex:/^[A-Za-z0-9._%+-]+$/', // Rule::unique('mail_aliases','local') // ->ignore($this->aliasId) // ->where(fn($q) => $q->where('domain_id', $this->domainId)), // Rule::notIn($occupied), // ], // 'type' => ['required','in:single,group'], // // 'recipients' => ['array','min:1','max:'.($this->type==='single' ? 1 : $this->maxGroupRecipients)], // 'recipients.*.mail_user_id' => ['nullable','exists:mail_users,id'], // 'recipients.*.email' => ['nullable','email:rfc'], // ]; // } // // protected function messages(): array // { // return [ // 'local.required' => 'Alias-Adresse ist erforderlich.', // 'local.regex' => 'Erlaubt sind Buchstaben, Zahlen und ._%+-', // 'local.unique' => 'Dieser Alias existiert in dieser Domain bereits.', // 'local.not_in' => 'Diese Adresse ist bereits als Postfach vergeben.', // 'recipients.min' => 'Mindestens ein Empfänger ist erforderlich.', // 'recipients.max' => $this->type === 'single' // ? 'Bei „Single“ ist nur ein Empfänger zulässig.' // : 'Maximal '.$this->maxGroupRecipients.' Empfänger erlaubt.', // ]; // } // // public function render() // { // return view('livewire.ui.mail.modal.alias-form-modal', [ // 'domains' => Domain::where('is_system', false) // ->orderBy('domain') // ->get(['id','domain']), // ]); // } //} // //namespace App\Livewire\Ui\Mail\Modal; // //use App\Models\Domain; //use App\Models\MailAlias; //use App\Models\MailAliasRecipient; //use App\Models\MailUser; //use Illuminate\Support\Facades\DB; //use Illuminate\Support\Str; //use Illuminate\Validation\Rule; //use Livewire\Attributes\On; //use LivewireUI\Modal\ModalComponent; // //class AliasFormModal extends ModalComponent //{ // // Eingabe-Props // public ?int $aliasId = null; // wenn gesetzt -> Edit // public ?int $domainId = null; // Create: vorbelegen (oder Auswahl) // public string $local = ''; // public string $type = 'single'; // 'single' | 'group' // public bool $is_active = true; // public ?string $notes = null; // public int $maxGroupRecipients = 20; // public bool $canAddRecipient = false; // public array $rowState = []; // public array $disabledMailboxIdsByRow = []; // // // optionale UI-Fehler pro Zeile (Anzeige) // public array $rowDuplicateError = []; // // public array $recipients = []; // // // UI-Daten // public ?Domain $domain = null; // /** @var \Illuminate\Support\Collection */ // public $domainMailUsers; // // public function mount(?int $aliasId = null, ?int $domainId = null): void // { // $this->aliasId = $aliasId; // $this->domainId = $domainId; // // if ($this->aliasId) { // $alias = MailAlias::with(['domain','recipients.mailUser'])->findOrFail($this->aliasId); // if ($alias->domain->is_system) { // abort(403, 'System-Domains sind für Aliasse gesperrt.'); // } // $this->domain = $alias->domain; // $this->domainId = $alias->domain_id; // // $this->fill([ // 'local' => $alias->local, // 'type' => $alias->type, // 'is_active' => $alias->is_active, // 'notes' => $alias->notes, // ]); // // $this->recipients = $alias->recipients->map(fn($r) => [ // 'mail_user_id' => $r->mail_user_id, // 'email' => $r->email, // ])->all(); // } else { // // Erst NICHT-System-Domain // $this->domain = $this->domainId // ? Domain::where('is_system', false)->findOrFail($this->domainId) // : Domain::where('is_system', false)->orderBy('domain')->first(); // // $this->domainId = $this->domain->id ?? null; // $this->recipients = [['mail_user_id' => null, 'email' => null]]; // } // // $this->domainMailUsers = $this->domain // ? MailUser::where('domain_id', $this->domain->id)->orderBy('localpart')->get() // : collect(); // // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // } // // public function updatedType(string $value): void // { // if ($value === 'single') { // // exakt eine Zeile behalten // $first = $this->recipients[0] ?? ['mail_user_id'=>null,'email'=>null]; // $this->recipients = [ $first ]; // } // } // // private function validateRecipientDuplicates(): bool // { // // vorhandene Feldfehler für Empfänger leeren // $this->resetErrorBag('recipients'); // // // Map: Zielstring je Zeile -> Anzahl // $values = []; // [i => 'office@pixio.at'] // $counts = []; // 'office@pixio.at' => n // // foreach ($this->recipients as $i => $r) { // $val = null; // // if (!empty($r['mail_user_id'])) { // $u = MailUser::find((int)$r['mail_user_id']); // if ($u && $this->domain) { // $val = Str::lower($u->localpart.'@'.$this->domain->domain); // } // } elseif (!empty($r['email'])) { // $val = Str::lower(trim((string)$r['email'])); // } // // if ($val) { // $values[$i] = $val; // $counts[$val] = ($counts[$val] ?? 0) + 1; // } // } // // $hasDupes = false; // // foreach ($values as $i => $val) { // if (($counts[$val] ?? 0) > 1) { // // Feldgenau markieren // if (!empty($this->recipients[$i]['mail_user_id'])) { // $this->addError("recipients.$i.mail_user_id", 'Dieser Empfänger ist bereits hinzugefügt.'); // } else { // $this->addError("recipients.$i.email", 'Dieser Empfänger ist bereits hinzugefügt.'); // } // $hasDupes = true; // } // } // // return $hasDupes; // } // // // // Livewire v3: universeller Hook für verschachtelte Felder // public function updated($name, $value): void // { // // XOR je Zeile // if (preg_match('/^recipients\.(\d+)\.(mail_user_id|email)$/', $name, $m)) { // $i = (int)$m[1]; // if ($m[2] === 'mail_user_id' && !empty($value)) { // $this->recipients[$i]['email'] = null; // } elseif ($m[2] === 'email' && !empty($value)) { // $this->recipients[$i]['mail_user_id'] = null; // $this->recipients[$i]['email'] = Str::lower(trim((string)$this->recipients[$i]['email'])); // } // } // // if ($name === 'type' && $this->type === 'single') { // $this->recipients = [ $this->recipients[0] ?? ['mail_user_id'=>null,'email'=>null] ]; // } // // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // // // sofortiges, feldgenaues Duplikat-Feedback // $this->validateRecipientDuplicates(); // } // // public function addRecipientRow(): void // { // if ($this->type === 'single') return; // if (count($this->recipients) >= $this->maxGroupRecipients) return; // // $this->recipients[] = ['mail_user_id'=>null,'email'=>null]; // $this->recomputeUi(); // } // // public function updatedDomainId(): void // { // $this->domain = $this->domainId // ? Domain::where('is_system', false)->find($this->domainId) // : null; // // $this->domainMailUsers = $this->domain // ? MailUser::where('domain_id', $this->domain->id)->orderBy('localpart')->get() // : collect(); // // $this->recipients = [['mail_user_id' => null, 'email' => null]]; // // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // } // // // public function removeRecipientRow(int $index): void // { // unset($this->recipients[$index]); // $this->recipients = array_values($this->recipients); // $this->ensureAtLeastOneRow(); // $this->recomputeUi(); // } // // private function ensureAtLeastOneRow(): void // { // if (empty($this->recipients)) { // $this->recipients = [['mail_user_id'=>null,'email'=>null]]; // } // } // // private function recomputeUi(): void // { // $count = count($this->recipients); // // // Button „+ Empfänger“ // $this->canAddRecipient = $this->type === 'group' && $count < $this->maxGroupRecipients; // // // Sammle bereits gewählte interne Mailbox-IDs und externe E-Mails (normalisiert) // $usedMailboxIds = []; // $usedExternal = []; // // foreach ($this->recipients as $i => $r) { // if (!empty($r['mail_user_id'])) $usedMailboxIds[] = (int)$r['mail_user_id']; // if (!empty($r['email'])) $usedExternal[] = Str::lower(trim((string)$r['email'])); // } // // // pro Zeile: welche Mailbox-IDs sollen disabled sein (alle used außer die eigene) // $this->disabledMailboxIdsByRow = []; // $this->rowState = []; // $this->rowDuplicateError = []; // // for ($i=0; $i<$count; $i++) { // $myId = (int)($this->recipients[$i]['mail_user_id'] ?? 0); // $myEmail = Str::lower(trim((string)($this->recipients[$i]['email'] ?? ''))); // // $disabled = array_values(array_unique(array_filter($usedMailboxIds, fn($id) => $id && $id !== $myId))); // $this->disabledMailboxIdsByRow[$i] = $disabled; // // $hasInternal = $myId > 0; // $hasExternal = $myEmail !== ''; // // $this->rowState[$i] = [ // 'disable_internal' => $hasExternal, // XOR // 'disable_external' => $hasInternal, // XOR // 'can_remove' => !($this->type === 'single' && $count === 1), // ]; // // // Duplikats-UI: wenn meine Zieladresse mehrfach vorkommt -> Fehlermeldung für diese Zeile // if ($hasInternal && count(array_filter($usedMailboxIds, fn($id) => $id === $myId)) > 1) { // $this->rowDuplicateError[$i] = 'Dieses Postfach ist bereits Empfänger.'; // } // if ($hasExternal && count(array_filter($usedExternal, fn($e) => $e === $myEmail)) > 1) { // $this->rowDuplicateError[$i] = 'Diese E-Mail ist bereits Empfänger.'; // } // } // // $this->hasDuplicatesAndMarkUi(); // } // //// private function assertNoDuplicates(): void //// { //// // Ziel-Strings bilden: intern -> localpart@domain, extern -> E-Mail //// $targets = []; //// foreach ($this->recipients as $r) { //// if (!empty($r['mail_user_id'])) { //// $u = MailUser::find((int)$r['mail_user_id']); //// if ($u && $this->domain) { //// $targets[] = Str::lower($u->localpart.'@'.$this->domain->domain); //// } //// } elseif (!empty($r['email'])) { //// $targets[] = Str::lower(trim((string)$r['email'])); //// } //// } //// $dupes = array_unique(array_diff_key($targets, array_unique($targets))); //// if (!empty($dupes)) { //// $this->addError('recipients', 'Doppelte Empfänger nicht erlaubt.'); //// throw new \RuntimeException('duplicates'); // brich Save clean ab //// } //// } // // private function hasDuplicatesAndMarkUi(): bool // { // // Ziel-String pro Zeile: intern => local@domain, extern => email // $targets = []; // [i => 'office@pixio.at'] // $byValueCount = []; // 'office@pixio.at' => n // // $this->rowDuplicateError = []; // UI reset // // foreach ($this->recipients as $i => $r) { // $val = null; // // if (!empty($r['mail_user_id'])) { // $u = MailUser::find((int)$r['mail_user_id']); // if ($u && $this->domain) { // $val = mb_strtolower($u->localpart.'@'.$this->domain->domain); // } // } elseif (!empty($r['email'])) { // $val = mb_strtolower(trim((string)$r['email'])); // } // // if ($val) { // $targets[$i] = $val; // $byValueCount[$val] = ($byValueCount[$val] ?? 0) + 1; // } // } // // $hasDupes = false; // foreach ($targets as $i => $val) { // if (($byValueCount[$val] ?? 0) > 1) { // $this->rowDuplicateError[$i] = 'Dieser Empfänger ist bereits hinzugefügt.'; // $hasDupes = true; // } // } // // return $hasDupes; // } // // // // public function save(): void // { // $this->validate($this->rules(), $this->messages()); // // // XOR-Check Empfänger + Single/Group-Logik // $rows = collect($this->recipients) // ->map(fn($r) => [ // 'mail_user_id' => $r['mail_user_id'] ?: null, // 'email' => isset($r['email']) && $r['email'] !== '' ? mb_strtolower(trim($r['email'])) : null, // ]) // ->filter(fn($r) => $r['mail_user_id'] || $r['email']) // ->values(); // // if ($rows->isEmpty()) { // $this->addError('recipients', 'Mindestens ein Empfänger erforderlich.'); // return; // } // if ($this->type === 'single' && $rows->count() !== 1) { // $this->addError('recipients', 'Bei Single-Alias ist genau ein Empfänger erlaubt.'); // return; // } // // foreach ($this->recipients as $i => $r) { // if (empty($r['mail_user_id']) && empty($r['email'])) { // $this->addError("recipients.$i", 'Wähle internes Postfach oder externe E-Mail.'); // return; // } // } // // if ($this->validateRecipientDuplicates()) { // return; // freundlich abbrechen, keine Exception // } // // // 4) Alias-Adresse vs. bestehende Mailboxen (siehe Teil B) // if ($this->aliasConflictsWithMailbox()) { // $this->addError('local', 'Diese Adresse ist bereits als Postfach vergeben.'); // return; // } // // // DB::transaction(function () use ($rows) { // /** @var MailAlias $alias */ // $alias = MailAlias::updateOrCreate( // ['id' => $this->aliasId], // [ // 'domain_id' => $this->domainId, // 'local' => $this->local, // 'type' => $this->type, // 'is_active' => $this->is_active, // 'notes' => $this->notes, // ] // ); // // // Empfänger neu schreiben (einfach & robust) // $alias->recipients()->delete(); // foreach ($rows as $i => $r) { // $alias->recipients()->create([ // 'mail_user_id' => $r['mail_user_id'], // 'email' => $r['email'], // 'position' => $i, // ]); // } // // $this->aliasId = $alias->id; // }); // // $this->dispatch('alias:' . ($this->aliasId ? 'updated' : 'created')); // $this->dispatch('closeModal'); // } // // private function aliasConflictsWithMailbox(): bool // { // if (!$this->domainId || $this->local === '') return false; // // $local = Str::lower(trim($this->local)); // // return MailUser::query() // ->where('domain_id', $this->domainId) // ->whereRaw('LOWER(localpart) = ?', [$local]) // ->exists(); // } // // protected function rules(): array // { // $occupied = MailUser::query() // ->where('domain_id', $this->domainId) // ->pluck('localpart') // ->map(fn($l) => Str::lower($l)) // ->all(); // // return [ // 'domainId' => ['required','exists:domains,id'], // 'local' => [ // 'required', // 'regex:/^[A-Za-z0-9._%+-]+$/', // Rule::unique('mail_aliases','local') // ->ignore($this->aliasId) // ->where(fn($q) => $q->where('domain_id', $this->domainId)), // Rule::notIn($occupied), // verhindert offensichtliche Kollisionen // ], // 'type' => ['required','in:single,group'], // 'recipients' => ['array','min:1','max:'.($this->type==='single'?1:$this->maxGroupRecipients)], // 'recipients.*.mail_user_id' => ['nullable','exists:mail_users,id'], // 'recipients.*.email' => ['nullable','email:rfc'], // ]; // } // // // protected function messages(): array // { // return [ // 'local.not_in' => 'Diese Adresse ist bereits als Postfach vergeben.', // 'local.unique' => 'Dieser Alias existiert in dieser Domain bereits.', // 'local.regex' => 'Erlaubt sind Buchstaben, Zahlen und ._%+-', // 'recipients.min' => 'Mindestens ein Empfänger ist erforderlich.', // 'recipients.max' => $this->type === 'single' // ? 'Bei „Single“ ist nur ein Empfänger zulässig.' // : 'Maximal '.$this->maxGroupRecipients.' Empfänger erlaubt.', // ]; // } // // // public function render() // { // return view('livewire.ui.mail.modal.alias-form-modal', [ // 'domains' => Domain::where('is_system', false)->orderBy('domain')->get(['id','domain']), // ]); // } //}