docs(mail): complete German help coverage
This commit is contained in:
@@ -253,6 +253,12 @@ split `mail:pop3:delete` when operators must never delete provider messages.
|
||||
Imported records follow configured Mail/records retention and require manual
|
||||
review for a data-subject request or deletion decision.
|
||||
|
||||
Contextual help is available from the page and from each policy-sensitive
|
||||
source setting, credential field, size limit, import action, and destructive
|
||||
confirmation. Press F1 while a control has focus to open the Mail-owned German
|
||||
reference for the exact POP3 context; the same topic remains available through
|
||||
the page help action.
|
||||
|
||||
## Profile administration
|
||||
|
||||
### Roles
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@govoplan/mail-webui",
|
||||
"version": "0.1.20",
|
||||
"version": "0.1.21",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@govoplan/mail-webui",
|
||||
"version": "0.1.20",
|
||||
"version": "0.1.21",
|
||||
"peerDependencies": {
|
||||
"@govoplan/core-webui": "^0.1.18",
|
||||
"lucide-react": "^1.23.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/mail-webui",
|
||||
"version": "0.1.20",
|
||||
"version": "0.1.21",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "webui/src/index.ts",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "govoplan-mail"
|
||||
version = "0.1.20"
|
||||
version = "0.1.21"
|
||||
description = "GovOPlaN mail module with backend and WebUI integration."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
@@ -400,7 +400,7 @@ POP3_PROVIDER = ExternalProviderDeclaration(
|
||||
manifest = ModuleManifest(
|
||||
id="mail",
|
||||
name="Mail",
|
||||
version="0.1.20",
|
||||
version="0.1.21",
|
||||
required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR),
|
||||
optional_dependencies=("campaigns", "addresses", "calendar", "postbox", "search"),
|
||||
provides_interfaces=(
|
||||
@@ -615,10 +615,75 @@ manifest = ModuleManifest(
|
||||
),
|
||||
related_modules=("audit", "records"),
|
||||
unlocks=("Governed retirement of legacy POP3 mailboxes without making destructive retrieval the default.",),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Nachrichten aus einem alten POP3-Postfach importieren",
|
||||
"summary": "Begrenzt viele POP3-Nachrichten in verschlüsselte lokale Prüfdatensätze übernehmen, ohne die Quelldaten standardmäßig zu löschen.",
|
||||
"body": (
|
||||
"POP3 ist ausschließlich ein nachrangiger Migrationsweg und wird niemals stillschweigend aktiviert. "
|
||||
"Eine Mail-Administration legt eine eigene POP3-Quelle an, aktiviert den Altimport ausdrücklich, "
|
||||
"hinterlegt Zugangsdaten verschlüsselt, prüft TLS und Anmeldung und entscheidet getrennt, ob das Löschen "
|
||||
"an der Quelle überhaupt angefordert werden darf. Importberechtigte laden eine begrenzte Live-Vorschau, "
|
||||
"wählen Nachrichten anhand stabiler UIDL-Kennungen und übernehmen sie als verschlüsselte Datensätze mit "
|
||||
"ausstehender Prüfung. Wiederholte Importe werden als Duplikate ausgewiesen und nicht erneut gespeichert. "
|
||||
"Quelldaten bleiben unverändert, solange nicht die Endpunktrichtlinie Löschen nach Import erlaubt, die "
|
||||
"ausführende Person zusätzlich mail:pop3:delete besitzt und das Löschen für den konkreten Stapel ausdrücklich "
|
||||
"ausgewählt wird. Mail schreibt und protokolliert zuerst den lokalen Import und versucht erst danach die "
|
||||
"Löschung beim Anbieter. Eine fehlgeschlagene oder unklare Löschung entfernt deshalb niemals den verwalteten "
|
||||
"lokalen Datensatz und muss vor einem weiteren destruktiven Versuch abgestimmt werden. Für den laufenden "
|
||||
"Postfachzugriff sind IMAP oder JMAP vorzuziehen."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"route": "/mail/legacy-import",
|
||||
"screen": "Legacy POP3 import",
|
||||
"help_contexts": [
|
||||
"mail.pop3",
|
||||
"mail.pop3.source-editor",
|
||||
"mail.pop3.action.reload",
|
||||
"mail.pop3.action.create-source",
|
||||
"mail.pop3.action.configure-source",
|
||||
"mail.pop3.action.save-source",
|
||||
"mail.pop3.action.test",
|
||||
"mail.pop3.action.preview",
|
||||
"mail.pop3.action.import",
|
||||
"mail.pop3.field.profile",
|
||||
"mail.pop3.field.name",
|
||||
"mail.pop3.field.host",
|
||||
"mail.pop3.field.port",
|
||||
"mail.pop3.field.transport-security",
|
||||
"mail.pop3.field.timeout",
|
||||
"mail.pop3.field.max-message-size",
|
||||
"mail.pop3.field.max-batch-size",
|
||||
"mail.pop3.field.preview-body-lines",
|
||||
"mail.pop3.field.username",
|
||||
"mail.pop3.field.password",
|
||||
"mail.pop3.field.enabled",
|
||||
"mail.pop3.field.allow-delete-after-import",
|
||||
"mail.pop3.field.message-selection",
|
||||
"mail.pop3.field.delete-after-import",
|
||||
"mail.pop3.confirm-delete-source",
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"label": "Transport security",
|
||||
"user_description": "Use TLS or STARTTLS; unencrypted transport remains subject to deployment egress policy and is not the safe default.",
|
||||
},
|
||||
{
|
||||
"label": "Message and batch limits",
|
||||
"user_description": "Bound both each downloaded message and the total imported batch before provider content enters local encrypted storage.",
|
||||
},
|
||||
{
|
||||
"label": "Explicitly enable legacy import",
|
||||
"user_description": "Keeps the source inert until endpoint metadata and its encrypted credential have both been stored.",
|
||||
},
|
||||
{
|
||||
"label": "Permit delete-after-import requests",
|
||||
"user_description": "Allows, but never selects, the separately authorized destructive request; each batch still needs explicit confirmation.",
|
||||
},
|
||||
],
|
||||
"steps": [
|
||||
"Ask a Mail administrator to configure and explicitly enable a dedicated POP3 legacy source.",
|
||||
"Test the source and refresh a bounded preview; no message flags or deletion state are changed.",
|
||||
@@ -630,6 +695,11 @@ manifest = ModuleManifest(
|
||||
"POP3 has no folder or flag semantics and is not the recommended protocol for ongoing mailbox access.",
|
||||
"Source deletion cannot be rolled back and may have an unknown outcome if the connection fails while QUIT commits deletions.",
|
||||
],
|
||||
"operational_consequences": [
|
||||
"Changing host, port, security, account identity, or import limits rotates the transport revision and invalidates stale previews.",
|
||||
"Enabling source deletion creates an irreversible external effect boundary; unknown QUIT outcomes require reconciliation rather than blind retry.",
|
||||
"Disabling legacy import leaves governed local imports and their evidence available for review while preventing new provider access.",
|
||||
],
|
||||
"verification": "Prove disabled policy, TLS and authentication diagnostics, non-destructive preview/import, UIDL duplicate prevention, encrypted raw retention, separate delete authorization, import-before-delete audit ordering, and failed or outcome-unknown deletion evidence.",
|
||||
},
|
||||
),
|
||||
@@ -656,6 +726,27 @@ manifest = ModuleManifest(
|
||||
DocumentationLink(label="Mail handbook", href="govoplan-mail/docs/MAIL_HANDBOOK.md", kind="repository"),
|
||||
),
|
||||
related_modules=("access", "audit", "campaigns", "postbox"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Mail-Daten in einer Auskunftsanfrage prüfen",
|
||||
"summary": "Mandantenbezogene Mail-Metadaten zusammentragen und dabei Transportnachweise sowie die Zuständigkeit des externen Postfachs bewahren.",
|
||||
"body": (
|
||||
"Der Mail-Provider für Betroffenenanfragen durchsucht den wirksamen Mandanten anhand normalisierter "
|
||||
"Absender-, Empfänger- oder Unzustellbarkeitsadressen, direkter Mitgliedschaftsverweise und namensraumgebundener "
|
||||
"Verweise auf Mail-Profile, Nachrichtenindizes, POP3-Importe, Zustellbefehle oder Rückläufer. Er grenzt nur "
|
||||
"passende Absender-, An- und Cc-Beteiligte ab und liefert begrenzte Inhalte aus Nachrichtenindex oder Import, "
|
||||
"sichere Metadaten persönlicher Profile sowie Zustell-, Versuch-, Abstimmungs- und Rückläufergebnisse. "
|
||||
"Ausgeschlossen bleiben SMTP-, IMAP- und POP3-Konfiguration, Benutzernamen und Zugangsdaten, verschlüsselte "
|
||||
"Nachrichten und Umschläge, POP3-UIDLs und Quellrevisionen, Ablehnungsdetails, Postfachordner und UIDs, Endpunkt- "
|
||||
"und Zugangsdatenkennungen, Idempotenz- und Worker-Ansprüche, Diagnosen, Fehlertexte und undurchsichtige Nachweise. "
|
||||
"Zustellversuche, Abstimmungsentscheidungen und Rückläuferbeobachtungen bleiben als unveränderliche Transport- "
|
||||
"und Wiederherstellungsnachweise erhalten. Nachrichtenindizes, verschlüsselte POP3-Importe und persönliche Profile "
|
||||
"müssen in Mail und im maßgeblichen externen Postfach manuell geprüft werden. Der Provider löscht nicht unmittelbar, "
|
||||
"weil das Entfernen eines abgeleiteten oder importierten Datensatzes den Zustand seiner externen Quelle nicht belegt "
|
||||
"und Änderungen an Profilen oder Zustellbefehlen Zugangsdaten, andere Personen und aufbewahrte Nachweise betreffen können."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"route": "/admin?section=tenant-data-subject-requests",
|
||||
@@ -855,6 +946,19 @@ manifest = ModuleManifest(
|
||||
documentation_types=("admin", "user"),
|
||||
audience=("mail_user", "mail_admin", "administrator"),
|
||||
related_modules=("search",),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Berechtigte Postfachnachrichten durchsuchen",
|
||||
"summary": "Begrenzte Metadaten und Vorschauen aus dem zwischengespeicherten Postfach für die berechtigungsbewusste Plattformsuche bereitstellen.",
|
||||
"body": (
|
||||
"Ist Search installiert, stellt Mail seinen nur lesbaren Postfachzwischenspeicher bereit, niemals Zugangsdaten "
|
||||
"oder unbegrenzte Rohnachrichten. Ergebnisse erfordern sowohl Leseberechtigung für das Postfach als auch "
|
||||
"Nutzungsberechtigung für das Profil. Vor jeder Ausgabe werden aktueller Profilbereich, Richtlinie, Aktivität "
|
||||
"sowie Sichtbarkeit für Mandant, Person, Gruppe oder Campaign erneut geprüft. Postfachaktualisierungen veröffentlichen "
|
||||
"dauerhafte Indexänderungen; für die Abstimmung bleibt ein vollständiger Neuaufbau verfügbar."
|
||||
),
|
||||
}
|
||||
},
|
||||
order=39,
|
||||
),
|
||||
DocumentationTopic(
|
||||
@@ -883,6 +987,17 @@ manifest = ModuleManifest(
|
||||
related_modules=("campaigns",),
|
||||
unlocks=("Campaign-local delivery rules become visible when govoplan-campaign is installed.",),
|
||||
configuration_keys=("mail_profile_policy",),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Mail-Profile und Richtlinienhierarchie",
|
||||
"summary": "Den Mail-Versand über wiederverwendbare SMTP-/IMAP-Profile und eine wirksame Richtlinie aus System-, Mandanten-, Eigentümer- und gegebenenfalls Campaign-Ebene steuern.",
|
||||
"body": (
|
||||
"Die wirksame Richtlinie entscheidet, ob Personen ausschließlich freigegebene Profile auswählen dürfen "
|
||||
"oder ob auf Personen-, Gruppen- und Campaign-Ebene zusätzliche wiederverwendbare Profile angelegt werden können. "
|
||||
"Die Laufzeitdokumentation ergänzt die aktuelle Lage des Mandanten, wenn die handelnde Person die Mail-Profilrichtlinie lesen darf."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "reference",
|
||||
"route": "/settings?section=mail-profiles",
|
||||
@@ -918,6 +1033,21 @@ manifest = ModuleManifest(
|
||||
DocumentationLink(label="Mail handbook", href="govoplan-mail/docs/MAIL_HANDBOOK.md", kind="repository"),
|
||||
),
|
||||
related_modules=("campaigns",),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Standardordner eines IMAP-Profils zuordnen",
|
||||
"summary": "Zuordnungen für Posteingang, Gesendet, Entwürfe, Papierkorb, Archiv und Junk am wiederverwendbaren Mail-Profil speichern und durch begrenzte IMAP-Erkennung vorbelegen.",
|
||||
"body": (
|
||||
"Ordnernamen sind Metadaten des Profils beziehungsweise Servers und keine Campaign-Einstellungen. Eine berechtigte "
|
||||
"Profiladministration kann Namen direkt eintragen oder die Ordnererkennung verwenden; leere Zuordnungen behalten "
|
||||
"das automatische Verhalten bei. Vorhandene Werte aus imap.sent_folder werden als Zuordnung für Gesendet angezeigt "
|
||||
"und gespeichert, während das Kompatibilitätsfeld für ältere Verbraucher synchron bleibt. Eine Campaign-spezifische "
|
||||
"Abweichung für den Gesendet-Ordner bleibt für diese Campaign maßgeblich und wird von der Profilerkennung nicht verändert. "
|
||||
"Die Erkennung listet nur die beim Anbieter sichtbaren Namen auf und legt keine externen Ordner an, benennt sie nicht um, "
|
||||
"verschiebt sie nicht und löscht sie nicht."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"route": "/settings?section=mail-profiles",
|
||||
@@ -958,6 +1088,20 @@ manifest = ModuleManifest(
|
||||
),
|
||||
),
|
||||
related_modules=("campaigns", "calendar", "audit"),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Zustellstatus und Rückläufer verarbeiten",
|
||||
"summary": "Einen berechtigten IMAP-Ordner beobachten und DSN-Ergebnisse mit dauerhaften Mail-Befehlen abgleichen.",
|
||||
"body": (
|
||||
"Mail speichert die ausgehende RFC-Message-ID zusammen mit dem dauerhaften Befehl, verarbeitet begrenzte "
|
||||
"message/delivery-status-Berichte ohne Postfachkennzeichen zu ändern und zeichnet je Empfänger idempotente "
|
||||
"Beobachtungen auf. Die SMTP-Annahme bleibt von einem späteren Rückläufer getrennt. Nicht zuordenbare Berichte "
|
||||
"bleiben zur Prüfung sichtbar; Mail speichert nur begrenzte Diagnosen und einen Digest des Rohinhalts, nicht den "
|
||||
"Rohtext des Rückläufers. Ist Calendar aktiv, leitet derselbe begrenzte Quellscan iCalendar-Teile mit METHOD:REPLY "
|
||||
"zur idempotenten Abstimmung der Teilnehmenden an Calendar weiter, ohne die Nachricht als Rückläufer einzuordnen."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"route": "/mail/bounces",
|
||||
@@ -996,6 +1140,21 @@ manifest = ModuleManifest(
|
||||
),
|
||||
related_modules=("campaigns", "access"),
|
||||
unlocks=("Reusable, governed delivery identities without cross-module secret duplication.",),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Mail besitzt Transportprofile und Zugangsdaten",
|
||||
"summary": "Andere Module wählen berechtigte Mail-Profile über stabile Kennungen aus; sie kopieren weder SMTP-/IMAP-Einstellungen noch Geheimnisse.",
|
||||
"body": (
|
||||
"Mail verschlüsselt Zugangsdaten, prüft Verbindungen, bewertet Profilbereich und Richtlinie und führt "
|
||||
"revisionsgebundene Transportwirkungen aus, ohne die aufgelöste Konfiguration zurückzugeben. Zufällige gespeicherte "
|
||||
"Revisionen wechseln, wenn sich normalisierte Transportdaten oder die Kontoidentität ändern; eine reine "
|
||||
"Passwortrotation bleibt für bereits aufgebauten fachlichen Willen transparent. Campaign speichert nur "
|
||||
"server.mail_profile_id und bereinigte Zustellnachweise. Campaign-lokale Transportfelder werden abgelehnt; "
|
||||
"ältere Campaign-Datensätze bleiben gesperrt, bis eine ausdrückliche Profilmigration den ursprünglichen "
|
||||
"Auditdatensatz bewahrt und eine bearbeitbare Version aktualisiert."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "reference",
|
||||
"route": "/settings?section=mail-profiles",
|
||||
@@ -1030,6 +1189,17 @@ manifest = ModuleManifest(
|
||||
),
|
||||
related_modules=("campaigns",),
|
||||
unlocks=("Reusable SMTP/IMAP identities that consumers can reference without handling credentials.",),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Ein wiederverwendbares Mail-Profil auswählen und prüfen",
|
||||
"summary": "Ein im aktuellen Kontext sichtbares Profil auswählen, seine SMTP- oder IMAP-Verbindung prüfen und im verbrauchenden Vorgang nur den stabilen Verweis speichern.",
|
||||
"body": (
|
||||
"Profiltests prüfen die aktuelle Verbindung und Anmeldung mit den von Mail verwalteten Zugangsdaten. Sie belegen "
|
||||
"weder eine spätere Richtlinienfreigabe in Campaign noch Zustellbarkeit, Empfängerannahme oder künftige Verfügbarkeit. "
|
||||
"Verwenden Sie die Auswahlkomponente, statt Kennungen einzugeben oder Transporteinstellungen in ein anderes Modul zu kopieren."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"route": "/settings?section=mail-profiles",
|
||||
@@ -1073,6 +1243,20 @@ manifest = ModuleManifest(
|
||||
DocumentationLink(label="Mail handbook", href="govoplan-mail/docs/MAIL_HANDBOOK.md", kind="repository"),
|
||||
),
|
||||
unlocks=("Read-only access to an authorized operational mailbox without broadening profile administration rights.",),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Ein berechtigtes Postfach ohne Änderungen lesen",
|
||||
"summary": "Ein IMAP-fähiges Profil auswählen, synchronisierte Ordner durchsuchen und begrenzte Nachrichteninhalte in der nur lesbaren Postfachoberfläche prüfen.",
|
||||
"body": (
|
||||
"Der Postfachzugriff erfordert sowohl Leseberechtigung für das Postfach als auch Nutzungsberechtigung für ein "
|
||||
"im Bereich der handelnden Person sichtbares Profil. Listen zeigen die vom Anbieter gelieferten Gelesen-/Ungelesen-Kennzeichen "
|
||||
"und die Herkunft aus Livezugriff, Zwischenspeicher oder Aktualisierung, ohne diese Zustände zu verändern. HTML-Inhalte "
|
||||
"werden isoliert und bereinigt; Anlagen, Inline-Verweise, nicht verfügbare Inhalte und Providerfehler bleiben ausdrücklich sichtbar. "
|
||||
"Das Auflisten von Ordnern oder Nachrichten darf keine Nachricht als gelesen markieren, verschieben oder löschen und keine "
|
||||
"unbegrenzten Inhalte offenlegen."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "workflow",
|
||||
"route": "/mail",
|
||||
@@ -1117,6 +1301,21 @@ manifest = ModuleManifest(
|
||||
),
|
||||
related_modules=("audit",),
|
||||
unlocks=("A testable Mail trust boundary with fail-closed egress and auditable secret disposal.",),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Mail-Zugangsdaten, Netzausgänge und Außerbetriebnahme schützen",
|
||||
"summary": "Geheimnisse in Mail belassen, jeden SMTP-/IMAP-/POP3-Gegenpunkt fest binden, Antworten begrenzen und eigene Zugangsdaten sofort mit nicht geheimem Auditnachweis löschen.",
|
||||
"body": (
|
||||
"Der Zugriff von Konnektoren auf private Netze wird bereitstellungsweit gesteuert; dennoch wird jeder erlaubte "
|
||||
"Hostname in einen freigegebenen Gegenpunkt aufgelöst und beim Aufbau des Sockets fest gebunden. Nicht unterstützte "
|
||||
"Transporte scheitern vor dem Verbindungsaufbau. Beim Löschen eines Profils werden dessen verschlüsselte SMTP-, IMAP- "
|
||||
"und POP3-Passwörter sofort entfernt und, sofern Geheimnisse vorhanden waren, mit nicht geheimen Angaben protokolliert. "
|
||||
"Scheitert das Entfernen oder das Audit, wird die Aktion zurückgerollt. Verschlüsselte POP3-Importinhalte folgen der "
|
||||
"Aufbewahrung von Mail beziehungsweise Records. Bei einer destruktiven Modulaußerbetriebnahme gilt dieselbe Regel: "
|
||||
"Zuerst werden Geheimnisse entfernt, erst danach alle Mail-Tabellen einschließlich der Importdaten gelöscht."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "reference",
|
||||
"route": "/settings?section=mail-profiles",
|
||||
@@ -1152,6 +1351,27 @@ manifest = ModuleManifest(
|
||||
),
|
||||
related_modules=("campaigns", "audit"),
|
||||
unlocks=("Profile-backed campaign delivery without cross-module credential or configuration copies.",),
|
||||
translations={
|
||||
"de": {
|
||||
"title": "Campaign über den Mail-Zustellvertrag integrieren",
|
||||
"summary": "Campaign friert Profilverweis und undurchsichtige Revision ein; Mail berechtigt erneut, prüft die Revision, löst Zugangsdaten auf und führt die Wirkung in einem Aufruf aus.",
|
||||
"body": (
|
||||
"Der Vertrag mail.campaign_delivery 0.2 gibt niemals entschlüsselte Zugangsdaten oder aufgelöste SMTP-/IMAP-Konfiguration zurück. "
|
||||
"Mail vergleicht die erwartete zufällige Transportrevision vor dem Entschlüsseln protokollspezifischer Zugangsdaten und liefert "
|
||||
"nur begrenzte, bereinigte Ergebnisse. Synchrone Stapel berechtigen die vollständige Empfängermenge und prüfen DNS, Netzausgang, "
|
||||
"Verbindung, TLS und Anmeldung vor der ersten Wirkung. Wenn die Bereitstellungsrichtlinie es erlaubt, verwendet Mail die begrenzte "
|
||||
"Verbindung erneut, prüft sie zuvor und verbindet vor der nächsten Nachricht neu, sobald sie veraltet ist. Ein Verbindungsverlust nach "
|
||||
"Beginn von DATA bleibt mit unbekanntem Ergebnis bestehen und wird niemals automatisch wiederholt. Systemische Anmelde-, Absender- oder "
|
||||
"Verbindungsfehler tragen stabile Ursachencodes, damit Campaign verbleibende Aufträge anhält und Verbindung, Neuverbindung, Fehler und "
|
||||
"Pausenfortschritt anzeigt. Campaign besitzt gewöhnliche Empfängeraufträge; Berichtsnachrichten nutzen das verschlüsselte, idempotente "
|
||||
"Zustellbefehls- und Versuchsledger von Mail. Jeder aktuelle SMTP- und Gesendet-Ordner-Versuch übergibt vor dem Providerkontakt eine "
|
||||
"stabile Wirkungskennung an einen von Mail besessenen Core-Wiederherstellungsvorgang. Der Nachweis des Wirkungsbeginns verhindert blinde "
|
||||
"Wiederzustellung; unbekannte Ergebnisse erfordern ausdrückliche Abstimmung, und rohe Empfängerablehnungen erfordern Mail-Diagnoseberechtigung. "
|
||||
"Mail-Ausgangsverarbeitung und Aufbewahrungsläufe werden nach Mandantenberechtigung partitioniert. Das Deaktivieren von Mail lässt bereits "
|
||||
"angenommene Befehle und Nachweise daher zur Klärung durch den Betrieb unverändert bestehen."
|
||||
),
|
||||
}
|
||||
},
|
||||
metadata={
|
||||
"kind": "reference",
|
||||
"route": "/campaigns/{campaign_id}/mail-settings",
|
||||
|
||||
@@ -94,6 +94,34 @@ class MailManifestTests(unittest.TestCase):
|
||||
pop3_topic = topics["mail.workflow.legacy-pop3-import"]
|
||||
self.assertEqual(("user", "admin"), pop3_topic.documentation_types)
|
||||
self.assertIn("mail:pop3:import", pop3_topic.conditions[0].any_scopes)
|
||||
self.assertTrue(
|
||||
{
|
||||
"mail.pop3",
|
||||
"mail.pop3.source-editor",
|
||||
"mail.pop3.action.reload",
|
||||
"mail.pop3.action.save-source",
|
||||
"mail.pop3.action.import",
|
||||
"mail.pop3.field.transport-security",
|
||||
"mail.pop3.field.max-message-size",
|
||||
"mail.pop3.field.password",
|
||||
"mail.pop3.field.delete-after-import",
|
||||
"mail.pop3.confirm-delete-source",
|
||||
}.issubset(pop3_topic.metadata["help_contexts"])
|
||||
)
|
||||
self.assertGreaterEqual(len(pop3_topic.metadata["fields"]), 4)
|
||||
self.assertGreaterEqual(
|
||||
len(pop3_topic.metadata["operational_consequences"]),
|
||||
3,
|
||||
)
|
||||
|
||||
for topic in manifest.documentation:
|
||||
with self.subTest(topic_id=topic.id):
|
||||
german = topic.translations.get("de", {})
|
||||
for attribute in ("title", "summary", "body"):
|
||||
self.assertTrue(
|
||||
str(german.get(attribute, "")).strip(),
|
||||
f"{topic.id} is missing its German {attribute}",
|
||||
)
|
||||
|
||||
pop3_provider = next(
|
||||
item
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@govoplan/mail-webui",
|
||||
"version": "0.1.20",
|
||||
"version": "0.1.21",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@govoplan/mail-webui",
|
||||
"version": "0.1.20",
|
||||
"version": "0.1.21",
|
||||
"devDependencies": {
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@govoplan/mail-webui",
|
||||
"version": "0.1.20",
|
||||
"version": "0.1.21",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -47,6 +47,25 @@ for (const sharedComponent of ["PageLayout", "PageActionBar", "SelectionList", "
|
||||
assert.match(legacyImport, /archetype="collection"/);
|
||||
assert.match(legacyImport, /variant="collection"[\s\S]*refreshable[\s\S]*reloadAction=/);
|
||||
assert.match(legacyImport, /topicId: "mail\.workflow\.legacy-pop3-import"/);
|
||||
assert.match(legacyImport, /helpContextId="mail\.pop3"[\s\S]*helpTopicId="mail\.workflow\.legacy-pop3-import"/);
|
||||
for (const helpContextId of [
|
||||
"mail.pop3.source-editor",
|
||||
"mail.pop3.action.reload",
|
||||
"mail.pop3.action.save-source",
|
||||
"mail.pop3.action.import",
|
||||
"mail.pop3.field.transport-security",
|
||||
"mail.pop3.field.max-message-size",
|
||||
"mail.pop3.field.max-batch-size",
|
||||
"mail.pop3.field.password",
|
||||
"mail.pop3.field.delete-after-import",
|
||||
"mail.pop3.confirm-delete-source",
|
||||
]) {
|
||||
assert.match(
|
||||
legacyImport,
|
||||
new RegExp(`helpContextId(?:=|:\\s*)"${helpContextId.replaceAll(".", "\\.")}"`)
|
||||
);
|
||||
}
|
||||
assert.match(legacyImport, /data-help-context-id="mail\.pop3\.field\.message-selection"/);
|
||||
assert.match(legacyImport, /<ConfirmDialog[\s\S]*tone="danger"[\s\S]*onConfirm=\{\(\) => void runImport\(\)\}/);
|
||||
assert.match(legacyImport, /legacy_import_enabled: false[\s\S]*is_active: false[\s\S]*createMailServerCredential[\s\S]*updateMailServerEndpoint/);
|
||||
assert.match(legacyImport, /expected_transport_revision: preview\.transport_revision/);
|
||||
|
||||
@@ -338,6 +338,8 @@ export default function MailLegacyImportPage({
|
||||
render: (item) => <input
|
||||
type="checkbox"
|
||||
aria-label={`Select ${item.subject || item.uidl}`}
|
||||
data-help-context-id="mail.pop3.field.message-selection"
|
||||
data-help-module-id="mail"
|
||||
checked={selectedUidls.includes(item.uidl)}
|
||||
disabled={Boolean(busy) || item.already_imported}
|
||||
onChange={() => setSelectedUidls((current) =>
|
||||
@@ -378,13 +380,16 @@ export default function MailLegacyImportPage({
|
||||
archetype="collection"
|
||||
title="Legacy POP3 import"
|
||||
description="Migrate bounded messages into encrypted local review records. POP3 is disabled by default and is not recommended for ongoing mailbox access."
|
||||
helpContextId="mail.pop3"
|
||||
helpModuleId="mail"
|
||||
helpTopicId="mail.workflow.legacy-pop3-import"
|
||||
actions={<PageActionBar
|
||||
variant="collection"
|
||||
refreshable
|
||||
reloadAction={{ onReload: () => void load(), disabled: Boolean(busy), disabledReason: busy ? "Wait for the current POP3 action to finish." : undefined }}
|
||||
reloadAction={{ onReload: () => void load(), helpContextId: "mail.pop3.action.reload", helpModuleId: "mail", helpTopicId: "mail.workflow.legacy-pop3-import", disabled: Boolean(busy), disabledReason: busy ? "Wait for the current POP3 action to finish." : undefined }}
|
||||
contextActions={<Button onClick={() => navigate("/mail")}><ArrowLeft size={16} aria-hidden="true" /> Mailbox</Button>}
|
||||
helpAction={<DocumentationHelpLink reference={DOCUMENTATION} />}
|
||||
createAction={canManage ? <Button variant="primary" onClick={() => openSourceDialog(null)} disabled={Boolean(busy) || configurableProfiles.length === 0} disabledReason={configurableProfiles.length === 0 ? "Create or gain write access to a Mail profile first." : undefined}><Plus size={16} aria-hidden="true" /> Add legacy source</Button> : undefined}
|
||||
createAction={canManage ? <Button helpContextId="mail.pop3.action.create-source" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import" variant="primary" onClick={() => openSourceDialog(null)} disabled={Boolean(busy) || configurableProfiles.length === 0} disabledReason={configurableProfiles.length === 0 ? "Create or gain write access to a Mail profile first." : undefined}><Plus size={16} aria-hidden="true" /> Add legacy source</Button> : undefined}
|
||||
/>}
|
||||
loading={loading}
|
||||
loadingLabel="Loading legacy POP3 sources"
|
||||
@@ -419,6 +424,9 @@ export default function MailLegacyImportPage({
|
||||
icon: <Pencil aria-hidden="true" />,
|
||||
disabled: Boolean(busy),
|
||||
disabledReason: busy ? "Wait for the current POP3 action to finish." : "",
|
||||
helpContextId: "mail.pop3.action.configure-source",
|
||||
helpModuleId: "mail",
|
||||
helpTopicId: "mail.workflow.legacy-pop3-import",
|
||||
onClick: () => openSourceDialog(selectedSource)
|
||||
}]} /> : undefined}
|
||||
>
|
||||
@@ -427,8 +435,8 @@ export default function MailLegacyImportPage({
|
||||
<FormField label="Policy"><StatusBadge status={selectedSource.server.config.legacy_import_enabled ? "success" : "inactive"} label={selectedSource.server.config.legacy_import_enabled ? "explicitly enabled" : "disabled"} /></FormField>
|
||||
<FormField label="Transport"><span>{selectedSource.server.config.security || "tls"} · {String(selectedSource.server.config.port || 995)}</span></FormField>
|
||||
<FormField label="Credential"><span>{selectedCredential ? String(selectedCredential.public_data?.username || selectedCredential.name) : "No credential"}</span></FormField>
|
||||
<Button onClick={() => void runConnectionTest()} disabled={Boolean(busy) || !selectedCredential || !selectedSource.server.config.legacy_import_enabled} disabledReason={!selectedCredential ? "Store an encrypted POP3 credential first." : !selectedSource.server.config.legacy_import_enabled ? "Explicitly enable legacy import first." : busy ? "Wait for the current POP3 action to finish." : undefined}>Test connection</Button>
|
||||
{canImport ? <Button variant="primary" onClick={() => void refreshPreview()} disabled={Boolean(busy) || !selectedCredential || !selectedSource.server.config.legacy_import_enabled} disabledReason={!selectedCredential ? "Store an encrypted POP3 credential first." : !selectedSource.server.config.legacy_import_enabled ? "Explicitly enable legacy import first." : busy ? "Wait for the current POP3 action to finish." : undefined}>Refresh live preview</Button> : null}
|
||||
<Button helpContextId="mail.pop3.action.test" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import" onClick={() => void runConnectionTest()} disabled={Boolean(busy) || !selectedCredential || !selectedSource.server.config.legacy_import_enabled} disabledReason={!selectedCredential ? "Store an encrypted POP3 credential first." : !selectedSource.server.config.legacy_import_enabled ? "Explicitly enable legacy import first." : busy ? "Wait for the current POP3 action to finish." : undefined}>Test connection</Button>
|
||||
{canImport ? <Button helpContextId="mail.pop3.action.preview" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import" variant="primary" onClick={() => void refreshPreview()} disabled={Boolean(busy) || !selectedCredential || !selectedSource.server.config.legacy_import_enabled} disabledReason={!selectedCredential ? "Store an encrypted POP3 credential first." : !selectedSource.server.config.legacy_import_enabled ? "Explicitly enable legacy import first." : busy ? "Wait for the current POP3 action to finish." : undefined}>Refresh live preview</Button> : null}
|
||||
</FormGrid> : <p className="muted">Select or configure a legacy source.</p>}
|
||||
</Card>
|
||||
</ContentGrid>
|
||||
@@ -438,8 +446,8 @@ export default function MailLegacyImportPage({
|
||||
<p className="muted">Provider reports {preview.message_count} message(s), {formatBytes(preview.mailbox_size_bytes)} total. Preview and ordinary import do not delete source messages.</p>
|
||||
<DataGrid id="mail-pop3-preview" rows={preview.messages} columns={previewColumns} getRowKey={(item) => item.uidl} emptyText="The legacy mailbox contains no messages." />
|
||||
<FormGrid columns={2} collapseAt="standard" spacing="block">
|
||||
<ToggleSwitch checked={deleteAfterImport} disabled={!canDelete || !preview.delete_after_import_allowed || Boolean(busy)} onChange={setDeleteAfterImport} label="Delete newly imported messages at the source" help="Destructive and separately governed. The local encrypted import is committed first." />
|
||||
<Button variant={deleteAfterImport ? "danger" : "primary"} onClick={() => deleteAfterImport ? setDeleteConfirmationOpen(true) : void runImport()} disabled={Boolean(busy) || Boolean(importBlocker)} disabledReason={importBlocker || (busy ? "Wait for the current POP3 action to finish." : undefined)}>Import {selectedUidls.length || "selected"} message(s)</Button>
|
||||
<ToggleSwitch helpContextId="mail.pop3.field.delete-after-import" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import" checked={deleteAfterImport} disabled={!canDelete || !preview.delete_after_import_allowed || Boolean(busy)} onChange={setDeleteAfterImport} label="Delete newly imported messages at the source" help="Destructive and separately governed. The local encrypted import is committed first." />
|
||||
<Button helpContextId="mail.pop3.action.import" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import" variant={deleteAfterImport ? "danger" : "primary"} onClick={() => deleteAfterImport ? setDeleteConfirmationOpen(true) : void runImport()} disabled={Boolean(busy) || Boolean(importBlocker)} disabledReason={importBlocker || (busy ? "Wait for the current POP3 action to finish." : undefined)}>Import {selectedUidls.length || "selected"} message(s)</Button>
|
||||
</FormGrid>
|
||||
</> : <p className="muted">Refresh a source to obtain a bounded, non-destructive preview.</p>}
|
||||
</Card>
|
||||
@@ -452,34 +460,37 @@ export default function MailLegacyImportPage({
|
||||
<Dialog
|
||||
open={sourceDialogOpen}
|
||||
title={editingSourceId ? "Configure legacy POP3 source" : "Add legacy POP3 source"}
|
||||
helpContextId="mail.pop3.source-editor"
|
||||
helpModuleId="mail"
|
||||
helpTopicId="mail.workflow.legacy-pop3-import"
|
||||
onClose={() => !busy && setSourceDialogOpen(false)}
|
||||
footer={<><Button onClick={() => setSourceDialogOpen(false)} disabled={Boolean(busy)}>Cancel</Button><Button variant="primary" onClick={() => void saveSource()} disabled={Boolean(busy) || Boolean(sourceBlocker)} disabledReason={sourceBlocker || (busy ? "Wait for the source to be saved." : undefined)}>Save source</Button></>}
|
||||
footer={<><Button onClick={() => setSourceDialogOpen(false)} disabled={Boolean(busy)}>Cancel</Button><Button helpContextId="mail.pop3.action.save-source" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import" variant="primary" onClick={() => void saveSource()} disabled={Boolean(busy) || Boolean(sourceBlocker)} disabledReason={sourceBlocker || (busy ? "Wait for the source to be saved." : undefined)}>Save source</Button></>}
|
||||
>
|
||||
<FormGrid columns={2} collapseAt="standard">
|
||||
<FormField label="Mail profile" help="The profile supplies scope and lifecycle ownership for this dedicated source.">
|
||||
<FormField label="Mail profile" help="The profile supplies scope and lifecycle ownership for this dedicated source." helpContextId="mail.pop3.field.profile" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import">
|
||||
<select value={sourceDraft.profileId} disabled={Boolean(editingSourceId) || Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, profileId: event.target.value }))}>
|
||||
<option value="">Select a profile</option>
|
||||
{configurableProfiles.map((profile) => <option key={profile.id} value={profile.id}>{profile.name} ({profile.scope_type})</option>)}
|
||||
</select>
|
||||
</FormField>
|
||||
<FormField label="Source name"><input value={sourceDraft.name} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, name: event.target.value }))} /></FormField>
|
||||
<FormField label="POP3 host"><input value={sourceDraft.host} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, host: event.target.value }))} placeholder="pop3.example.org" /></FormField>
|
||||
<FormField label="Port"><input type="number" min={1} max={65535} value={sourceDraft.port} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, port: event.target.value }))} /></FormField>
|
||||
<FormField label="Transport security">
|
||||
<FormField label="Source name" helpContextId="mail.pop3.field.name" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import"><input value={sourceDraft.name} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, name: event.target.value }))} /></FormField>
|
||||
<FormField label="POP3 host" helpContextId="mail.pop3.field.host" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import"><input value={sourceDraft.host} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, host: event.target.value }))} placeholder="pop3.example.org" /></FormField>
|
||||
<FormField label="Port" helpContextId="mail.pop3.field.port" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import"><input type="number" min={1} max={65535} value={sourceDraft.port} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, port: event.target.value }))} /></FormField>
|
||||
<FormField label="Transport security" helpContextId="mail.pop3.field.transport-security" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import">
|
||||
<select value={sourceDraft.security} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, security: event.target.value as SourceDraft["security"], port: event.target.value === "tls" ? "995" : "110" }))}>
|
||||
<option value="tls">TLS</option>
|
||||
<option value="starttls">STARTTLS</option>
|
||||
<option value="plain">Plain (deployment policy may deny)</option>
|
||||
</select>
|
||||
</FormField>
|
||||
<FormField label="Timeout (seconds)"><input type="number" min={1} max={300} value={sourceDraft.timeoutSeconds} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, timeoutSeconds: event.target.value }))} /></FormField>
|
||||
<FormField label="Maximum message size (MiB)"><input type="number" min={1} max={50} value={sourceDraft.maxMessageMiB} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, maxMessageMiB: event.target.value }))} /></FormField>
|
||||
<FormField label="Maximum batch size (MiB)"><input type="number" min={1} max={500} value={sourceDraft.maxBatchMiB} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, maxBatchMiB: event.target.value }))} /></FormField>
|
||||
<FormField label="Preview body lines"><input type="number" min={0} max={100} value={sourceDraft.previewBodyLines} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, previewBodyLines: event.target.value }))} /></FormField>
|
||||
<FormField label="Username"><input value={sourceDraft.username} disabled={Boolean(busy) || !canManageSecrets} onChange={(event) => setSourceDraft((draft) => ({ ...draft, username: event.target.value }))} autoComplete="username" /></FormField>
|
||||
<FormField label={editingSourceId ? "New password (optional)" : "Password"}><input type="password" value={sourceDraft.password} disabled={Boolean(busy) || !canManageSecrets} onChange={(event) => setSourceDraft((draft) => ({ ...draft, password: event.target.value }))} autoComplete="new-password" /></FormField>
|
||||
<ToggleSwitch checked={sourceDraft.enabled} disabled={Boolean(busy)} onChange={(enabled) => setSourceDraft((draft) => ({ ...draft, enabled, allowDeleteAfterImport: enabled ? draft.allowDeleteAfterImport : false }))} label="Explicitly enable legacy import" help="Off is the product default." />
|
||||
<ToggleSwitch checked={sourceDraft.allowDeleteAfterImport} disabled={Boolean(busy) || !sourceDraft.enabled} onChange={(allowDeleteAfterImport) => setSourceDraft((draft) => ({ ...draft, allowDeleteAfterImport }))} label="Permit delete-after-import requests" help="Operators still need a separate destructive permission and must choose deletion per batch." />
|
||||
<FormField label="Timeout (seconds)" helpContextId="mail.pop3.field.timeout" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import"><input type="number" min={1} max={300} value={sourceDraft.timeoutSeconds} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, timeoutSeconds: event.target.value }))} /></FormField>
|
||||
<FormField label="Maximum message size (MiB)" helpContextId="mail.pop3.field.max-message-size" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import"><input type="number" min={1} max={50} value={sourceDraft.maxMessageMiB} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, maxMessageMiB: event.target.value }))} /></FormField>
|
||||
<FormField label="Maximum batch size (MiB)" helpContextId="mail.pop3.field.max-batch-size" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import"><input type="number" min={1} max={500} value={sourceDraft.maxBatchMiB} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, maxBatchMiB: event.target.value }))} /></FormField>
|
||||
<FormField label="Preview body lines" helpContextId="mail.pop3.field.preview-body-lines" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import"><input type="number" min={0} max={100} value={sourceDraft.previewBodyLines} disabled={Boolean(busy)} onChange={(event) => setSourceDraft((draft) => ({ ...draft, previewBodyLines: event.target.value }))} /></FormField>
|
||||
<FormField label="Username" helpContextId="mail.pop3.field.username" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import"><input value={sourceDraft.username} disabled={Boolean(busy) || !canManageSecrets} onChange={(event) => setSourceDraft((draft) => ({ ...draft, username: event.target.value }))} autoComplete="username" /></FormField>
|
||||
<FormField label={editingSourceId ? "New password (optional)" : "Password"} helpContextId="mail.pop3.field.password" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import"><input type="password" value={sourceDraft.password} disabled={Boolean(busy) || !canManageSecrets} onChange={(event) => setSourceDraft((draft) => ({ ...draft, password: event.target.value }))} autoComplete="new-password" /></FormField>
|
||||
<ToggleSwitch helpContextId="mail.pop3.field.enabled" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import" checked={sourceDraft.enabled} disabled={Boolean(busy)} onChange={(enabled) => setSourceDraft((draft) => ({ ...draft, enabled, allowDeleteAfterImport: enabled ? draft.allowDeleteAfterImport : false }))} label="Explicitly enable legacy import" help="Off is the product default." />
|
||||
<ToggleSwitch helpContextId="mail.pop3.field.allow-delete-after-import" helpModuleId="mail" helpTopicId="mail.workflow.legacy-pop3-import" checked={sourceDraft.allowDeleteAfterImport} disabled={Boolean(busy) || !sourceDraft.enabled} onChange={(allowDeleteAfterImport) => setSourceDraft((draft) => ({ ...draft, allowDeleteAfterImport }))} label="Permit delete-after-import requests" help="Operators still need a separate destructive permission and must choose deletion per batch." />
|
||||
</FormGrid>
|
||||
</Dialog>
|
||||
|
||||
@@ -488,6 +499,9 @@ export default function MailLegacyImportPage({
|
||||
title="Import and delete source messages"
|
||||
message={`Mail will first commit and audit ${selectedUidls.length} encrypted local import(s), then ask the POP3 server to delete only those newly imported messages. Provider deletion cannot be undone and may require reconciliation if its outcome is unknown.`}
|
||||
confirmLabel="Import, then delete source"
|
||||
helpContextId="mail.pop3.confirm-delete-source"
|
||||
helpModuleId="mail"
|
||||
helpTopicId="mail.workflow.legacy-pop3-import"
|
||||
tone="danger"
|
||||
busy={Boolean(busy)}
|
||||
onCancel={() => setDeleteConfirmationOpen(false)}
|
||||
|
||||
Reference in New Issue
Block a user