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:
@@ -36,7 +36,15 @@ NEXTCLOUD_ADMIN_PASSWORD=
|
||||
# --- Mailserver-Ports für POP3-Sammler in Kerio ---
|
||||
POP3_PORT=995
|
||||
POP3_KEEP_DAYS=14
|
||||
# POP3_SSL leer lassen → Auto-Erkennung anhand POP3_PORT
|
||||
# 110 → SSL aus (Plain POP3)
|
||||
# alles andere → SSL an (POP3S, auch 465)
|
||||
# Manuell überschreibbar mit POP3_SSL=true|false.
|
||||
POP3_SSL=
|
||||
|
||||
# Nur für die Anzeige im Kunden-PDF (Mailprogramm-Konfig).
|
||||
SMTP_PORT=465
|
||||
IMAP_PORT=993
|
||||
|
||||
# TLS-Zertifikate prüfen? false nur bei selbstsignierten Test-Zertifikaten.
|
||||
VERIFY_TLS=true
|
||||
|
||||
@@ -53,8 +53,10 @@ Dreistufige Wahl, je nachdem welchen Zugriff du auf das Plesk hast:
|
||||
| `NEXTCLOUD_ADMIN_USER` | Admin-User (App-Passwort empfohlen) |
|
||||
| `NEXTCLOUD_ADMIN_PASSWORD` | Admin- oder App-Passwort |
|
||||
| `POP3_PORT` | POP3-Sammler in Kerio (default `995` = POP3S) |
|
||||
| `POP3_SSL` | Leer → Auto: `110` ⇒ aus, sonst an. Manuell `true`/`false`. |
|
||||
| `POP3_KEEP_DAYS` | Tage, die Mails auf Plesk verbleiben (default `14`) |
|
||||
| `SMTP_PORT` | Nur für die PDF-Anzeige (default `465`) |
|
||||
| `SMTP_PORT` | Nur für die PDF-Anzeige (default `465`, Mailprogramm-Konfig) |
|
||||
| `IMAP_PORT` | Nur für die Minimal-PDF-Anzeige (default `993` = IMAPS) |
|
||||
| `VERIFY_TLS` | `false` nur bei Test/selbst signierten Zertifikaten |
|
||||
|
||||
---
|
||||
@@ -114,10 +116,29 @@ case-insensitive. Beispiel: [`example.csv`](./example.csv).
|
||||
|
||||
### CLI
|
||||
|
||||
Voller Lauf (Plesk + Kerio + Nextcloud + PDFs):
|
||||
|
||||
```bash
|
||||
python deploy.py --csv example.csv --output ./output
|
||||
python deploy.py --csv kunden.csv --output ./output
|
||||
```
|
||||
|
||||
Nur PDFs aus der CSV neu erzeugen, ohne Plesk/Kerio/Nextcloud anzufassen
|
||||
(z.B. wenn die Konten schon angelegt sind oder eine PDF wiederholt
|
||||
ausgedruckt werden soll):
|
||||
|
||||
```bash
|
||||
python deploy.py --csv kunden.csv --pdf-only
|
||||
```
|
||||
|
||||
Weitere Flags:
|
||||
|
||||
| Flag | Bedeutung |
|
||||
| ------------ | --------------------------------------------------------------- |
|
||||
| `--csv PATH` | CSV-Datei einlesen |
|
||||
| `--output D` | Verzeichnis für PDFs + Admin-Report (default `./output`) |
|
||||
| `--pdf-only` | Nur PDFs schreiben, keine Account-Anlage |
|
||||
| `--gui` | GUI starten (auch ohne `--csv` aufrufbar) |
|
||||
|
||||
Exit-Code `0` wenn alle Konten ohne Fehler verarbeitet wurden, sonst `1`.
|
||||
|
||||
### GUI
|
||||
@@ -129,11 +150,13 @@ python deploy.py
|
||||
```
|
||||
|
||||
In der GUI:
|
||||
- Per **+ Zeile** beliebig viele Konten hinzufügen.
|
||||
- **+ Zeile** fügt eine neue Eingabezeile hinzu (Endlosfelder).
|
||||
- **✕ löschen** entfernt eine Zeile (rote Schrift).
|
||||
- **CSV laden** füllt die Felder aus einer bestehenden CSV.
|
||||
- **Ausgabeordner** wählen, dann **Ausführen ▶**.
|
||||
- Log unten zeigt Fortschritt; bei jedem Konto entsteht eine
|
||||
Einzel-PDF, am Ende eine Sammel-PDF mit Zeitstempel.
|
||||
- **Ausgabeordner** wählen.
|
||||
- **Ausführen ▶** legt die Konten an + erzeugt PDFs + Admin-Report.
|
||||
- **📄 Nur PDF** schreibt nur die PDFs ohne API-Aufrufe.
|
||||
- Log unten zeigt Fortschritt.
|
||||
|
||||
---
|
||||
|
||||
@@ -153,16 +176,41 @@ Nextcloud: User vorname.nachname mit Gruppe + Quota anlegen
|
||||
PDF (einzeln) schreiben
|
||||
```
|
||||
|
||||
Am Ende:
|
||||
- eine **Einzel-PDF** pro Konto + eine **Sammel-PDF** – beide enthalten
|
||||
ausschließlich Zugangsdaten und sind 1:1 an den Kunden weitergebbar.
|
||||
- ein **`_admin_report_<timestamp>.txt`** mit Status pro Konto
|
||||
(✓ angelegt / · übersprungen / ⚠ manuell / ✗ Fehler) – **NICHT für
|
||||
den Kunden**, das ist deine Admin-Übersicht.
|
||||
Am Ende werden pro Lauf vier Dokument-Sorten geschrieben (alle in `output/`):
|
||||
|
||||
Bestehende Konten werden **übersprungen** (kein Fehler) und im
|
||||
Admin-Report entsprechend markiert. Damit kann eine CSV gefahrlos
|
||||
zweimal laufen.
|
||||
| Datei | Inhalt |
|
||||
| ---------------------------------------------- | --------------------------------------------------------------------- |
|
||||
| `zugangsdaten_<email>.pdf` | **Voll** pro Benutzer: Plesk-Mailpostfach + Kerio + Nextcloud |
|
||||
| `zugangsdaten_minimal_<email>.pdf` | **Minimal** pro Benutzer: nur Email (Kerio) + Cloud (Nextcloud) |
|
||||
| `zugangsdaten_gesamt_<ts>.pdf` | Sammel-PDF aller voller Datensätze |
|
||||
| `zugangsdaten_gesamt_minimal_<ts>.pdf` | Sammel-PDF aller minimalen Datensätze |
|
||||
| `_admin_report_<ts>.txt` | Status pro Konto (✓/·/⚠/✗). **NICHT für den Kunden** – Admin-Doku. |
|
||||
|
||||
Die **Minimal-PDF** enthält genau das, was der Endkunde fürs Mailprogramm
|
||||
und die Cloud-Anmeldung braucht (Mailadresse, Kennwort, SMTP, IMAP, Cloud-URL,
|
||||
Cloud-Username, Cloud-Kennwort) – ohne Backend-Details wie POP3-Sammler oder
|
||||
Plesk-Mailserver.
|
||||
|
||||
### Idempotenz – bestehende Konten werden übersprungen
|
||||
|
||||
Jeder Dienst wird **einzeln** geprüft, bevor angelegt wird:
|
||||
|
||||
| Dienst | Prüfung | Wenn vorhanden |
|
||||
| --------- | -------------------------------------------------------------------- | ----------------------------------------------------------------- |
|
||||
| Plesk | `mail --info` (api/ssh) bzw. ohnehin Manual-Modus | übersprungen |
|
||||
| Kerio | `Users.get` mit `loginName` + `domainId` | übersprungen, **kein neuer POP3-Sammler** (sonst doppelt!) |
|
||||
| Nextcloud | `GET /ocs/v2.php/cloud/users/<vorname.nachname>` | übersprungen, **keine Quota-/Gruppen-Änderung** |
|
||||
|
||||
→ Eine CSV kann gefahrlos zwei- oder dreimal durchgejagt werden, z.B. wenn
|
||||
beim ersten Lauf nur Plesk geklappt hat und du Kerio + Nextcloud nachziehen
|
||||
willst. Status pro Konto und Dienst steht im Admin-Report als
|
||||
`✓ angelegt` / `· übersprungen` / `⚠ manuell` / `✗ Fehler`.
|
||||
|
||||
**Caveats**:
|
||||
- Kerio prüft `loginName` *in der zur Mailadresse passenden Domain* – Aliase
|
||||
auf einer anderen Domain werden nicht erkannt.
|
||||
- Nextcloud prüft den abgeleiteten Username `vorname.nachname` – existiert
|
||||
derselbe Mensch dort unter abweichendem Username, wird das nicht gefunden.
|
||||
|
||||
## Workflow bei `PLESK_BACKEND=manual` (Shared Host)
|
||||
|
||||
|
||||
@@ -44,5 +44,16 @@ class Config:
|
||||
POP3_PORT = int(os.getenv("POP3_PORT", "995"))
|
||||
POP3_KEEP_DAYS = int(os.getenv("POP3_KEEP_DAYS", "14"))
|
||||
SMTP_PORT = int(os.getenv("SMTP_PORT", "465"))
|
||||
IMAP_PORT = int(os.getenv("IMAP_PORT", "993"))
|
||||
|
||||
# SSL für POP3-Sammler:
|
||||
# - explizit per POP3_SSL=true|false setzbar
|
||||
# - sonst Auto-Erkennung anhand des Ports:
|
||||
# 110 (Plain POP3) → SSL aus, alles andere (995, 465, …) → SSL an
|
||||
_pop3_ssl_env = os.getenv("POP3_SSL")
|
||||
if _pop3_ssl_env is None or _pop3_ssl_env == "":
|
||||
POP3_SSL = POP3_PORT != 110
|
||||
else:
|
||||
POP3_SSL = _bool(_pop3_ssl_env, True)
|
||||
|
||||
VERIFY_TLS = _bool(os.getenv("VERIFY_TLS"), True)
|
||||
|
||||
@@ -9,7 +9,10 @@ from typing import Callable, List, Tuple
|
||||
|
||||
from config import Config
|
||||
from models import Account, AccountResult, StepResult
|
||||
from pdf import write_combined_pdf, write_user_pdf
|
||||
from pdf import (
|
||||
write_combined_minimal_pdf, write_combined_pdf,
|
||||
write_user_minimal_pdf, write_user_pdf,
|
||||
)
|
||||
|
||||
|
||||
def _detect_delimiter(path: Path) -> str:
|
||||
@@ -141,7 +144,7 @@ def _deploy_one(account: Account, cfg: Config, log: Callable[[str], None]) -> Ac
|
||||
server=account.pleskhost,
|
||||
login_name=account.emailadresse,
|
||||
password=account.pleskemailkennwort,
|
||||
port=cfg.POP3_PORT, ssl=True,
|
||||
port=cfg.POP3_PORT, ssl=cfg.POP3_SSL,
|
||||
leave_days=cfg.POP3_KEEP_DAYS,
|
||||
)
|
||||
result.steps.append(StepResult("Kerio", "angelegt", "inkl. POP3-Sammler"))
|
||||
@@ -212,6 +215,34 @@ def _write_admin_report(path: Path, results: List[AccountResult],
|
||||
path.write_text("\n".join(lines), encoding="utf-8")
|
||||
|
||||
|
||||
def pdf_only(accounts: List[Account], cfg: Config,
|
||||
output_dir, log: Callable[[str], None] = print) -> Path:
|
||||
"""Nur PDFs schreiben – KEIN Plesk/Kerio/Nextcloud-Aufruf.
|
||||
|
||||
Praktisch wenn die Konten schon angelegt sind oder die CSV nochmal
|
||||
durchgejagt werden soll, um die PDFs neu zu generieren.
|
||||
"""
|
||||
output_dir = Path(output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
for i, acc in enumerate(accounts, 1):
|
||||
log(f"[{i}/{len(accounts)}] {acc.vollname} <{acc.emailadresse}>")
|
||||
safe = "".join(c if c.isalnum() or c in "-_." else "_" for c in acc.emailadresse)
|
||||
per_pdf = output_dir / f"zugangsdaten_{safe}.pdf"
|
||||
per_min = output_dir / f"zugangsdaten_minimal_{safe}.pdf"
|
||||
write_user_pdf(per_pdf, acc, cfg.SMTP_PORT, cfg.POP3_PORT, cfg.POP3_SSL)
|
||||
write_user_minimal_pdf(per_min, acc, cfg.SMTP_PORT, cfg.IMAP_PORT)
|
||||
log(f" ⤷ PDF: {per_pdf}")
|
||||
log(f" ⤷ PDF (minimal): {per_min}")
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
combined = output_dir / f"zugangsdaten_gesamt_{timestamp}.pdf"
|
||||
combined_min = output_dir / f"zugangsdaten_gesamt_minimal_{timestamp}.pdf"
|
||||
write_combined_pdf(combined, accounts, cfg.SMTP_PORT, cfg.POP3_PORT, cfg.POP3_SSL)
|
||||
write_combined_minimal_pdf(combined_min, accounts, cfg.SMTP_PORT, cfg.IMAP_PORT)
|
||||
log(f"✓ Gesamt-PDF: {combined}")
|
||||
log(f"✓ Gesamt-PDF (minimal): {combined_min}")
|
||||
return combined
|
||||
|
||||
|
||||
def run_deploy(accounts: List[Account], cfg: Config,
|
||||
output_dir, log: Callable[[str], None] = print
|
||||
) -> Tuple[List[AccountResult], Path]:
|
||||
@@ -224,13 +255,19 @@ def run_deploy(accounts: List[Account], cfg: Config,
|
||||
results.append(result)
|
||||
safe = "".join(c if c.isalnum() or c in "-_." else "_" for c in acc.emailadresse)
|
||||
per_pdf = output_dir / f"zugangsdaten_{safe}.pdf"
|
||||
write_user_pdf(per_pdf, acc, cfg.SMTP_PORT, cfg.POP3_PORT)
|
||||
per_min = output_dir / f"zugangsdaten_minimal_{safe}.pdf"
|
||||
write_user_pdf(per_pdf, acc, cfg.SMTP_PORT, cfg.POP3_PORT, cfg.POP3_SSL)
|
||||
write_user_minimal_pdf(per_min, acc, cfg.SMTP_PORT, cfg.IMAP_PORT)
|
||||
log(f" ⤷ PDF: {per_pdf}")
|
||||
log(f" ⤷ PDF (minimal): {per_min}")
|
||||
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
combined = output_dir / f"zugangsdaten_gesamt_{timestamp}.pdf"
|
||||
write_combined_pdf(combined, accounts, cfg.SMTP_PORT, cfg.POP3_PORT)
|
||||
combined_min = output_dir / f"zugangsdaten_gesamt_minimal_{timestamp}.pdf"
|
||||
write_combined_pdf(combined, accounts, cfg.SMTP_PORT, cfg.POP3_PORT, cfg.POP3_SSL)
|
||||
write_combined_minimal_pdf(combined_min, accounts, cfg.SMTP_PORT, cfg.IMAP_PORT)
|
||||
log(f"✓ Gesamt-PDF: {combined}")
|
||||
log(f"✓ Gesamt-PDF (minimal): {combined_min}")
|
||||
|
||||
report = output_dir / f"_admin_report_{timestamp}.txt"
|
||||
_write_admin_report(report, results, cfg, timestamp)
|
||||
@@ -245,6 +282,8 @@ def main():
|
||||
p.add_argument("--csv", help="Pfad zur CSV-Datei (CLI-Modus)")
|
||||
p.add_argument("--output", default="./output", help="Verzeichnis für PDFs (default ./output)")
|
||||
p.add_argument("--gui", action="store_true", help="GUI starten")
|
||||
p.add_argument("--pdf-only", action="store_true",
|
||||
help="Nur PDFs aus der CSV erzeugen, keine Anlage in Plesk/Kerio/Nextcloud")
|
||||
args = p.parse_args()
|
||||
|
||||
if args.gui or not args.csv:
|
||||
@@ -259,6 +298,12 @@ def main():
|
||||
print(f"CSV-Fehler: {e}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
print(f"{len(accounts)} Account(s) eingelesen.\n")
|
||||
|
||||
if args.pdf_only:
|
||||
combined = pdf_only(accounts, cfg, args.output)
|
||||
print(f"\nFertig (nur PDF). Gesamt-PDF: {combined}")
|
||||
sys.exit(0)
|
||||
|
||||
results, combined = run_deploy(accounts, cfg, args.output)
|
||||
failed = sum(1 for r in results if r.has_errors)
|
||||
print(f"\nFertig. {len(results) - failed}/{len(results)} ohne Fehler. Gesamt-PDF: {combined}")
|
||||
|
||||
@@ -6,7 +6,7 @@ import tkinter as tk
|
||||
from tkinter import filedialog, messagebox, scrolledtext, ttk
|
||||
|
||||
from config import Config
|
||||
from deploy import parse_csv, run_deploy
|
||||
from deploy import parse_csv, pdf_only, run_deploy
|
||||
from models import Account
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ class AccountRow(ttk.Frame):
|
||||
e = ttk.Entry(self, width=width)
|
||||
e.grid(row=0, column=col, padx=2, pady=2, sticky="ew")
|
||||
self.entries[key] = e
|
||||
ttk.Button(self, text="✕", width=3,
|
||||
ttk.Button(self, text="✕ löschen", width=10,
|
||||
style="Remove.TButton",
|
||||
command=lambda: on_remove(self)).grid(
|
||||
row=0, column=len(FIELDS), padx=4)
|
||||
|
||||
@@ -54,6 +55,12 @@ class App(tk.Tk):
|
||||
self.geometry("1500x800")
|
||||
self.output_dir = Path("./output").resolve()
|
||||
self.rows: list[AccountRow] = []
|
||||
# Roter Lösch-Button-Style
|
||||
style = ttk.Style(self)
|
||||
try:
|
||||
style.configure("Remove.TButton", foreground="#b00020")
|
||||
except tk.TclError:
|
||||
pass
|
||||
self._build()
|
||||
self.add_row()
|
||||
|
||||
@@ -68,6 +75,8 @@ class App(tk.Tk):
|
||||
self.output_lbl.pack(side="left", padx=8)
|
||||
self.run_btn = ttk.Button(toolbar, text="Ausführen ▶", command=self.run)
|
||||
self.run_btn.pack(side="right", padx=2)
|
||||
self.pdf_btn = ttk.Button(toolbar, text="📄 Nur PDF", command=self.run_pdf_only)
|
||||
self.pdf_btn.pack(side="right", padx=2)
|
||||
|
||||
# Header über die Eingabezeilen
|
||||
header = ttk.Frame(self)
|
||||
@@ -112,11 +121,10 @@ class App(tk.Tk):
|
||||
self.rows.append(row)
|
||||
|
||||
def remove_row(self, row):
|
||||
if len(self.rows) <= 1:
|
||||
row.from_dict({}) # leere statt entfernen
|
||||
return
|
||||
self.rows.remove(row)
|
||||
row.destroy()
|
||||
if not self.rows:
|
||||
self.add_row() # immer mindestens eine leere Zeile
|
||||
|
||||
# ----- Buttons -----
|
||||
def load_csv(self):
|
||||
@@ -189,7 +197,7 @@ class App(tk.Tk):
|
||||
self.log.insert("end", msg + "\n")
|
||||
self.log.see("end")
|
||||
|
||||
def run(self):
|
||||
def _start_worker(self, fn, success_title: str):
|
||||
try:
|
||||
accounts = self._collect_accounts()
|
||||
except ValueError as e:
|
||||
@@ -201,21 +209,32 @@ class App(tk.Tk):
|
||||
cfg = Config()
|
||||
self.log.delete("1.0", "end")
|
||||
self.run_btn.config(state="disabled")
|
||||
self.pdf_btn.config(state="disabled")
|
||||
|
||||
def worker():
|
||||
try:
|
||||
_, combined = run_deploy(accounts, cfg, self.output_dir,
|
||||
log=lambda m: self.after(0, self._log, m))
|
||||
combined = fn(accounts, cfg, self.output_dir,
|
||||
log=lambda m: self.after(0, self._log, m))
|
||||
# run_deploy gibt (results, combined) zurück, pdf_only nur combined
|
||||
if isinstance(combined, tuple):
|
||||
combined = combined[1]
|
||||
self.after(0, lambda: messagebox.showinfo(
|
||||
"Fertig", f"Verarbeitung abgeschlossen.\n\nGesamt-PDF:\n{combined}"))
|
||||
success_title, f"Fertig.\n\nGesamt-PDF:\n{combined}"))
|
||||
except Exception as e:
|
||||
self.after(0, lambda: self._log(f"FEHLER: {e}"))
|
||||
self.after(0, lambda: messagebox.showerror("Fehler", str(e)))
|
||||
finally:
|
||||
self.after(0, lambda: self.run_btn.config(state="normal"))
|
||||
self.after(0, lambda: self.pdf_btn.config(state="normal"))
|
||||
|
||||
threading.Thread(target=worker, daemon=True).start()
|
||||
|
||||
def run(self):
|
||||
self._start_worker(run_deploy, "Verarbeitung abgeschlossen")
|
||||
|
||||
def run_pdf_only(self):
|
||||
self._start_worker(pdf_only, "Nur PDFs erzeugt")
|
||||
|
||||
|
||||
def launch():
|
||||
App().mainloop()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user