19 KiB
Paperless-Connector für LibreOffice
Erweiterung, die Dokumente direkt aus einem Paperless-ngx-Archiv öffnet, Bearbeitungen als neue Version desselben Dokuments ablegt, die Klassifizierung bearbeitet und zeitlich begrenzte Freigabelinks erzeugt.
Voraussetzung ist Paperless-ngx 3.0 oder neuer — erst dort gibt es die Dokumentversionierung.
Funktionen
| Befehl | Wirkung |
|---|---|
| Aus Paperless öffnen | Suche, Trefferliste, Versionsliste — lädt das Original |
| Als neue Version ablegen | hängt die Fassung als Version an das bestehende Dokument |
| In Paperless ablegen | neues Dokument oder neue Version eines beliebigen anderen Dokuments |
| Klassifizierung bearbeiten | Titel, Datum, Korrespondent, Dokumenttyp, Tags |
| Freigabelink erzeugen | zeitlich begrenzter, unauthentifizierter Link, optional mit Kennzeichnung |
| Status anzeigen | Kennung, Titel, Versionsanzahl des aktuellen Dokuments |
| Verbindung einrichten | Basisadresse und API-Token |
Erreichbar über das Menü Extras → Paperless und über eine eigene Symbolleiste.
Sprachen
Die Oberfläche liegt in Deutsch, Englisch und Französisch vor. Die Sprache richtet sich nach der Einstellung von LibreOffice, nicht nach der des Betriebssystems — maßgeblich ist die Sprache, in der die Erweiterung erscheint.
Übersetzt wird in pythonpath/paperless_i18n.py. Als Schlüssel dient der
deutsche Text selbst; fehlt eine Übersetzung, erscheint das deutsche
Original. Eine weitere Sprache ergänzt man, indem man ein Wörterbuch nach
dem Muster von EN anlegt und in KATALOG einträgt.
Die Menü- und Symbolleistentexte liegen getrennt davon in Addons.xcu;
dort sind sie je Sprache als xml:lang-Werte hinterlegt.
Sicherheit
Ausführlich in SICHERHEIT.md. Die wichtigsten Punkte:
- Makros werden nie ausgeführt. Heruntergeladene Dateien werden mit
MacroExecutionMode = NEVER_EXECUTEgeladen — auch eine Rückfrage gibt es nicht. Ein Dokument aus dem Archiv ist fremder Inhalt, selbst aus dem eigenen Bestand. - Keine externen Inhalte beim Öffnen.
UpdateDocMode = NO_UPDATE. - Dateinamen aus der Antwort werden vollständig bereinigt, beide Pfadtrennzeichen, Windows-Gerätenamen, Punkte am Ende.
- Arbeitsverzeichnis im Benutzerprofil, nicht in
/tmp— dort wäre ein Symlink-Angriff möglich. - Nur http und https als Basisadresse und bei Weiterleitungen.
- Größenbegrenzung auf Antworten.
Bewusste Entwurfsentscheidungen
Eigene Schaltflächen statt Speichern-Knopf
Paperless als echtes Speicherziel in die Datei-Dialoge einzuhängen würde einen UCB Content Provider erfordern — C++-UNO-Arbeit in einer sehr dünn dokumentierten Ecke, für ein Einzelprojekt nicht tragfähig.
Eigene Schaltflächen sind ohnehin die ehrlichere Lösung: Es bleibt sichtbar, wann eine neue Version im Archiv entsteht, und ein versehentliches Strg+S erzeugt keine Archivversion.
Prüfungen vor der Freigabe
Ein Link auf einen Stand, der nur lokal existiert, wäre irreführend — der Empfänger bekäme etwas anderes zu sehen als der Absender vor sich hat. Deshalb wird vorher geprüft:
| Zustand | Reaktion |
|---|---|
| Dokument nicht in Paperless | Angebot, es zuerst abzulegen |
| lokale Änderungen nicht abgelegt | Auswahl: abbrechen, trotzdem teilen, oder erst neue Version ablegen |
Zusätzlich lässt sich das Dokument beim Teilen mit einem Tag
kennzeichnen, voreingestellt Sharelink. Das macht geteilte Dokumente
später gezielt auffindbar — Paperless zeigt zwar die eigenen Freigaben an,
aber ein Tag lässt sich in gespeicherten Ansichten und Regeln verwenden.
Der zuletzt verwendete Tagname wird gemerkt. Ist er noch nicht vorhanden, wird er nach Rückfrage angelegt. Vorhandene Tags des Dokuments bleiben erhalten.
Drei Wege ins Archiv
| Befehl | Ergebnis |
|---|---|
| Als neue Version ablegen | eine weitere Fassung desselben Dokuments |
| In Paperless ablegen → Neues Dokument | eigenständiges Dokument mit eigener Versionskette und Seriennummer |
| In Paperless ablegen → An bestehendes anhängen | neue Version eines beliebigen anderen Dokuments |
Der dritte Weg deckt eine Lücke, die Dokumentenverwaltungen gern offenlassen:
Die Rechnung kommt als PDF ins Archiv. Später entsteht die bearbeitbare Fassung. Beides gehört zusammen — aber das PDF war zuerst da.
„Als neue Version ablegen" zielt immer auf das eigene Wurzeldokument und hilft hier nicht. Über die Zielauswahl lässt sich dagegen jedes vorhandene Dokument suchen und die geöffnete Datei daran anhängen.
Beim neuen Dokument lässt sich die Klassifizierung des Ursprungs
übernehmen — Dokumenttyp, Korrespondent, Tags, Zusatzfelder und
Eigentümer. Zusatzfelder mit Herkunftsbezug werden bewusst nicht
kopiert — wer aus einem anderen System migriert hat, führt dort oft ein Feld
mit der Fremdkennung, und eine Kopie mit derselben Kennung wäre falsch.
Welche Feldnamen das betrifft, steht in den Einstellungen unter
herkunftsfelder.
In beiden Fällen kann das geöffnete Fenster danach dem Ziel zugeordnet werden. Weitere Versionen landen dann dort.
Der Befehl funktioniert auch mit Dateien, die nicht aus Paperless stammen — dann ist er der Weg, sie überhaupt erst einzuliefern.
Und er funktioniert mit Dokumenten, die noch nie gespeichert wurden. Ein frisch angelegtes „Unbenannt 1" hat keinen Speicherort; es wird dann in dasselbe temporäre Verzeichnis geschrieben, das auch beim Öffnen benutzt wird, im zur Anwendung passenden ODF-Format. Der eingegebene Titel bestimmt dabei den Dateinamen, unter dem Paperless das Original ablegt.
Ein Dialog von oben nach unten
┌─ Suche ──────────────────────────────────────── [Suchen] ─┐
│ │
│ Dokumente ASN Datum Titel │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 11263 2024-02-11 Mietvertrag │ │
│ │ 11264 2024-03-02 Nebenkostenabrechnung │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ Versionen Nr. Datum Format Bezeichnung │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ • 3 2026-09-01 PDF LibreOffice 2026-09-01 │ │
│ │ 2 2025-06-04 ODF Text │ │
│ │ 1 2024-02-11 ODF Text │ │
│ └──────────────────────────────────────────────────────┘ │
│ Bearbeitbar. Änderungen können als neue Version … │
│ ☐ Schreibgeschützt öffnen [Öffnen] [Abbrechen] │
└────────────────────────────────────────────────────────────┘
Die Spaltenüberschrift steht jeweils unter der Bezeichnung des Abschnitts und über der Liste, nicht daneben.
Beide Listen verwenden eine dicht laufende Schrift mit festen Spaltenbreiten und senkrechten Strichen als Trenner. Die Schriftgröße wird bewusst nicht festgelegt, sondern vom Dialog geerbt — also die Vorgabe des Systems. Mit der sonst üblichen Proportionalschrift wären mit Leerzeichen aufgefüllte Spalten nicht ausgerichtet — die Überschrift stünde woanders als der Inhalt.
Die Trefferliste zeigt je Dokument einen Eintrag, nämlich das Wurzeldokument. Bei Auswahl füllt sich darunter die Versionsliste mit Nummer, Datum, Dateiformat und Bezeichnung.
Vorausgewählt ist die aktuellste Version. Ein Punkt markiert Fassungen, die LibreOffice nach der Formattabelle nicht bearbeiten kann — etwa ein PDF, während eine ältere Version noch das ODT ist.
Öffnungsart
Unten im Dialog wird die Öffnungsart gewählt. Die Vorbelegung ist ein Vorschlag, keine Vorschrift — die Formaterkennung kann danebenliegen, und dann soll der Anwender trotzdem bearbeitbar öffnen können.
| Fall | Vorbelegung | Hinweis |
|---|---|---|
| bekannt und bearbeitbar | Bearbeitbar | — |
| nicht erkannt | Bearbeitbar | zeigt Dateiname und MIME-Typ, damit das Format gemeldet und ergänzt werden kann |
| bekannt, nicht bearbeitbar (PDF, E-Mail) | Schreibgeschützt | bearbeitbar bleibt wählbar |
Beim Zurückschreiben eines unbekannten Formats kommt eine Rückfrage: Es gibt dann keinen passenden Speicherfilter, LibreOffice speichert im eigenen Format, und die Dateiendung passt danach möglicherweise nicht mehr zum Inhalt.
Welche Formate als bearbeitbar gelten, steht in
pythonpath/paperless_formats.py. Die Tabelle lässt sich dort ergänzen —
wenn im Hinweistext regelmäßig derselbe MIME-Typ auftaucht, gehört er
hinein.
Die Formatangabe steht nicht in der eingebetteten versions-Liste, deshalb
ruft die Erweiterung je Version einen Datensatz ab. Die Ergebnisse werden
zwischengespeichert, damit das Blättern in der Trefferliste nicht bei jedem
Tastendruck erneut abfragt.
Immer das Original laden
Der Download erfolgt mit ?original=true. Bei ZUGFeRD bleibt damit die
eingebettete XML erhalten, bei E-Mails die Anhänge. Die von Paperless
erzeugte Archivfassung wäre eine Ableitung.
Kennung in den Dokumenteigenschaften
Die Paperless-ID wird in den benutzerdefinierten Dokumenteigenschaften
abgelegt (PaperlessDocumentId). Diese überleben einen ODF-Speichervorgang
und wandern sogar mit, wenn die Datei kopiert wird.
Als Rückfallebene dient eine lokale Zuordnungsdatei über den Datei-URL — für Formate, die keine solchen Eigenschaften kennen.
Betriebssystemneutral
Die Erweiterung läuft unter Linux, Windows und macOS. Was dafür nötig war:
| Punkt | Umsetzung |
|---|---|
| Konfigurationsort | %APPDATA% unter Windows, sonst XDG_CONFIG_HOME bzw. ~/.config |
| Pfade | durchgehend os.path.join, nie feste Trennzeichen |
| Datei-URLs | unohelper.systemPathToFileUrl statt eigener Zusammensetzung |
| Dateinamen | Windows-Verbotszeichen und Gerätenamen werden auch unter Linux entfernt |
Eine Einschränkung: chmod ist unter Windows weitgehend wirkungslos. Die
Einstellungsdatei mit dem Token liegt dort im Benutzerprofil, auf das
andere Benutzer standardmäßig keinen Zugriff haben — der Schutz kommt also
vom Betriebssystem statt von den Dateirechten.
build.sh ist ein Shell-Skript und läuft nur unter Linux und macOS. Es
wird ausschließlich zur Entwicklung gebraucht; die fertige .oxt lässt
sich überall installieren.
Ohne requests
Die in LibreOffice mitgelieferte Python-Laufzeit bringt nur die
Standardbibliothek mit. Alles läuft über urllib, inklusive
selbstgebautem Multipart-Upload.
Bauen und installieren
./build.sh
unopkg add --force paperless-libreoffice-1.1.0.oxt
Zur Versionsabhängigkeit:
OpenOffice.org-minimal-versionindescription.xmlbezieht sich auf die OpenOffice-Referenzversion, und die ist bei LibreOffice dauerhaft auf 4.1 eingefroren — unabhängig davon, ob die installierte Version 7, 24 oder 25 heißt. Ein Wert wie7.0schließt dort jede Installation aus und führt zu „Die Erweiterung benötigt mindestens OpenOffice.org Referenzversion 7.0".Für echte LibreOffice-Versionen gibt es
lo:LibreOffice-minimal-versionim Namensraumhttp://libreoffice.org/extensions/description/2011.
LibreOffice danach vollständig beenden, auch den Schnellstarter:
pkill soffice
Entfernen:
unopkg remove org.example.paperless.libreoffice
Alternativ über Extras → Extension Manager.
Einrichten
- Extras → Paperless → Verbindung einrichten
- Basisadresse eintragen, etwa
https://dms.example.org— ohne abschließenden Schrägstrich - API-Token eintragen. Erzeugt wird er im Benutzerprofil von Paperless, nicht in der allgemeinen Verwaltung
- Speichern — die Verbindung wird sofort geprüft
Die Einstellungen liegen unter
~/.config/paperless-libreoffice/settings.json mit Modus 600.
Zum Token: Er liegt im Klartext in dieser Datei. Das entspricht dem, was andere Desktop-Anwendungen tun, ist aber kein Geheimnisspeicher. Wer es strenger will, müsste die Erweiterung an den Systemschlüsselbund anbinden — über
com.sun.star.task.PasswordContaineroder libsecret.
Ablauf im Alltag
Extras → Paperless → Aus Paperless öffnen…
Suchbegriff oder Archiv-Seriennummer eingeben
Dokument auswählen → öffnet sich in LibreOffice
… bearbeiten, zwischendurch normal mit Strg+S speichern
(bleibt lokal, das Archiv bleibt unberührt)
Extras → Paperless → Als neue Version ablegen
Rückfrage bestätigen → Upload → Bestätigung mit Versions-ID
Die bisherige Fassung bleibt als Version erhalten. In Paperless entsteht kein neuer Eintrag, sondern eine Version am bestehenden Dokument.
Fehlersuche
Die Erweiterung erscheint nicht im Menü:
unopkg list | grep -i paperless
Fehlt sie, wurde LibreOffice nicht vollständig beendet. Python-Fehler beim Laden erscheinen auf der Konsole:
soffice --writer
Ausführlichere Meldungen mit:
SAL_LOG="+WARN+INFO.extensions" soffice --writer
Bekannte Stolpersteine
Dokument ohne Speicherort. Ein noch nie gespeichertes Dokument wird für die Ablage automatisch temporär geschrieben. In LibreOffice bleibt es danach weiterhin ungespeichert — das ist Absicht, damit nicht ungefragt eine Datei im Dateisystem entsteht, von der niemand weiß.
„Dieses Dokument stammt nicht aus Paperless." Die Kennung wurde nicht gefunden. Das passiert, wenn die Datei über den normalen Öffnen-Dialog geladen wurde oder das Format keine benutzerdefinierten Dokumenteigenschaften kennt.
Speichern in Fremdformaten. Beim Zurückspeichern eines DOCX oder XLSX
wird der passende Filter gewählt (siehe FORMATS in
pythonpath/paperless_formats.py). Fehlt ein Format dort, speichert
LibreOffice als ODF — dann stimmt die Endung nicht mehr zum Inhalt.
Freigabelinks. Der Endpunkt heißt je nach Paperless-Version
share_links/ oder sharelinks/. Beide werden probiert.
HTTP 403 nur über die Domain, nicht über die IP. Klassisches Zeichen
dafür, dass ein Schutzsystem vor der Anwendung den User-Agent abweist.
urllib sendet ohne Zutun Python-urllib/3.x, und diese Kennung steht in
den Sperrlisten von CrowdSec, fail2ban und gängigen WAF-Regelwerken.
Die Erweiterung setzt deshalb eine eigene Kennung. Wird auch die
abgewiesen, lässt sie sich in paperless_client.py über die Konstante
USER_AGENT ändern.
Zum Nachstellen:
curl -s -o /dev/null -w '%{http_code}\n' -A "Python-urllib/3.10" \
-H "Authorization: Token $PT" https://dms.example.org/api/documents/
403 gegenüber 200 mit der curl-Vorgabe bestätigt die Ursache. Die Fehlermeldung kann dabei von einem völlig anderen Dienst stammen als dem, den man ansprechen wollte.
Symbole
Alle Symbole teilen dieselbe Grundform — ein Blatt mit umgeknickter Ecke in Paperless-Grün — damit die Leiste als zusammengehörige Gruppe erkennbar bleibt. Unterschieden wird über eine farbige Scheibe unten rechts:
| Befehl | Beizeichen |
|---|---|
| Markensymbol | gefüllte grüne Fläche mit weißem Blatt, ganz links in der Leiste |
| Öffnen | blauer Pfeil nach unten |
| Neue Version | oranger Pfeil nach oben mit Plus |
| In Paperless ablegen | oranger Pfeil nach oben mit Sternchen, zweites Blatt dahinter |
| Klassifizierung | violettes Etikett |
| Teilen | türkise verbundene Punkte |
| Einstellungen | graues Zahnrad |
Das Markensymbol steht am Anfang der Leiste, durch einen Trenner von den Befehlen abgesetzt. Es ist mit Status anzeigen belegt, also nicht nur Zierde. Durch die gefüllte Fläche unterscheidet es sich deutlich von den Bordmitteln von LibreOffice — man sieht auf einen Blick, wo die Paperless-Leiste beginnt und endet.
Format
Registriert sind SVG. LibreOffice skaliert sie auf die eingestellte Symbolgröße (Extras → Optionen → Ansicht → Symbolgröße: Klein, Groß, Sehr groß). Mit PNG wären nur zwei feste Größen möglich, und bei „Sehr groß" würde hochgerechnet.
Erzeugt werden die Symbole mit make_icons.py aus einer gemeinsamen
Geometriebeschreibung — SVG und PNG können deshalb nicht auseinanderlaufen.
Das Skript braucht Pillow für die PNG-Ausgabe und wird nur zur Entwicklung
gebraucht; die fertigen Dateien liegen unter icons/ und sind im Paket.
Zusätzlich zu den SVG enthält das Paket PNG in 16, 26 und 32 Pixeln als
Rückfallebene. Sollte die SVG-Unterstützung in einer LibreOffice-Version
nicht greifen und gar kein Symbol erscheinen, in Addons.xcu umstellen:
ImageSmallURL → %origin%/icons/paperless-NAME_16.png
ImageBigURL → %origin%/icons/paperless-NAME_26.png
Reihenfolge in der Leiste
[Paperless] │ [Öffnen] [Neue Version] [Ablegen unter] [Klassifizierung] [Teilen] │ [Verbindung]
Aufbau
paperless_addon.py Protokoll-Handler, Befehlslogik
make_icons.py erzeugt die Symbole (nur zur Entwicklung)
pythonpath/
paperless_client.py REST-Client, nur Standardbibliothek
paperless_config.py Einstellungen, Dokumentzuordnung
paperless_dialogs.py UNO-Dialoge, programmatisch aufgebaut
paperless_formats.py Formate, Filter, Bearbeitbarkeit
Addons.xcu Menü, Symbolleiste, Symbole
icons/ 16x16 und 26x26 PNG je Befehl
ProtocolHandler.xcu Registrierung von paperless.Command:*
Der Protokoll-Handler ist der Kern: Menüeinträge lösen URLs wie
paperless.Command:Open aus, die LibreOffice an die registrierte Klasse
weiterreicht.
Stand und Grenzen
Version 1.1.0. Das Gerüst ist vollständig und die Client-Logik getestet, aber die Erweiterung ist an einer echten Instanz noch nicht über längere Zeit erprobt.
Nicht enthalten:
- Zusatzfelder im Klassifizierungsdialog — nur die Grundfelder
- Fortschrittsanzeige beim Upload — bei großen Dateien wirkt LibreOffice währenddessen eingefroren
- Übersetzungen über die Oberflächentexte hinaus
- Konfliktbehandlung, wenn dasselbe Dokument parallel anderswo geändert wurde
Der letzte Punkt ist der relevanteste für den Mehrbenutzerbetrieb. Paperless kennt keine Sperren; wer zeitgleich arbeitet, erzeugt zwei Versionen statt eines Konflikts. Bei einem kleinen Kreis ist das verschmerzbar.
Die Software wird ohne jegliche Gewähr oder Haftungsübernahme bereitgestellt. Die Nutzung erfolgt auf eigene Gefahr.