Compare commits

..
3 Commits
Author SHA1 Message Date
zemion ecc1283de7 feat: contribute Messages product surface
Module Package Release / publish-packages (push) Successful in 11s
2026-08-24 12:53:44 +02:00
zemion 48205e6e46 fix(webui): bind reusable mail credentials to help
Module Package Release / publish-packages (push) Successful in 11s
2026-08-24 11:36:41 +02:00
zemion c31f9b69cb docs: complete German structured documentation
Module Package Release / publish-packages (push) Successful in 11s
2026-08-24 01:15:35 +02:00
9 changed files with 282 additions and 15 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@govoplan/mail-webui",
"version": "0.1.22",
"version": "0.1.25",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@govoplan/mail-webui",
"version": "0.1.22",
"version": "0.1.25",
"peerDependencies": {
"@govoplan/core-webui": "^0.1.18",
"lucide-react": "^1.23.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@govoplan/mail-webui",
"version": "0.1.22",
"version": "0.1.25",
"private": true,
"type": "module",
"main": "webui/src/index.ts",
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "govoplan-mail"
version = "0.1.22"
version = "0.1.25"
description = "GovOPlaN mail module with backend and WebUI integration."
readme = "README.md"
requires-python = ">=3.12"
@@ -0,0 +1,214 @@
"""German translations for public structured documentation metadata."""
from __future__ import annotations
from typing import Any
GERMAN_STRUCTURED_TRANSLATIONS: dict[str, dict[str, Any]] = {'mail.bounce-processing': {'verification': 'Senden Sie eine Nachricht mit einer eindeutigen '
'Message-ID, nehmen Sie zweimal einen DSN auf und '
'überprüfen Sie eine korrelierte Beobachtung, während '
'die SMTP-Akzeptanz intakt bleibt.'},
'mail.privacy.data-subject-requests': {'limitations': ['Verschlüsselte ausgehende Nutzlasten '
'können vom Empfänger nicht ohne eine '
'unabhängig bestätigte '
'Mail-Befehlsreferenz durchsucht werden.',
'Die externe Mailbox-Löschung befindet '
'sich außerhalb des DSAR-Anbieters und '
'muss mit dem konfigurierten Anbieter '
'koordiniert werden.'],
'steps': ['Führen Sie die Mail-Provider-Suche und '
'Überprüfung von Mailbox-Index, POP3-Import, '
'Profil, Lieferung, Abgleich und '
'Bounce-Dispositionen aus.',
'Bewahren Sie den unveränderlichen '
'Transportnachweis mit seinem Grund auf.',
'Koordinieren Sie die genehmigte '
'Mailbox-Inhaltelöschung mit der autoritativen '
'externen Mailbox und aktualisieren Sie dann den '
'abgeleiteten Index.',
'Verwenden Sie '
'E-Mail-Profillebenszykluskontrollen für '
'genehmigte Änderungen des persönlichen Profils; '
'Bearbeiten Sie nicht direkt verschlüsselte '
'Nutzlast- oder Beweiszeilen.']},
'mail.profile-standard-folder-mappings': {'steps': ['Öffnen Sie einen editierbaren IMAP-Server in '
'einem wiederverwendbaren Mail-Profil.',
'Wählen Sie Ordner erkennen, um die vom '
'Anbieter sichtbaren Ordnernamen zu laden, '
'oder geben Sie genaue Namen manuell ein.',
'Wenden Sie die erkannten Zuordnungen an, '
'überprüfen Sie jede Rolle und lassen Sie '
'unsichere Rollen für automatisches Verhalten '
'leer.',
'Speichern Sie das Profil und laden Sie es '
'neu, um zu überprüfen, ob die Zuordnungen '
'beibehalten wurden.'],
'verification': 'Bestätigen Sie, dass ältere '
'Sent-only-Profile das gleiche '
'effektive Sent-Mapping zeigen, und '
'bestätigen Sie, dass ein '
'kampagnenlokaler Sent-Override '
'unverändert bleibt.'},
'mail.reference.campaign-delivery-contract': {'verification': 'Nachweisen Sie, dass veraltete '
'Revisionen vor der Entschlüsselung '
'fehlschlagen, der Batch-Preflight '
'vor DATA fehlschlägt, zwei '
'Nachrichten eine gesunde '
'Verbindung wiederverwenden, eine '
'veraltete Verbindung vor der '
'nächsten Nachricht wieder '
'verbunden wird, die Trennung nach '
'der DATA nie wiedergegeben wird, '
'systemische Ausfälle verbleibende '
'Jobs anhalten, Anbieterdetails '
'werden gelöscht und das Interface '
'/ Versionsgate wird übergeben.'},
'mail.reference.credentials-egress-retirement': {'verification': 'Testen Sie DNS-Rebinding und '
'verweigerte Adressen, beweisen '
'Sie, dass der verbundene Peer '
'angeheftet ist, injizieren Sie '
'Credential-Scrub- und '
'Auditfehler, wiederholen Sie '
'die Löschung für Idempotenz und '
'führen Sie den '
'Ruhestands-Preflight gegen '
'einen Snapshot aus.'},
'mail.workflow.choose-and-test-profile': {'outcome': 'Die verbrauchende Aufgabe verweist auf ein '
'verfügbares Mail-eigenes Profil und enthält '
'keine kopierte Transportkonfiguration oder '
'Anmeldeinformationen.',
'prerequisites': ['Mail ist installiert und Sie können '
'Profile lesen, verwenden und testen, '
'die im aktuellen Kontext sichtbar '
'sind.',
'Ein Profiladministrator hat '
'Anmeldeinformationen und effektive '
'Richtlinien konfiguriert.'],
'steps': ['Öffnen Sie Mail-Profile und wählen Sie ein '
'sichtbares aktives Profil.',
'Überprüfen Sie den Sicherheitsumfang, die '
'Verfügbarkeit von SMTP/IMAP/JMAP und die '
'Absenderidentität, ohne Anmeldewerte zu '
'erwarten.',
'Führen Sie den entsprechenden SMTP-, IMAP- '
'oder '
'JMAP-Konnektivitäts-/Authentifizierungstest '
'zuerst gegen ein Nicht-Produktionsziel aus.',
'Kehren Sie zur verbrauchenden Aufgabe zurück '
'und wählen Sie dasselbe Profil über den '
'Picker aus.'],
'verification': 'Laden Sie beide Oberflächen neu, '
'bestätigen Sie, dass nur die stabile '
'Referenz vom Verbraucher beibehalten '
'wird, und führen Sie die eigene '
'kontextbezogene Richtlinienvalidierung '
'des Verbrauchers durch.'},
'mail.workflow.legacy-pop3-import': {'fields': [{'label': 'Verkehrssicherheit',
'user_description': 'Verwenden Sie TLS oder '
'STARTTLS; unverschlüsselter '
'Transport unterliegt '
'weiterhin der '
'Deployment-Egress-Richtlinie '
'und ist nicht der sichere '
'Standard.'},
{'label': 'Grenzwerte für Nachrichten und Chargen',
'user_description': 'Bound sowohl jede '
'heruntergeladene Nachricht '
'als auch die gesamte '
'importierte Charge, bevor '
'der Anbieterinhalt in den '
'lokalen verschlüsselten '
'Speicher gelangt.'},
{'label': 'Ausdrücklich Legacy Import ermöglichen',
'user_description': 'Hält die Quelle inaktiv, '
'bis die Endpunktmetadaten '
'und ihre verschlüsselten '
'Anmeldeinformationen beide '
'gespeichert wurden.'},
{'label': 'Anträge auf Löschung nach Einfuhr',
'user_description': 'Erlaubt, aber wählt niemals '
'die separat autorisierte '
'destruktive Anforderung '
'aus; jede Charge benötigt '
'noch eine explizite '
'Bestätigung.'}],
'limitations': ['Ein POP3-Server muss stabile '
'UIDL-Identifikatoren bereitstellen; '
'ansonsten ist eine sichere doppelte '
'Verhinderung nicht verfügbar und der Import '
'wird abgelehnt.',
'POP3 hat keine Ordner- oder Flag-Semantik '
'und ist nicht das empfohlene Protokoll für '
'den laufenden Mailbox-Zugriff.',
'Die Quelllöschung kann nicht zurückgesetzt '
'werden und hat möglicherweise ein '
'unbekanntes Ergebnis, wenn die Verbindung '
'fehlschlägt, während QUIT Löschungen '
'festlegt.'],
'operational_consequences': ['Durch das Ändern von Host-, '
'Port-, Sicherheits-, '
'Kontoidentitäts- oder '
'Importlimits wird die '
'Transportrevision geändert und '
'veraltete Previews ungültig '
'gemacht.',
'Durch das Ermöglichen des '
'Löschens von Quellen entsteht '
'eine irreversible Grenze für '
'externe Effekte; unbekannte '
'QUIT-Ergebnisse erfordern eine '
'Versöhnung statt eines blinden '
'Wiederholens.',
'Die Deaktivierung von '
'Legacy-Importen führt zu '
'regulierten lokalen Importen '
'und deren Nachweisen, die zur '
'Überprüfung zur Verfügung '
'stehen, während der Zugang '
'neuer Anbieter verhindert '
'wird.'],
'steps': ['Bitten Sie einen Mail-Administrator, eine '
'dedizierte POP3-Altquelle zu konfigurieren und '
'explizit zu aktivieren.',
'Testen Sie die Quelle und aktualisieren Sie eine '
'begrenzte Vorschau; Es werden keine '
'Nachrichtenflags oder Löschstatus geändert.',
'Wählen Sie Nachrichten aus und importieren Sie '
'sie in verschlüsselte Pending-Review-Datensätze.',
'Verwenden Sie delete-after-import nur, wenn '
'Richtlinien und eine separate destruktive '
'Berechtigung dies zulassen, und versöhnen Sie '
'dann fehlgeschlagene oder unbekannte Ergebnisse.'],
'verification': 'Nachweisen Sie deaktivierte Richtlinien, '
'TLS- und Authentifizierungsdiagnostik, '
'zerstörungsfreie Vorschau / Import, '
'UIDL-Duplikate-Verhinderung, verschlüsselte '
'Rohdatenspeicherung, separate '
'Löschberechtigung, '
'Import-Vor-Löschen-Auditbestellung und '
'fehlgeschlagene oder ergebnisunbekannte '
'Löschnachweise.'},
'mail.workflow.read-mailbox': {'outcome': 'Die erforderliche Nachricht wurde inspiziert, ohne den '
'Status der Provider-Mailbox zu ändern.',
'prerequisites': ['Ein aktives sichtbares Profil hat IMAP oder '
'JMAP konfiguriert.',
'Sie können beide dieses profil verwenden und '
'seine mailbox lesen.'],
'steps': ['Öffnen Sie Mail und wählen Sie ein autorisiertes IMAP- '
'oder JMAP-fähiges Profil.',
'Wählen Sie einen Ordner aus, überprüfen Sie das '
'Live/Cache-Synchronisationslabel und stellen Sie den '
'begrenzten Nachrichtenindex auf die Seite; JMAP-Suchen '
'werden beim Anbieter ausgeführt.',
'Verwenden Sie den vom Anbieter abgeleiteten '
'Read/Unread-Indikator und öffnen Sie dann nur die für '
'die Aufgabe benötigte Nachricht.',
'Wechseln Sie bei Bedarf zwischen sicheren Klartext- und '
'isolierten HTML-Ansichten und überprüfen Sie die '
'Anhang- oder Nichtverfügbarkeitsangaben, bevor Sie die '
'Vorschau schließen.'],
'verification': 'Aktualisieren Sie das Provider-Postfach '
'unabhängig und bestätigen Sie, dass keine Lese-, '
'Verschiebe-, Lösch-, Antwort- oder Flag-Mutation '
'durch GovOPlaN verursacht wurde.'}}
+50 -1
View File
@@ -1,5 +1,8 @@
from __future__ import annotations
from govoplan_core.core.modules import with_documentation_structured_translations
from govoplan_mail.backend.german_structured_documentation import GERMAN_STRUCTURED_TRANSLATIONS
from dataclasses import replace
from pathlib import Path
@@ -29,7 +32,9 @@ from govoplan_core.core.modules import (
ModuleManifest,
NavItem,
PermissionDefinition,
ProductAvailabilityExplanation,
ProductAreaContribution,
ProductSurfaceContribution,
QuickAccessTool,
RoleTemplate,
)
@@ -442,7 +447,7 @@ POP3_PROVIDER = ExternalProviderDeclaration(
manifest = ModuleManifest(
id="mail",
name="Mail",
version="0.1.22",
version="0.1.25",
required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR),
optional_dependencies=("campaigns", "addresses", "calendar", "postbox", "search"),
provides_interfaces=(
@@ -559,6 +564,39 @@ manifest = ModuleManifest(
order=40,
),
),
product_surfaces=(
ProductSurfaceContribution(
id="communication.messages",
module_id="mail",
label="i18n:govoplan-core.product_surface.messages",
description="i18n:govoplan-core.product_surface.messages_description",
icon="mail",
entry_path="/messages",
route_path="/mail",
surface_ids=("mail.nav.mail", "mail.route.mail"),
presentations=("task", "reader"),
search_source_ids=("mail.mailbox_messages",),
help_context_ids=("mail.quick_access.messages",),
documentation_topic_ids=("mail.quick-access-and-product-area",),
required_any=("mail:mailbox:read",),
aliases=("/inbox",),
order=10,
unavailable=ProductAvailabilityExplanation(
reason="authorization",
title="i18n:govoplan-core.product_surface.messages_unavailable",
description="i18n:govoplan-core.product_surface.messages_unavailable_description",
resolution="i18n:govoplan-core.product_surface.messages_unavailable_resolution",
responsible_role="i18n:govoplan-core.access_administrator",
),
degraded=ProductAvailabilityExplanation(
reason="provider_degraded",
title="i18n:govoplan-core.product_surface.messages_degraded",
description="i18n:govoplan-core.product_surface.messages_degraded_description",
resolution="i18n:govoplan-core.product_surface.messages_degraded_resolution",
responsible_role="i18n:govoplan-core.integration_operator",
),
),
),
quick_access_tools=(
QuickAccessTool(
id="mail.messages",
@@ -890,6 +928,9 @@ manifest = ModuleManifest(
body=(
"Mail contributes its full mailbox to Communication. When Quick Access is enabled, its compact provider surface "
"appears inside the shared Messages drawer alongside independent Postbox and future chat contributions. "
"Mail also contributes to the stable Messages product entry: `/messages` and its migration alias `/inbox` open "
"Mail only when its mailbox route is currently authorized and visible in the effective View; otherwise another "
"available Messages owner is selected or the shell explains how access can be restored. "
"Recent entries open the exact authorized profile, folder, and message; Drafts resolves the configured or "
"provider-detected Drafts folder, and the full Mail page keeps the versioned Quick Access return context. "
"When an active Case requests a selection, choosing a recent message returns only its tenant-bound profile, "
@@ -911,6 +952,9 @@ manifest = ModuleManifest(
"body": (
"Mail ordnet das vollständige Postfach Kommunikation zu. Ist der Schnellzugriff aktiviert, erscheint die kompakte "
"Mail-Oberfläche gemeinsam mit unabhängigen Beiträgen aus Postbox und künftig Chat unter Nachrichten. "
"Mail trägt außerdem zum stabilen Produkteinstieg Nachrichten bei: `/messages` und der Migrationsalias `/inbox` "
"öffnen Mail nur, wenn die Postfachroute aktuell berechtigt und in der wirksamen View sichtbar ist. Andernfalls "
"wählt die Shell einen anderen verfügbaren Nachrichten-Eigentümer oder erklärt die Wiederherstellung des Zugriffs. "
"Aktuelle Einträge öffnen das genaue berechtigte Profil, den Ordner und die Nachricht; Entwürfe verwendet "
"den konfigurierten oder vom Anbieter erkannten Entwurfsordner. Die vollständige Mail-Seite erhält den "
"versionierten Rücksprungkontext. Verfassen öffnet bewusst die konfigurierte Mail-Anwendung des Benutzers, "
@@ -1484,5 +1528,10 @@ manifest = ModuleManifest(
)
manifest = with_documentation_structured_translations(
manifest, locale="de", translations=GERMAN_STRUCTURED_TRANSLATIONS
)
def get_manifest() -> ModuleManifest:
return manifest
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@govoplan/mail-webui",
"version": "0.1.22",
"version": "0.1.25",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@govoplan/mail-webui",
"version": "0.1.22",
"version": "0.1.25",
"devDependencies": {
"typescript": "^5.7.2"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@govoplan/mail-webui",
"version": "0.1.22",
"version": "0.1.25",
"private": true,
"type": "module",
"main": "src/index.ts",
@@ -1495,7 +1495,7 @@ function ProfileForm({
{editTarget.kind === "credentials" &&
<FormGrid columns={2} gap="small" collapseAt="workspace" className="">
{creatingCredential &&
<FormField label="Reusable credential" documentation={MAIL_PROFILE_WORKFLOW_DOCUMENTATION}>
<FormField label="Reusable credential" documentation={MAIL_PROFILE_WORKFLOW_DOCUMENTATION} helpContextId="mail.profiles" helpModuleId="mail">
<select value={reuseCredentialId} disabled={credentialDisabled} onChange={(event) => setReuseCredentialId(event.target.value)}>
<option value="">Create a new credential</option>
{availableCredentials.map((credential) =>
@@ -1509,13 +1509,13 @@ function ProfileForm({
{creatingCredential && <div />}
{!reuseCredentialId &&
<>
<FormField label="Credential name" documentation={MAIL_PROFILE_DOCUMENTATION}>
<FormField label="Credential name" documentation={MAIL_PROFILE_DOCUMENTATION} helpContextId="mail.profiles" helpModuleId="mail">
<input value={draft.credentialName} disabled={credentialDisabled} onChange={(event) => setDraft({ ...draft, credentialName: event.target.value })} />
</FormField>
<FormField label="Description">
<input value={draft.credentialDescription} disabled={credentialDisabled} onChange={(event) => setDraft({ ...draft, credentialDescription: event.target.value })} />
</FormField>
<FormField label="Available to modules" help="Choose installed modules. Keep Mail selected to use this credential here.">
<FormField label="Available to modules" help="Choose installed modules. Keep Mail selected to use this credential here." helpContextId="mail.profiles" helpModuleId="mail">
<ReferenceMultiSelect
values={commaValues(draft.credentialAllowedModules)}
onChange={(values) => setDraft({ ...draft, credentialAllowedModules: values.join(", ") })}
@@ -1527,7 +1527,9 @@ function ProfileForm({
</FormField>
<FormField
label="Limited to servers"
help="Choose an announced server or enter a module-qualified reference. Leave blank for every permitted server.">
help="Choose an announced server or enter a module-qualified reference. Leave blank for every permitted server."
helpContextId="mail.profiles"
helpModuleId="mail">
<ReferenceMultiSelect
values={commaValues(draft.credentialAllowedServerRefs)}
onChange={(values) => setDraft({ ...draft, credentialAllowedServerRefs: values.join(", ") })}
@@ -1540,6 +1542,8 @@ function ProfileForm({
</FormField>
<ToggleSwitch
checked={draft.credentialIsDefault}
helpContextId="mail.profiles"
helpModuleId="mail"
disabled={credentialDisabled}
onChange={(checked) => setDraft({ ...draft, credentialIsDefault: checked })}
label={`Default credential for this ${editTarget.protocol.toUpperCase()} server`} />
+3 -3
View File
@@ -1,5 +1,5 @@
import { createElement, lazy } from "react";
import type { MailDevMailboxUiCapability, MailProfilesUiCapability, PlatformWebModule, QuickAccessToolsUiCapability } from "@govoplan/core-webui";
import { messagesProductSurfaceTranslations, type MailDevMailboxUiCapability, type MailProfilesUiCapability, type PlatformWebModule, type QuickAccessToolsUiCapability } from "@govoplan/core-webui";
import { MailProfilePolicyEditor, MailProfileScopeManager } from "./features/mail/MailProfileManagement";
import { validateMailPolicy } from "./features/mail/mailPolicyValidation";
import { mailCredentialReferenceSelectors } from "./features/mail/mailReferenceProviders";
@@ -14,8 +14,8 @@ const mailboxRead = ["mail:mailbox:read"];
const bounceRead = ["mail:bounce:read", "mail:bounce:manage"];
const legacyImportAccess = ["mail:pop3:import", "mail:pop3:manage"];
const translations = {
en: generatedTranslations.en,
de: generatedTranslations.de
en: { ...generatedTranslations.en, ...messagesProductSurfaceTranslations.en },
de: { ...generatedTranslations.de, ...messagesProductSurfaceTranslations.de }
};
const mailQuickAccessTools: QuickAccessToolsUiCapability = {
tools: [