add manual/api/ssh Plesk-Backends, Minimal-PDF, --pdf-only und GUI-Verbesserungen

- PLESK_BACKEND={manual,api,ssh}: manual als Default für Shared Hosts,
  api unverändert, ssh ruft `plesk bin mail` per paramiko auf.
- POP3_PORT default 995, POP3_SSL mit Auto-Erkennung anhand Port.
- Kerio: User wird mit mayChangePassword=False angelegt.
- Zusätzliche Minimal-PDF (nur Email + Cloud) pro Konto + Sammel-Variante,
  IMAP-Port konfigurierbar.
- CLI-Flag --pdf-only und entsprechender GUI-Button "📄 Nur PDF".
- GUI: Lösch-Button "✕ löschen" sichtbarer, letzte Zeile löschbar.
- PDFs sind kunden-tauglich (kein Status-Block, kein ACHTUNG-Hinweis);
  Anlage-Status separat in _admin_report_<ts>.txt.
- README dokumentiert die Skip-Logik pro Dienst und ihre Caveats.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 12:15:39 +02:00
parent e951c839ca
commit dda5c746ce
6 changed files with 216 additions and 34 deletions
+57 -6
View File
@@ -31,7 +31,8 @@ def _section(title: str, rows, col_widths=(5 * cm, 11 * cm)):
return elements
def _build_user_section(account: Account, smtp_port: int, pop_port: int):
def _build_user_section(account: Account, smtp_port: int,
pop_port: int, pop_ssl: bool):
styles = getSampleStyleSheet()
elements = [
Paragraph(f"Zugangsdaten {account.vollname}", styles["Title"]),
@@ -47,6 +48,7 @@ def _build_user_section(account: Account, smtp_port: int, pop_port: int):
("Hinweis", "Anmeldung nur am Mailserver keine Plesk-Webinterface-Anmeldung."),
])
pop_proto = "SSL" if pop_ssl else "unverschlüsselt"
elements += _section("Kerio Connect (Hauptmailkonto)", [
("Webmail", f"https://{account.keriohost}/webmail/"),
("Benutzername", account.emailadresse),
@@ -54,7 +56,7 @@ def _build_user_section(account: Account, smtp_port: int, pop_port: int):
("SMTP-Server (Versand)", f"{account.keriohost}:{smtp_port} (SSL)"),
("IMAP/POP-Server", f"{account.keriohost}"),
("POP3-Sammler",
f"holt automatisch von {account.pleskhost}:{pop_port} (SSL), "
f"holt automatisch von {account.pleskhost}:{pop_port} ({pop_proto}), "
f"behält 14 Tage auf dem Server"),
])
@@ -71,18 +73,18 @@ def _build_user_section(account: Account, smtp_port: int, pop_port: int):
def write_user_pdf(path: Path, account: Account,
smtp_port: int, pop_port: int) -> None:
smtp_port: int, pop_port: int, pop_ssl: bool) -> None:
doc = SimpleDocTemplate(
str(path), pagesize=A4,
leftMargin=2 * cm, rightMargin=2 * cm,
topMargin=2 * cm, bottomMargin=2 * cm,
title=f"Zugangsdaten {account.vollname}",
)
doc.build(_build_user_section(account, smtp_port, pop_port))
doc.build(_build_user_section(account, smtp_port, pop_port, pop_ssl))
def write_combined_pdf(path: Path, accounts: List[Account],
smtp_port: int, pop_port: int) -> None:
smtp_port: int, pop_port: int, pop_ssl: bool) -> None:
doc = SimpleDocTemplate(
str(path), pagesize=A4,
leftMargin=2 * cm, rightMargin=2 * cm,
@@ -91,7 +93,56 @@ def write_combined_pdf(path: Path, accounts: List[Account],
)
flow = []
for i, acc in enumerate(accounts):
flow += _build_user_section(acc, smtp_port, pop_port)
flow += _build_user_section(acc, smtp_port, pop_port, pop_ssl)
if i < len(accounts) - 1:
flow.append(PageBreak())
doc.build(flow)
# ---------- Minimal-PDF (nur Email + Cloud, ohne Plesk/POP3-Sammler) ----------
def _build_minimal_section(account: Account, smtp_port: int, imap_port: int):
styles = getSampleStyleSheet()
elements = [
Paragraph(f"Zugangsdaten {account.vollname}", styles["Title"]),
Spacer(1, 0.4 * cm),
]
elements += _section("Email", [
("Emailadresse", account.emailadresse),
("Kennwort", account.kerioemailkennwort),
("SMTP-Server", f"{account.keriohost}:{smtp_port} (SSL)"),
("IMAP-Server", f"{account.keriohost}:{imap_port} (SSL)"),
])
elements += _section("Cloud", [
("Cloud URL", f"https://{account.nextcloudhost}"),
("Benutzername", account.nextcloud_username),
("Kennwort", account.nextcloudkennwort),
])
return elements
def write_user_minimal_pdf(path: Path, account: Account,
smtp_port: int, imap_port: int) -> None:
doc = SimpleDocTemplate(
str(path), pagesize=A4,
leftMargin=2 * cm, rightMargin=2 * cm,
topMargin=2 * cm, bottomMargin=2 * cm,
title=f"Zugangsdaten {account.vollname}",
)
doc.build(_build_minimal_section(account, smtp_port, imap_port))
def write_combined_minimal_pdf(path: Path, accounts: List[Account],
smtp_port: int, imap_port: int) -> None:
doc = SimpleDocTemplate(
str(path), pagesize=A4,
leftMargin=2 * cm, rightMargin=2 * cm,
topMargin=2 * cm, bottomMargin=2 * cm,
title="Zugangsdaten minimal (Sammel-PDF)",
)
flow = []
for i, acc in enumerate(accounts):
flow += _build_minimal_section(acc, smtp_port, imap_port)
if i < len(accounts) - 1:
flow.append(PageBreak())
doc.build(flow)