Mirror docs/ from 13303b2

github-actions[bot] committed 2026-10-23 09:18:36 +00:00
1 parent 0bd27a1e0b
commit 34e7c167dc
31 files changed
+1417 -76

No files matched your search

+7 -5
@@ -711,7 +711,9 @@ your client wants one, **ask for it rather than engineering around it** (see [§
here as missing while `SearchSnippetGetMethod` was in the tree.
- **Contacts are autocomplete only.** `Contact/autocomplete`, under
`urn:plmail:params:jmap:contacts`, returns ranked recipient suggestions; there is no
`Contact/get`, `/query` or `/set` — the address book is harvested from mail headers, not writable.
`Contact/get`, `/query` or `/set`. Suggestions come from addresses harvested from mail headers and
from the user's CardDAV address books (`inAddressBook: true`); those books are edited in the web
app and synced with their server, not through JMAP.
Calendars are served, under `urn:plmail:params:jmap:calendars`; there is no
`Calendar/set` and no `/changes` on either type, because calendars cannot calculate changes — see
[JMAP](Internals-Jmap).
@@ -1448,10 +1450,10 @@ Ordered roughly by how much users will miss them.
**Writing**
- Compose, reply, reply-all, forward. Rich text.
- Contact autocomplete (harvested from synced mail server-side; ask `Contact/autocomplete` under
`urn:plmail:params:jmap:contacts` — the server ranks over the whole address book, so it beats a
local cache. The OS address book remains a sensible *supplement* for people the user has never
mailed).
- Contact autocomplete (harvested from synced mail server-side, plus any CardDAV address book the
user mirrors; ask `Contact/autocomplete` under `urn:plmail:params:jmap:contacts` — the server ranks
over the whole address book, so it beats a local cache. The OS address book remains a sensible
*supplement* for people the user has never mailed and has not added to a mirrored book).
- Send from any account **and any sendable alias** — always show the From picker.
- Draft autosave.
- Undo send (client-side for JMAP; see above).
+11 -7
@@ -1,4 +1,4 @@
<!-- translated-from: CLIENT_DEVELOPMENT.md sha1:1f3b51a6f659a64adbc57fb6125f4885ce518f82 -->
<!-- translated-from: CLIENT_DEVELOPMENT.md sha1:c51b2fe57829cba3fad6d6c2d3e4c8287e300e05 -->
# Einen Client für plMail bauen
Alles, was eine Entwicklerin (oder ein Agent) braucht, um einen *neuen* plMail-Client zu schreiben
@@ -779,8 +779,10 @@ konstruieren** (siehe [§0](#0-lies-das-zuerst-der-server-wird-aktiv-weiterentwi
fehlend, während `SearchSnippetGetMethod` bereits im Baum lag.
- **Kontakte sind nur Autovervollständigung.** `Contact/autocomplete`, unter
`urn:plmail:params:jmap:contacts`, liefert gereihte Empfängervorschläge; es gibt kein
`Contact/get`, `/query` oder `/set` — das Adressbuch wird aus Mail-Headern geerntet und ist nicht
beschreibbar. Kalender werden bedient, unter `urn:plmail:params:jmap:calendars`; es
`Contact/get`, `/query` oder `/set`. Die Vorschläge kommen aus Adressen, die aus Mail-Headern
geerntet werden, und aus den CardDAV-Adressbüchern des Nutzers (`inAddressBook: true`); diese
Bücher werden in der Web-App bearbeitet und mit ihrem Server synchronisiert, nicht über JMAP.
Kalender werden bedient, unter `urn:plmail:params:jmap:calendars`; es
gibt kein `Calendar/set` und kein `/changes` auf beiden Typen, weil Kalender keine Änderungen
berechnen können — siehe [JMAP](De-Internals-Jmap).
@@ -1592,10 +1594,12 @@ Grob danach geordnet, wie sehr Nutzerinnen sie vermissen werden.
**Schreiben**
- Verfassen, antworten, allen antworten, weiterleiten. Formatierter Text.
- Autovervollständigung von Kontakten (serverseitig aus synchronisierter Mail geerntet; frag
`Contact/autocomplete` unter `urn:plmail:params:jmap:contacts` — der Server reiht über das ganze
Adressbuch und schlägt damit jeden lokalen Cache. Das Adressbuch des Betriebssystems bleibt eine
sinnvolle *Ergänzung* für Menschen, denen der Nutzer noch nie geschrieben hat).
- Autovervollständigung von Kontakten (serverseitig aus synchronisierter Mail geerntet, dazu jedes
CardDAV-Adressbuch, das der Nutzer spiegelt; frag `Contact/autocomplete` unter
`urn:plmail:params:jmap:contacts` — der Server reiht über das ganze Adressbuch und schlägt damit
jeden lokalen Cache. Das Adressbuch des Betriebssystems bleibt eine sinnvolle *Ergänzung* für
Menschen, denen der Nutzer noch nie geschrieben und die er in kein gespiegeltes Buch eingetragen
hat).
- Senden aus jedem Konto **und jedem sendefähigen Alias** — zeig immer die Von-Auswahl.
- Automatisches Speichern von Entwürfen.
- Senden rückgängig machen (bei JMAP clientseitig; siehe oben).
+55 -1
@@ -1,4 +1,4 @@
<!-- translated-from: features/accounts.md sha1:e3b70a077702b079166c724d88537caef5fba35f -->
<!-- translated-from: features/accounts.md sha1:a008e379503ce8b4292e95d3580ed2550552e214 -->
# Konten und Aliase
@@ -282,6 +282,60 @@ Einen Vorbehalt nennt plMail auf der Seite selbst: **Outlook versendet unabhäng
Wahl hier immer über den primären Alias des Kontos.** Das ist Microsofts Verhalten, geändert
wird es in deinen Microsoft-Kontoeinstellungen und nicht hier.
### Gmail holt Mail für ein anderes Konto ab (Gmailify)
Gmail kann die Mail einer anderen Adresse in deinen Gmail-Posteingang holen. Ist diese andere
Adresse auch hier verbunden, kommt jede Nachricht zweimal bei plMail an: einmal über Gmail und
einmal über die eigene IMAP-Synchronisierung des anderen Kontos. plMail legt solche Mail unter dem
anderen Konto ab und führt die beiden Kopien zu einer zusammen, sodass eine Nachricht nur einmal
in der Liste steht. Die zusammengeführte Nachricht gehört weiter dem anderen Konto: Ihr
IMAP-Ordner entscheidet, ob sie im Posteingang, im Papierkorb oder in einem anderen Ordner liegt,
egal was Gmail mit seiner eigenen Kopie gemacht hat. Labels, die du ihr in Gmail gibst, erscheinen
hier, und wenn du sie hier archivierst, markierst oder labelst, geht das auch an Gmail.
Unter jedem Gmail-Konto auf dieser Seite schaltet **Mail für meine anderen Konten unter diesen
ablegen** das an oder aus. Es ist an, solange du es nicht ausschaltest. Schalte es aus, sobald
Gmail diese Mail nicht mehr abholt, typischerweise nachdem eine Domain von Gmail zu einem eigenen
IMAP-Anbieter umgezogen ist. Sonst werden alte und verspätete Kopien aus Gmail weiter dem anderen
Konto zugeordnet. Wenn es aus ist:
- Das Gmail-Konto behält nur Mail, die an es selbst gerichtet ist. Mail, die es für andere
Adressen hat, wird nicht noch einmal gespeichert.
- Die eigenen Kopien des anderen Kontos folgen Gmail nicht mehr. Sie verlieren die Labels, die
Gmail ihnen gegeben hat, und was du an ihnen änderst, geht nicht mehr an Gmail. Die Mail selbst
bleibt, wo ihr IMAP-Ordner sie hat. Das läuft im Hintergrund, bei einem Postfach, das jahrelang
Mail abgeholt hat, also unter Umständen noch ein paar Minuten nach dem Ausschalten.
- Mail, die Gmail unter dem anderen Konto abgelegt hat und die der IMAP-Server des anderen Kontos
nie hatte, bleibt, wie sie ist, und folgt weiter Gmail. Gmail ist der einzige Ort, an dem es
diese Mail gibt.
**Am besten ausschalten, sobald Gmail nicht mehr abholt.** Solange Gmail die Mail der anderen
Adresse noch abholt, unterscheidet plMail diese Mail an ihren Empfängern: Eine Nachricht, deren An
oder Cc die andere Adresse nennt und nicht die Gmail-Adresse, bleibt dem anderen Konto überlassen,
auch wenn Gmail seine eigene Adresse als Zustelladresse daraufstempelt. Eine Nachricht, die per Bcc
an die andere Adresse ging, nennt niemanden, sieht deshalb wie eigene Mail des Gmail-Kontos aus und
liegt in beiden Konten. Mailinglisten und Newsletter kommen oft so an. Beende das Abholen in Gmail
(Einstellungen → Konten → E-Mails von anderen Konten abrufen), dann hören auch diese Doppelten auf.
Schaltest du es wieder an, ändert sich an Gespeichertem nichts. Mail, die danach ankommt, wird
wieder zugeordnet, und eine ältere Nachricht ebenso, sobald Gmail eine Änderung an ihr meldet.
**Mail, die Gmail vor v0.3.9 verschoben hat.** Frühere Versionen haben die Kopie des anderen
Kontos dorthin verschoben, wohin Gmail seine eigene verschoben hat. Mail, die in Gmail gelöscht
oder archiviert wurde, stand in plMail im Papierkorb oder im Archiv, obwohl der IMAP-Server sie
noch im Posteingang hatte. Um diese Mail dorthin zurückzulegen, wo ihr IMAP-Ordner sie hat, führt
ein Administrator aus:
```bash
php bin/console app:backfill gmailify-placement
```
Führe es aus, wenn die Worker ruhig sind. Eine Verschiebung, die du in plMail machst, ändert erst
die Labels und einen Moment später den IMAP-Ordner, und eine Nachricht, die genau dazwischen
erwischt wird, ginge kurz in ihren alten Ordner zurück. Mail, die frühere Versionen entfernt
haben, weil Gmail sie aus seinem Papierkorb geleert hatte, holt das nicht zurück. Die
IMAP-Synchronisierung holt nur Mail, die neuer ist als das, was sie schon hat.
### Was jede Adresse standardmäßig tut
Zwei Einstellungen hängen an der Aliasliste statt am Konto, denn sie sind Eigenschaften der
+33 -5
@@ -1,4 +1,4 @@
<!-- translated-from: features/admin.md sha1:745cb9593e98a39e4729dffb19f7be3e1808df78 -->
<!-- translated-from: features/admin.md sha1:32548a111582d707b9c8a1ef8439eeaebb02c69c -->
# Administration
@@ -333,8 +333,8 @@ den erledigten Stapel ab; der Knopf erscheint nur, wenn etwas darin liegt, und n
## Integrationen
Zwei Dinge wohnen hier: welche Dateidienste diese Installation anbietet, und die
OAuth-Anwendungen, über die Menschen sich bei ihrer Mail anmelden.
Drei Dinge wohnen hier: welche Dateidienste diese Installation anbietet, die Serveradresse für
ihre Kalenderdienste, und die OAuth-Anwendungen, über die Menschen sich bei ihrer Mail anmelden.
**Mail-Anmeldung** hält Client-ID und Client-Secret von Google und Microsoft, den
Microsoft-Tenant und für Gmail das Pub/Sub-Topic und das Push-Verifizierungstoken. Lässt du
@@ -357,6 +357,26 @@ hat:
- **Serveradresse**, bei den selbst gehosteten. Lass sie leer, und jede Person trägt ihre eigene
ein; setze sie, und alle sind an diesen Server gebunden.
**Kalender** listet CalDAV und Kalender-Feeds unter den Dateidiensten auf, mit derselben Zeile und
demselben Formular hinter **Konfigurieren**. Beide stehen allen zur Verfügung, gleich was hier
eingestellt ist, darum steht in der Zeile **Für alle verfügbar**, und das Formular hat keinen
Schalter. Nextcloud steht dort nicht ein zweites Mal: Es wird einmal bei den Dateidiensten
eingerichtet, und seine Kalender nutzen diese Adresse.
- **CalDAV** hat die eine Einstellung, die der Einrichtungsassistent womöglich schon geschrieben
hat: **Serveradresse**. Setze sie, und jede CalDAV-Verbindung findet ihre Kalender auf diesem
Server; leere sie, und es gilt wieder die eigene Adresse jeder Person. Eine Änderung greift, wenn
auf einer Verbindung das nächste Mal Kalender gesucht werden. Bereits gespiegelte Kalender
behalten die Adresse, unter der sie gefunden wurden, und wer eine Adresse in einem privaten Netz
leert, beendet auch ihre Ausnahme von der Regel für private Netze — diese Kalender
synchronisieren dann nicht mehr, bis der Host in `INTEGRATIONS_ALLOWED_HOSTS` steht. Sobald sich
jemand verbunden hat, sagt das Formular, wie viele Verbindungen eine Änderung erreicht. Siehe
[CalDAV](De-Providers-Caldav).
- **Calendar feed**, der Kalender-Feed, hat keinen Server zum Festlegen, denn jeder Feed ist seine
eigene Adresse; seine Zeile hat darum nichts einzurichten. Nur eine Adresse, die der
Einrichtungsassistent früher gespeichert hat, wird angezeigt, mit **Konfigurieren**, um sie zu
leeren: Eine solche Adresse lässt nur Feeds auf ihrem Host an der Regel für private Netze vorbei.
**Zugangsdaten von … übernehmen** kopiert für Google wie für Microsoft eine Client-ID und ein
Secret serverseitig hinüber, ohne das Secret je anzuzeigen. Ein Google-Cloud-Projekt deckt auch
Drive und Photos ab; eine Entra-Registrierung deckt auch OneDrive ab. Die Zugangsdaten zu
@@ -638,7 +658,7 @@ Speicher und der Klick hat nichts gebracht. Praktisch nach einem Neustart — un
was ein kalter Ladevorgang auf deiner Hardware wirklich kostet, bevor du entscheidest, wie lange du
das Modell hältst.
### Die vier Funktionen sind einzeln schaltbar
### Die fünf Funktionen sind einzeln schaltbar
Sie kosten sehr Unterschiedliches, und es hat sehr unterschiedliche Folgen, wenn das Modell danebenliegt.
@@ -648,6 +668,7 @@ Sie kosten sehr Unterschiedliches, und es hat sehr unterschiedliche Folgen, wenn
| **Mail in Tabs einsortieren** | Setzt eine Kategorie, wenn keine Regel gegriffen hat | Bei jeder eingehenden Nachricht |
| **Beim Schreiben helfen** | Bietet Entwürfe und Umformulierungen im Editor an | Nur wenn du fragst |
| **Konversationen zusammenfassen** | Schreibt einen kurzen Abriss eines langen Gesprächs | Nur wenn jemand darum bittet |
| **Kontakthilfe** | Liest einen Kontakt aus eingefügtem Text und schlägt Änderungen an einem Kontakt anhand der Signatur seiner letzten Mail vor | Nur wenn jemand auf **Mit KI verbessern** klickt |
Beim Einsortieren solltest du am ehesten vorsichtig sein: Es läuft unbeaufsichtigt. Es kann immer nur
den Stichentscheid geben — wenn ein Header, ein Gmail-Label oder die Tatsache, dass du der Person
@@ -674,7 +695,7 @@ Zwei Dinge sorgen dafür, dass das Einsortieren ankommender Mail nicht im Weg st
Die Schreibhilfe ersetzt nie, was du geschrieben hast. Sie hängt an, dein Original bleibt stehen.
Zusammenfassungen sind die teuerste der vier. Sie nutzen dasselbe Schreibmodell, aber ein ganzes
Zusammenfassungen sind die teuerste der fünf. Sie nutzen dasselbe Schreibmodell, aber ein ganzes
Gespräch ist eine deutlich größere Eingabe als ein Entwurf: Auf einem 30B-Modell mit einer GPU
rechne mit rund einer Minute für die erste Zusammenfassung nach einer ruhigen Phase und etwa der
Hälfte, solange das Modell warm ist. Die GPU ist die ganze Zeit belegt, auf einer geteilten Maschine
@@ -682,6 +703,13 @@ merken das also alle. Von allein wird nie etwas zusammengefasst — keine eingeh
nächtlicher Durchlauf — und eine geschriebene Zusammenfassung wird aufbewahrt und wieder angezeigt,
das zweite Öffnen derselben Konversation kostet also nichts.
Die Kontakthilfe schickt Text über andere Menschen: was jemand in einen neuen Kontakt eingefügt hat,
oder einen gespeicherten Kontakt zusammen mit dem Signaturblock der neuesten Mail, die dieser Kontakt
geschickt hat — der Teil unter `-- ` oder unter der Grußformel („Viele Grüße“), nie der Text der Mail.
Eine Mail mit keinem von beiden schickt nichts aus dieser Mail. Nichts, was sie vorschlägt, wird
gespeichert, bevor die Person es anhakt und den Kontakt speichert. Ohne sie funktioniert das Einfügen
trotzdem: plMail liest den Text nach Regeln, nur der Knopf, der das Modell fragt, fällt weg.
### Suchen nach Bedeutung braucht einmal einen Durchlauf
Die semantische Suche findet nur Mail, die indexiert wurde, und das Einschalten allein indexiert
+14 -4
@@ -1,4 +1,4 @@
<!-- translated-from: features/calendar-sync.md sha1:2035fa53b2757d4eef5706a37a0d6fab6f70ef92 -->
<!-- translated-from: features/calendar-sync.md sha1:86f9d9e067468c420433595984c981709680f476 -->
# Verbundene Kalender
@@ -23,9 +23,19 @@ Zustimmung erfragt wie den Postfachzugriff. Es gibt keine zweite Anmeldung, kein
registrieren und nichts Zweites einzurichten — das Konto zeigt eine Schaltfläche **Kalender auf …
suchen**, und das ist alles.
**Verbindungen, die es nur für Kalender gibt.** Ein **CalDAV-Server** — Nextcloud, Radicale, Baïkal,
Fastmail, iCloud, eine Kiste im Schrank — oder eine **Kalenderadresse**, also jede veröffentlichte
`.ics`.
**Verbindungen, die es nur für Kalender gibt.** Ein **CalDAV-Server** — Radicale, Baïkal, Fastmail,
iCloud, eine Kiste im Schrank — oder eine **Kalenderadresse**, also jede veröffentlichte `.ics`.
**Deine Nextcloud-Verbindung.** Hast du Nextcloud für Dateien unter **Einstellungen →
Integrationen** verbunden, steht sie auch hier, mit einer eigenen Schaltfläche **Kalender auf …
suchen**. Dasselbe App-Passwort erreicht deine Nextcloud-Kalender, es gibt also keine Adresse und
kein Passwort ein zweites Mal einzugeben. Statt einer Schaltfläche zum Trennen steht dort *Unter
Integrationen eingerichtet*: Die Verbindung hält auch deine Dateien, also wird sie dort getrennt,
und die Rückfrage dort sagt, wie viele Kalender mitgehen.
Ein Kalender, der schon über eine andere Verbindung gespiegelt wird — etwa über eine CalDAV-Verbindung,
die du von Hand zur selben Nextcloud angelegt hast —, erscheint als *Wird bereits über … gespiegelt*
und lässt sich kein zweites Mal anhaken, damit seine Termine nicht doppelt synchronisiert werden.
Sagt der Abschnitt *Noch kein Konto und keine Verbindung hier führt Kalender.*, hast du IMAP-Konten
und sonst nichts; verbinde einen CalDAV-Server oder abonniere eine Adresse.
+36 -9
@@ -1,4 +1,4 @@
<!-- translated-from: features/calendar.md sha1:122f0e14aa2280cfe38b171539644284c35d7c38 -->
<!-- translated-from: features/calendar.md sha1:d781321a4c7533e5b2770a4250b64e69f3cfee39 -->
# Kalender
@@ -76,13 +76,25 @@ Zurück und Weiter springen in der Monatsansicht um einen Monat, in der 7-Tage-A
und überall sonst um einen Tag — die Agenda eingeschlossen, denn sie ist eine fortlaufende Liste und
keine Seite.
**Auf einem schmalen Bildschirm rückt die Werkzeugleiste zusammen, statt zu scrollen.** Unter etwa
590 Pixeln zeigt der Ansichtsumschalter Symbole, und das Datum nimmt eine kurze Form an: der Monat
ohne Jahr, solange es dieses Jahr ist (**Oktober**), eine Woche als **20.–26. Okt.**, ein Tag als
**20. Okt.** Auf den schmalsten Telefonen wird **Heute** zum heutigen Datum in einem kleinen Kästchen.
Die volle Seite öffnet mit der Ansicht **7 Tage**. Die angedockte Fläche öffnet mit **Agenda** — sie
ist 380 Pixel einer geteilten Zeile, und ein Monatsraster in dieser Breite besteht überwiegend aus
leeren Zellen. Die Fläche bietet trotzdem alle vier Ansichten an, als Symbole statt als Wörter, denn
die Fassung, die den Umschalter ganz weggelassen hatte, ließ die Fläche nichts als ihre Agenda
zeigen.
In der Monatsansicht zeichnet eine Zelle so viele Einträge ihres Tages, wie hineinpassen, und blendet
**In der Monatsansicht steht bei einem Eintrag der Titel zuerst**, auf bis zu zwei Zeilen, und
darunter, wann er ist und wo: **Quartalsplanung Q4**, dann **10:00–11:00 · Raum Elbe**. In einem
schmaleren Tag fallen das Ende und der Ort weg, und der Titel bekommt eine dritte Zeile in kleinerer
Schrift; lange Wörter werden in der Sprache getrennt, auf die plMail eingestellt ist. Eine Einladung,
die du noch nicht beantwortet hast, hat einen gestrichelten Balken, damit die offenen Einladungen
der Woche auf einen Blick zu sehen sind.
Eine Zelle zeichnet so viele Einträge ihres Tages, wie hineinpassen, und blendet
den letzten dort aus, wo der Platz endet — statt sie zu zählen. Früher stand dort **N weitere**, und
diese Zahl konnte nicht stimmen: Die Höhe einer Zelle ist die des Fensters geteilt durch sechs, und
ein Eintrag ist ein oder zwei Zeilen hoch, je nachdem, ob er eine Uhrzeit hat. Die Zahl war immer
@@ -92,13 +104,18 @@ diesen Tag, und dort stehen die übrigen.
**Bleibt der Zeiger auf einem Tag stehen, klappt dieser ganze Tag heraus.** Die Zelle wird zu einer
kleinen Fläche, breit genug für die vollen Titel und lang genug für jeden Eintrag. Dabei verrutscht
nichts: Sie wächst nach rechts und nach unten, genau von dort, wo die Liste ohnehin stand, und der
Eintrag, den du angesehen hast, liegt weiterhin unter dem Zeiger.
Eintrag, den du angesehen hast, liegt weiterhin unter dem Zeiger. Auf einem Touchscreen, der keinen
Zeiger hat, der stehen bleiben könnte, **öffnet der erste Tipp auf einen Tag diesen Tag** und tut
sonst nichts; die Einträge darin öffnen sich beim nächsten Tipp.
**Wo eine Zelle zu schmal für Wörter ist, verzichtet sie darauf.** Sieben Spalten eines Telefons
sind je etwa vierzig Pixel breit, und in dieser Breite wird weder ein Titel noch eine Uhrzeit fertig.
Darunter wird ein Eintrag deshalb als Balken in der Farbe seines Kalenders gezeichnet — wie viele,
und aus welchen Kalendern, auf einen Blick. Ein Tipp auf das Datum öffnet den Tag, dort stehen sie
ausgeschrieben.
**Wo eine Zelle zu schmal für Wörter ist, verzichtet sie darauf, und der Tag steht stattdessen
unter dem Monat.** Sieben Spalten eines Telefons sind je etwa fünfzig Pixel breit, und in dieser
Breite wird weder ein Titel noch eine Uhrzeit fertig. Dort wird ein Eintrag deshalb als Balken in der
Farbe seines Kalenders gezeichnet — wie viele, und aus welchen Kalendern, auf einen Blick —, und das
Raster wird kleiner, um darunter Platz für die Liste eines Tages zu machen, ausgeschrieben, wie die
Agenda schreibt. Sie beginnt mit heute; **ein Tipp auf einen beliebigen Tag, die Balken
eingeschlossen, listet stattdessen diesen Tag**. Die Liste hat ihr eigenes **+**, das einen neuen
Termin an diesem Tag öffnet, und **Neuer Termin** in der Werkzeugleiste legt ihn ebenfalls dort an.
Die Agenda fasst ihre Einträge unter einer Überschrift je Tag zusammen, die oben stehen bleibt,
solange die Termine dieses Tages auf dem Schirm sind. Beginn und Ende — oder **Ganztägig** — stehen
@@ -141,6 +158,11 @@ Quadratzentimeter groß und hat keinen Platz zu sagen, wo im Tag etwas liegt; un
Agenda besteht darin, die leere Zeit zu überspringen — also genau das, was eine Rasterachse zeichnen
würde.
**Die rote Linie der aktuellen Zeit läuft mit der Uhr mit**, einmal pro Minute, und wenn sich das
Datum ändert, zeichnet sich die Ansicht neu — die Linie, die Tönung auf heute und der Kreis um das
heutige Datum im Monat wandern zum neuen Tag —, sodass ein Kalender, der neben der Mail offen bleibt,
stimmt.
**Das Raster wird auf deiner Uhr gezeichnet.** Die Stunden an der Seite, die Stelle, an der die
Linie der aktuellen Zeit sitzt, und die Zeit, die für einen neuen Termin vorgeschlagen wird, wenn du
auf leeren Raum doppelklickst, folgen alle der Zeitzone aus **Einstellungen → Allgemein**. Das ist
@@ -179,7 +201,12 @@ Agenda, und die Tagesansicht ist in der Werkzeugleiste einen Tipper entfernt und
**Neuer Termin** in der Werkzeugleiste, das **+** an einer Tagesüberschrift und ein Klick auf einen
vorhandenen Termin öffnen alle denselben Editor. Er ist ein Dialog über der Seite, auf der du gerade
warst — der Kalenderseite oder einer Mail-Ansicht mit dem angedockten Kalender daneben — und
Speichern bringt dich dorthin zurück, statt dich in den Kalender zu ziehen.
Speichern bringt dich dorthin zurück, statt dich in den Kalender zu ziehen. Auf einem Telefon füllt er
den ganzen Bildschirm, wie das Fenster zum Verfassen.
Ein **+** an einem Tag öffnet den Editor an diesem Tag zur nächsten vollen Stunde. Ein Tag, der schon
vorbei ist, behält sein Datum: Ein Termin, den du nachträglich einträgst, landet an dem Tag, an dem er
war, nicht an heute.
Die Felder sind **Titel**, **Beginn**, **Ende**, **Ganztägig**, **Kalender**, **Wiederholung**,
**Ort**, **Beschreibung** und **Erinnerungen**, dazu **Ändern**, sobald sich der Termin wiederholt.
+224
@@ -0,0 +1,224 @@
<!-- translated-from: features/contacts.md sha1:713dc966d588067fd0d5ad0abafcfad4a6e76e2e -->
# Kontakte
plMail führt ein Adressbuch: die Menschen, denen du schreibst, mit ihren Adressen, Telefonnummern,
Postanschriften, Geburtstagen, einem Foto und Notizen. Es ist eine Liste, egal wo ein Kontakt liegt. Manche
gehören nur plMail und gibt es nur hier. Andere kommen aus einem Adressbuch auf einem Server, und
Änderungen gehen in beide Richtungen:
- ein **Google**- oder **Microsoft**-Mailkonto bringt seine Kontakte mit, auf derselben Anmeldung
wie seine Mail und Kalender — nichts zu verbinden (siehe [Google](De-Providers-Google) und
[Microsoft](De-Providers-Microsoft));
- deine [Nextcloud](De-Providers-Carddav#einen-server-verbinden) oder ein anderer CardDAV-Server
wird unter **Einstellungen → Kontakte** verbunden.
Ein Konto, das verbunden wurde, bevor plMail Kontakte synchronisiert hat, braucht eine weitere
Anmeldung, um sie zu erlauben: **Einstellungen → Kontakte** und **Einstellungen → Zustand der Konten** bieten sie
beide an.
Du öffnest es mit dem Adressbuch-Symbol neben dem Kalender-Knopf. Wie der Kalender-Knopf schaltet
es durch drei Stellungen: das Adressbuch neben der Mail, dann in voller Breite mit deiner Mail
dahinter, dann wieder zu. **Einstellungen → Kontakte → Verhalten** macht daraus zwei Stellungen
(**Nur volle Breite** oder **Nur neben der Mail**), wie beim Kalender. Auf dem Handy öffnet
**Kontakte** im Menü die ganze Seite.
Das angedockte Adressbuch ist das ganze Adressbuch: Du suchst, öffnest, bearbeitest, legst an und
führst Kontakte dort zusammen, während deine Mail bleibt, wo sie war. Zieh an seinem Rand, um es
breiter oder schmaler zu machen (oder nimm die Pfeiltasten am Rand; ein Doppelklick stellt es
zurück). Ziehst du es über seine größte Breite hinaus und lässt los, geht es auf volle Breite; über
seine kleinste hinaus, schließt es sich. plMail merkt sich Breite und Stellung. Die Pfeile in seiner
Kopfzeile schalten auf volle Breite, und das × schließt es. Öffnest du bei voller Breite eine
Nachricht aus einem Kontakt, kommt die Mail daneben zurück.
Auf einem breiten Monitor (ab 1920 Pixeln Breite) können Adressbuch und Kalender nebeneinander
angedockt sein. Auf einem kleineren Bildschirm, etwa dem eines Laptops, ist neben der Mail Platz für
eines von beiden, also räumt das eine das andere weg. Unter 1024 Pixeln öffnet das Symbol die ganze
Seite.
## Die Liste
Jeder Kontakt als Karte: Foto oder Initialen, Name, Position und Firma und die wichtigste
E-Mail-Adresse. Die Suche findet einen Kontakt über jeden seiner Namen, seine Firma oder jede seiner
Adressen. Schlagwörter (du vergibst sie an einem Kontakt) stehen als Chips über der Liste und
grenzen sie auf ein Schlagwort ein.
Sobald du ein synchronisiertes Adressbuch hast, wählt eine zweite Reihe Chips, wo du suchst: **Alle
Bücher**, **Nicht synchronisiert** (die Kontakte, die nur in plMail liegen) oder ein Adressbuch. Ein
synchronisierter Kontakt trägt eine kleine Wolke, oder ein Schloss, wenn sein Adressbuch
schreibgeschützt ist. Eine Uhr heißt, dass eine hier gemachte Änderung darauf wartet, an den Server
zu gehen.
## Ein Kontakt
Die Seite eines Kontakts zeigt seine Angaben, seine Notizen, verwandte Kontakte und
**Referenz-E-Mails**: jede nicht gelöschte Nachricht an ihn oder von ihm, in jedem Ordner. Ein
Klick öffnet sie in der Mail neben dem Kontakt, der bleibt, wie er ist; auf der ganzen Seite schiebt
sich die Mail von links herein.
**Neuer Kontakt** öffnet die **Schnellerfassung**: ein Feld, in das du irgendetwas über die Person
tippst oder einfügst — einen Namen, eine E-Mail-Signatur, eine Zeile wie
`Mia Hoffmann <mia@example.net>`, eine Adresse oder eine ganze vCard. Es wird beim Tippen gelesen,
und was gefunden wird, erscheint darunter als Zeilen: Jede kannst du abwählen, neu tippen oder mit
einem Klick einem anderen Typ zuordnen (eine Arbeitsnummer, die eine private ist, eine Zeile, die als
Position erkannt wurde und eigentlich die Firma ist). Eine Zeile, die nicht zugeordnet werden konnte,
wird erfragt statt geraten, eine Grußformel über einer Signatur wird weggelassen, und eine Adresse,
die schon bei einem deiner Kontakte steht, wird angezeigt. Eine vCard wird Feld für Feld gelesen,
nichts wird geraten. **Alle Felder** öffnet den vollständigen Editor mit allem, was gefunden wurde.
Der **Editor** zeigt den Namen als ein Feld; seine Teile (Anrede/Titel, Vor-, zweiter Vor- und
Nachname, Namenszusatz) füllen sich beim Tippen und öffnen sich unter **Teile bearbeiten**. Bearbeitest
du einen Teil von Hand, bleibt er so, und die Seite sagt, wenn Name und Teile nicht mehr zusammenpassen.
E-Mail und Telefon enden in einer gestrichelten Zeile — tipp hinein, und sie wird zu einer Zeile,
als privat oder geschäftlich nach der Adresse oder als Handy nach der Nummer eingeordnet, bis du
selbst einen Typ wählst. Der Stern markiert die bevorzugte. Jede andere Art von Angabe erscheint nur,
wenn der Kontakt eine hat; die Chips unter **Hinzufügen** holen den Rest. Sobald sich etwas geändert
hat, sagt eine Leiste unten, wie viel, und bietet **Speichern** und **Verwerfen** an.
**Details einfügen** im Editor liest eingefügten Text genauso und vergleicht ihn mit dem Kontakt,
bevor sich etwas ändert: Was schon da ist, ist ausgegraut, was neu ist, ist angehakt, und was vom
Gespeicherten abweicht („Ersetzt …“ oder ein anderer Typ für dieselbe Nummer), hakst du selbst an.
**Übernehmen** trägt das Angehakte ins Formular ein und markiert es; gespeichert wird erst, wenn du
speicherst.
Hat deine Administration die **Kontakthilfe** eingeschaltet (Admin → KI), haben beide Seiten
**Mit KI verbessern**. In der Schnellerfassung liest es den Text ein zweites Mal — die Firma, die die
Regeln nicht von einem Slogan unterscheiden konnten, eine Faxnummer, die als `Fax -99` dasteht — und
markiert, was es geändert hat; **KI-Änderungen rückgängig** geht zurück. Im Editor schickt es den
Kontakt und die Signatur der neuesten Mail, die der Kontakt dir geschickt hat, und zeigt seine
Vorschläge, jeden mit Begründung, in derselben Liste wie eingefügte Details. Für dich selbst schaltest
du es unter Einstellungen → Assistent ab.
Ein Kontakt hat:
- seinen Namen in Teilen (Anrede/Titel, Vor-, zweiter Vor- und Nachname, Namenszusatz), einen
Spitznamen und wie die Namen gesprochen werden (**Phonetische Schreibweise**, für Namen, die ein
Handy anders sortieren oder vorlesen soll);
- Firma, Abteilung, Position und Büro;
- E-Mail-Adressen, Telefonnummern und Postanschriften, jede mit Bezeichnung und je eine als primär
markiert;
- einen Geburtstag und weitere Daten (etwa einen Jahrestag) — das Jahr darf fehlen;
- **Personen**: die Namen von Ehepartner, Kindern, Vorgesetzten, Assistenz und so weiter;
- Websites, Chat-Adressen (SIP, XMPP, Skype, Teams, Matrix und andere) und **eigene Felder** mit
selbst gewählten Namen;
- Notizen, Schlagwörter und ein Foto.
Eine Bezeichnung, die vom Server kommt und keine von plMails eigenen ist (eine auf dem Handy
getippte „Patentante“), wird so gezeigt, wie sie ist, und bleibt beim Speichern erhalten.
Hast du ein synchronisiertes Adressbuch, wählst du unter **Speichern in**, wohin ein neuer Kontakt
kommt: ein Knopf pro Adressbuch, wenn du bis zu vier hast, ein Menü, wenn es mehr sind. **plMail**
behält ihn nur in plMail. Sonst geht er an den Server dieses Adressbuchs und von dort auf jedes
Gerät, das es synchronisiert; die Zeile unter den Knöpfen sagt, wohin. Ein schreibgeschütztes
Adressbuch wird nie angeboten.
Bearbeiten geht bei beiden Arten gleich. Bei einem synchronisierten Kontakt wird die Änderung hier
sofort gespeichert und gleich danach an den Server geschickt, und von dort auf dein Handy. Was
plMail nicht als Feld hat, etwa die Eigenschaften, mit denen ein Handy einen Kontakt an eine App
bindet, bleibt auf der Kopie des Servers unberührt. Google und Outlook haben für manche Felder
keinen Platz; siehe **Fallstricke**. Wie
Änderungen und Konflikte behandelt werden, steht unter
[CardDAV](De-Providers-Carddav#synchronisation).
Einen Kontakt aus einem **schreibgeschützten** Adressbuch (einem geteilten Buch, einem
Firmenverzeichnis) kannst du lesen und anschreiben, aber hier nicht ändern oder löschen. Seine Seite
sagt, woher er kommt.
**Fotos** sind JPG, PNG oder WebP bis 5 MB und werden mit höchstens 512 Pixeln an der längeren
Seite gespeichert. Sie gehen in beide Richtungen: Ein Foto auf dem Server wird hier angezeigt, und
ein Foto, das du hier hochlädst oder entfernst, geht an den Server. Im Editor lädt die Kamera am
Foto eines hoch, und **Foto entfernen** unten entfernt es.
## Löschen
**Löschen** legt einen Kontakt in den Papierkorb. Einen synchronisierten Kontakt zu löschen löscht
auch seine Karte auf dem Server, und der Kontakt bleibt im Papierkorb als einer, der nur in plMail
liegt.
## Duplikate
**Duplikate** listet Paare, die nach derselben Person aussehen, mit der Sicherheit des Treffers und
dem Grund (dieselbe E-Mail-Adresse, dieselbe Telefonnummer, ein ähnlicher Name). Wähle den, der
bleiben soll, und **Zusammenführen**. Adressen, Nummern und Schlagwörter des anderen wandern zum
behaltenen Kontakt, und seine Notizen werden unter die des behaltenen gehängt. Eine Zusammenführung
lässt sich 90 Tage lang rückgängig machen.
Synchronisierte Kontakte werden zusammengeführt wie alle anderen. Die Karte des behaltenen wird
aktualisiert, und die des zusammengeführten wird auf seinem Server gelöscht, sodass ein Rückgängig
den zusammengeführten Kontakt als einen zurückbringt, der nur in plMail liegt. Kontakte aus einem
schreibgeschützten Adressbuch lassen sich nicht zusammenführen.
## Import und Export
**Import** nimmt eine `.vcf`-Datei (vCard, wie jedes Handy und jedes Adressbuch sie exportiert) oder
eine `.csv`-Datei. Du siehst zuerst eine Vorschau. Importierte Kontakte liegen nur in plMail, und
danach landest du auf der Duplikate-Seite, um zusammenzuführen, was du schon hattest.
**Export** lädt alle Kontakte als eine `.vcf`-Datei herunter, mit Fotos. Ein synchronisierter
Kontakt wird als Karte seines Servers exportiert, mit deinen Änderungen darin, sodass die Datei
behält, wofür plMail kein Feld hat. Der CSV-Export ist eine Übersicht für eine Tabelle — eine
E-Mail-Adresse, eine Nummer und eine Website pro Kontakt — und lässt sich wieder importieren; um
Kontakte vollständig umzuziehen, nimm vCard.
## Beim Lesen und Schreiben von E-Mails
Beim Absender einer Nachricht und bei jeder Adresse in ihren Details (An, Cc, Antwort an) siehst du,
ob sie schon in deinen Kontakten steht, mit einem Knopf, um sie hinzuzufügen, wenn nicht. Der Knopf
fragt, wer das ist:
- **Neuer Kontakt**: Prüf den Namen aus der E-Mail und speichere. *Weitere Angaben* öffnet den
vollen Editor, Name und Adresse sind schon eingetragen.
- **Zu einem bestehenden Kontakt hinzufügen**: Such beim Tippen in deinen Kontakten und wähl einen
aus. Die Adresse kommt zu diesem Kontakt dazu, so bleibt jemand, der von einer zweiten Adresse
schreibt, eine Person. Die Suche beginnt mit dem Namen des Absenders. Kontakte in
schreibgeschützten Adressbüchern werden nicht angeboten.
Nach dem Speichern öffnet sich der Kontakt im angedockten Adressbuch neben der Mail. War es zu,
geht es auf, war es offen, springt es zu diesem Kontakt. Auch *Weitere Angaben* öffnet den Editor
dort. Auf dem Handy wird nichts angedockt: *Weitere Angaben* öffnet den Editor als Seite, und nach
dem Speichern wird der Knopf zu einem Link auf den Kontakt.
Adressierst du eine Nachricht, werden dir deine Kontakte
vorgeschlagen, zusammen mit den Adressen, die plMail aus deinen E-Mails kennt. Die Reihenfolge steht
unter [Vorschläge beim Schreiben](De-Providers-Carddav#vorschläge-beim-schreiben).
## Auf dem Handy
Die plMail-Apps lesen und bearbeiten dieselben Kontakte über JMAP (`Contact/get`, `Contact/query`,
`Contact/set`). Jeder Kontakt sagt, in welchem Adressbuch er liegt und ob er sich ändern lässt. Eine
App kann einen Kontakt aus einem schreibgeschützten Buch nicht ändern, und ihre Änderungen an einem
synchronisierten Kontakt gehen an den Server weiter wie Änderungen im Web.
## Fallstricke
**Der Papierkorb hat noch keine Seite.** Ein gelöschter Kontakt wird aufbewahrt, aber im Web holt
ihn nichts zurück. Führe Duplikate lieber zusammen, statt sie zu löschen: Eine Zusammenführung lässt
sich rückgängig machen.
**Ein Import legt Kontakte an, die nur in plMail liegen.** Willst du eine `.vcf`-Datei in ein
synchronisiertes Adressbuch bringen, importiere sie auf dem Server (in der Nextcloud unter
Kontakte), und die Kontakte kommen mit der nächsten Synchronisierung hierher.
**Ein Kontakt kann nicht zwischen Adressbüchern wandern**, auch nicht aus plMail in ein
synchronisiertes. Lege ihn am anderen Ort an und lösche den alten.
**Google und Outlook fassen nicht alles.** Google behält jedes Feld oben. Outlook hat feste Plätze:
drei E-Mail-Adressen, zwei geschäftliche und zwei private Nummern und eine Mobilnummer, je eine
Anschrift jeder Art, je einen Ehepartner, Vorgesetzten und eine Assistenz und beliebig viele Kinder,
eine Website, drei Chat-Adressen und einen Geburtstag. Weitere Daten, einen phonetischen zweiten
Vornamen und eigene Felder kennt es nicht. Was nicht passt, wird nicht gesendet und bleibt nur in
plMail, bis sich der Kontakt das nächste Mal in Outlook ändert (siehe
[Microsoft](De-Providers-Microsoft#kontakte-hängen-ebenfalls-an-derselben-anmeldung)).
**Outlook behält ein hier entferntes Foto.** Microsofts Schnittstelle kann das Foto eines
Outlook-Kontakts ersetzen, aber nicht entfernen; ein in plMail entferntes Foto kommt also zurück,
sobald sich der Kontakt in Outlook ändert. Entferne es in Outlook.
**Eigene Felder und das Büro gehören auf einem CardDAV-Server plMail.** vCard hat für beides keine
Eigenschaft, also stehen sie unter plMails eigenen Namen auf der Karte (`X-PLMAIL-FIELD`,
`X-PLMAIL-OFFICE`). Sie überstehen den Hin- und Rückweg, aber ein Handy zeigt sie nicht.
---
*This page is generated from [`docs/de/features/contacts.md`](https://github.com/karatektus/pl_mail/blob/main/docs/de/features/contacts.md). Edit it there — changes made here are overwritten on the next push to `main`.*
+77 -4
@@ -1,4 +1,4 @@
<!-- translated-from: features/mail.md sha1:3ce077e532ee6d78f8b007edb8ab2e563af0413d -->
<!-- translated-from: features/mail.md sha1:df37980670901d8064e9a02c3c0316ebd5549b93 -->
# Mail
@@ -333,6 +333,12 @@ Was ein Datum trägt, reiht sich im Kalender-Panel **Demnächst** ein, unter **A
eine Konversation, in der etwas gefunden wurde, zeigt ihre Karten außerdem in einer Leiste
**In dieser Konversation gefunden** über den Nachrichten.
Mail im Spam ergibt keine Karten, auch nicht Mail, die du später dorthin verschiebst: Ihre Karten
verschwinden aus dem Panel und aus der Leiste über der Mailliste, und eine Spam-Konversation zeigt
keine über ihren Nachrichten — eine gefälschte Versandbenachrichtigung ist genau das, was Spam
nachahmt. **Kein Spam** (oder das Verschieben in den Posteingang) holt sie zurück. Der Papierkorb ist
etwas anderes: Wer eine Versandbestätigung löscht, hält das Paket nicht auf, also bleiben ihre Karten.
Auch ein Shop, der nie einen Zusteller nennt, ergibt ein Paket. Amazon nennt eine Bestellnummer und
einen Link in den eigenen Tracker und nirgends eine Sendungsnummer — genau das trägt dann die Karte.
Und ein Liefertag, der in Worten statt in Ziffern steht ("Arriving today", "Ankunft Montag"), wird
@@ -443,8 +449,10 @@ die SPF-, DKIM- und DMARC-Urteile daneben, sofern der Anbieter einen
HTML-Texte werden direkt eingebettet dargestellt und nicht in einem iframe. Vorher läuft der
Text genau einmal, beim Import, durch einen Sanitizer: `cid:`-Verweise werden auf plMails
eigene Anhang-Route umgeschrieben, `<style>`-Blöcke werden auf die Elemente heruntergezogen,
für die sie galten, und Skripte, Formulare, iframes und Klassen fallen weg. Links werden
gezwungen, außerhalb der App zu öffnen.
für die sie galten, und Skripte, iframes und Klassen fallen weg. Ein Formular wird aufgelöst statt
entfernt: Sein Inhalt bleibt, seine Textfelder und Auswahllisten fallen weg, und die Mail sieht so
aus, wie ihr Absender sie für ein Mailprogramm ohne Formulare gedacht hat. Links werden gezwungen,
außerhalb der App zu öffnen.
### Ein langes Gespräch zusammenfassen
@@ -604,6 +612,9 @@ deren Text nur `help.wirhub.de` steht.
`junk` (oder `spam`), `archive` (oder `archived`) und `snoozed`. Werte in Anführungszeichen
bleiben zusammen, `from:"Ada Lovelace"` funktioniert also.
Die Suche lässt Spam und Papierkorb aus, solange du sie nicht mit `in:spam` oder `in:trash`
verlangst, so wie die Listen auch. Die Vorschläge beim Tippen lassen beide aus.
Vorschläge erscheinen beim Tippen — und **echte Treffer** ebenfalls: die zehn neuesten passenden
Konversationen, unter den Operator-Vorschlägen, etwa ab dem dritten Zeichen. Sie sind eine Vorschau
und nicht die Antwort: Es laufen nur die Durchgänge, die schnell genug für jeden Tastendruck sind.
@@ -715,6 +726,37 @@ Knopf oben auf dem Bildschirm dreht sich, solange er läuft, und der Hinweis dre
Berührung ist der Knopf der Ersatz: ein benannter Knopf für Tastatur und Bildschirmleser, und die
Meldung zur Synchronisierung sagt, wann ein Lauf beginnt und endet.
Auf dem Telefon lässt sich eine Zeile auch wischen. Zieh sie nach rechts, und geschieht, was die
Richtung rechts eingestellt hat; standardmäßig wird sie als gelesen markiert, oder als ungelesen,
wenn sie schon gelesen ist. Zieh sie nach links, und standardmäßig wird sie archiviert. Was jede Richtung
tut, stellst du unter **Einstellungen → E-Mail → Wischgesten** ein (siehe unten). Über ein Drittel der Zeilenbreite wartet die Aktion hinter der Zeile, und Drücken führt
sie aus. Über zwei Drittel, oder ein schneller Wisch über das erste Drittel, geschieht sie beim
Loslassen. Ein Tippen irgendwo anders schließt eine offene Zeile, und dieses Tippen tut sonst nichts.
Gelesen / ungelesen und Archivieren gibt es nur bei normalen Mails: nie bei einem Entwurf und nie im
Archiv, im Papierkorb oder bei Spam. Stern gibt es bei jeder Zeile, auch bei Entwürfen. Ohne Berührung erledigen das
Kontrollkästchen der Zeile (ihr Avatar) und die Leiste dieselbe Arbeit, **Archivieren** und **Mehr**
für eine Auswahl eingeschlossen.
Jede Richtung hat ihre eigene Einstellung unter **Einstellungen → E-Mail → Wischgesten**, je eine
Auswahl für rechts und für links. Zur Wahl stehen Gelesen / ungelesen, Archivieren, Stern und Nichts.
Beide Richtungen dürfen auf dasselbe gesetzt sein. Löschen gehört nicht dazu (siehe *Fallstricke*). Ein Stern ist auf der Zeile ein Schalter: Ein zweiter Wisch nimmt ihn wieder weg, wie bei Gelesen /
ungelesen; Archivieren bietet sein **Rückgängig** an.
Auf dem Telefon öffnet auch ein längeres Drücken einer Zeile ihre Aktionen. Halte eine Zeile etwa eine
halbe Sekunde still, und von unten erscheint ein Aktionsblatt mit allem, was die Zeile anbietet:
auswählen, als gelesen oder ungelesen markieren, archivieren, markieren, verschieben (nur eine
Unterhaltung, nicht eine einzelne Nachricht), mit Labels versehen, auf später legen, als Spam markieren, löschen und, im Papierkorb oder im Spam, in den
Posteingang zurückholen und, wenn du unter **Einstellungen → E-Mail → Entsorgen → Knöpfe zum sofortigen
Löschen** eingeschaltet hast, endgültig löschen. Die Einträge sind die, die auch die eigenen Knöpfe der
Zeile anbieten, an denselben Stellen, und jeder tut, was sein Knopf tut. **Verschieben**, **Labels** und
**Später** öffnen ihre Auswahl über dem Blatt, wie in der Leiste. Das Blatt schließt sich bei einem
Tippen auf die abgedunkelte Seite, mit Escape oder nach einer Aktion; die Seite dahinter reagiert,
solange es offen ist, nicht. Eine Aktualisierung im Hintergrund lässt es in Ruhe, und eine Aktualisierung,
die die Zeile aus der Liste nimmt, schließt es. Wo der Browser vibrieren kann (Android Chrome), gibt es
beim Öffnen einen kurzen Impuls; iOS kennt für Webseiten keine Vibration, dort gibt es keinen. Bei
reduzierter Bewegung erscheint das Blatt ohne Gleiten. Ohne Berührung erledigen die Knöpfe der Zeile und
die Leiste dieselbe Arbeit.
### Tastenkürzel
Die einzelnen Tasten, die Gmail benutzt, funktionieren auch hier. Mit **?** bekommst du überall die
@@ -778,6 +820,9 @@ holt die Konversation zurück und nimmt einen Filter wieder weg, den derselbe Kl
an: Eine Auswahl hat nicht den einen Absender. Für Mail, die schon im Spam oder im Papierkorb
liegt, wird der Knopf nicht gezeigt.
Eine geöffnete Konversation im Spam hat stattdessen einen Knopf **Kein Spam** in ihrer
Werkzeugleiste. Er verschiebt sie zurück in den Posteingang, wie **Verschieben nach → Posteingang**.
Mail in den Spam zu verschieben sagt plMail nichts über die nächste Nachricht. plMail hat keinen
eigenen Spamfilter, der lernt; ob dein Anbieter das Verschieben als Signal wertet, entscheidet
der Anbieter. Ein Filter ist der Weg, es hier dauerhaft zu machen.
@@ -882,6 +927,10 @@ nur für diese Mail und **Bilder von … immer anzeigen**, das sich den Absender
Unter der Mail eines vertrauten Absenders steht das dann, mit **Nicht mehr anzeigen**, um es
zurückzunehmen. Mail, die du selbst gesendet hast, zeigt ihre Bilder ohne Nachfrage.
Angezeigte Bilder kommen immer über plMail, und nur über eine verschlüsselte Verbindung: Ein Bild,
das die Mail mit `http://` verlinkt, wird mit `https://` geholt. Der seltene Server, der so nicht
antwortet, hinterlässt ein leeres Feld.
**Einstellungen → Mail → Bilder**, **Externe Bilder immer anzeigen**, schaltet die Nachfrage für
alle Absender ab. Es ist aus, bis du es einschaltest. Absender können dann sehen, dass du ihre Mail
geöffnet hast, aber nicht deine Adresse: Die Bilder holt plMail und reicht sie an dich weiter, nie
@@ -1372,9 +1421,33 @@ Code noch geht — das kann nur die Mail, aus der er stammt.
**Ziehen zum Aktualisieren gibt es nur mit dem Finger, und nur ganz oben in der Liste.** Maus,
Trackpad und jedes Fenster ab der Tablet-Breite ziehen nie. Ein Ziehen beginnt nur, wenn die Liste
ganz oben steht, und nicht bei offener Seitenleiste; von weiter unten scrollt dieselbe Bewegung nur.
ganz oben steht, und nicht bei offener Seitenleiste, offener Zeile oder offenem Aktionsblatt; von weiter
unten scrollt dieselbe Bewegung nur.
Reduzierte Bewegung nimmt dem Hinweis das Gleiten und sonst nichts, der Auslösepunkt bleibt gleich.
**Ein Wisch ist eine Fingergeste auf dem Telefon, und seine Richtungen sind echte Richtungen.** Eine
Maus wischt nie, in keiner Breite, und ab der Tablet-Breite wischt nichts. Rechts heißt zum rechten
Bildschirmrand, unabhängig von der Sprache, denn eine Sprache von rechts nach links wird nicht
mitgeliefert. Wer eine ergänzt, muss entscheiden, wie sich die beiden Seiten spiegeln.
**Eine Aktualisierung lässt eine Zeile, die du wischst oder offen hast, wo sie ist.** Eine solche Zeile
behält ihren Zustand, während eine Synchronisierung oder eine Aktualisierung die Liste neu zeichnet.
Die Änderung an dieser Zeile kommt, wenn du sie schließt. Eine Zeile, die nur gedrückt wird, oder die
ein langes Drücken in das Aktionsblatt geöffnet hat, wird wie gewohnt neu gezeichnet. Eine
Aktualisierung, die die Zeile entfernt, nimmt sie von der Liste und schließt das Blatt mit ihr.
**Löschen ist kein Wisch.** Ein voller Zug hat kein Tippen davor, und eine Zeile im Papierkorb hat
kein eigenes Rückgängig. Das Löschen bleibt daher bei den Knöpfen der Zeile und im Papierkorb, wo
zuerst getippt werden muss. Auch unter **Einstellungen → E-Mail → Wischgesten** ist Löschen keine
Wahl: Ein dafür gespeicherter Wert gilt als Standard der Richtung, sodass auch ein wiederhergestelltes
Backup es nicht zurückbringt.
**Ein längeres Drücken öffnet das Blatt, und Zeilen werden auf dem Telefon nicht gezogen.** Unterhalb des
Tablet-Breakpunkts ist eine Unterhaltung kein natives Ziehen: Ziehen per HTML5 hat mit dem Finger nie
funktioniert, und das Abschalten verhindert, dass iOS eine Zeile während der halben Sekunde anhebt. Mit der
Maus lässt sich eine Zeile wie bisher ziehen. Das Blatt bietet Löschen an einer Zeile im Posteingang an, wie
es der eigene Knopf der Zeile tut, und hat kein Rückgängig, so wie dieser Knopf.
---
+2 -1
@@ -1,4 +1,4 @@
<!-- translated-from: install/troubleshooting.md sha1:ad52dfe87905d09cdd1f0545fabb06f413d402fa -->
<!-- translated-from: install/troubleshooting.md sha1:81f5c6d505546c5bab2723acf94c75233a8d970e -->
# Fehlersuche
Was `/healthz` bedeutet, wie du eine hängende von einer leeren Warteschlange unterscheidest, wo die
@@ -111,6 +111,7 @@ von ihnen ausgelöst.**
| jede Minute | `app:calendar:alerts` |
| `*/15` | `app:mail:sync` |
| `7-59/15` | `app:calendar:sync --stale` |
| `11-59/15` | `app:contacts:sync --stale` |
| stündlich um :20 | `app:calendar:push` |
| täglich 03:50 | `app:calendar:materialise` |
| täglich 04:00 | `app:push:renew --repair` |
+2 -1
@@ -1,4 +1,4 @@
<!-- translated-from: internals/architecture.md sha1:776308c1fc128a2474116af3c889c58aa5c5e49a -->
<!-- translated-from: internals/architecture.md sha1:23028b00c52d254c70297c87bd36f5d6f1cffad5 -->
# Architektur
Die Schichten, was wo liegt, und die Regeln, die das so halten. Diese Seite beschreibt die
@@ -230,6 +230,7 @@ war, in dem das Projekt vorher steckte: Logs und verwaiste Blobs wuchsen ohne Gr
|---|---|---|
| `*/15 * * * *` | `app:mail:sync` | Weder Gmail-Push noch Graph-Abonnements garantieren Zustellung, und IDLE-Verbindungen brechen ab; Polling ist die Rückfallebene |
| `7-59/15 * * * *` | `app:calendar:sync --stale` | Der Mechanismus für CalDAV und ICS-Feeds, die Rückfallebene für Google und Graph. Gegen die Viertelstunde versetzt, damit er sich nicht auf den Mail-Durchlauf stapelt — beide teilen sich einen Worker |
| `11-59/15 * * * *` | `app:contacts:sync --stale` | CardDAV-Adressbücher haben überhaupt keinen Push, also kommen Änderungen vom Handy nur so an. Gegen beide Durchläufe oben versetzt, aus demselben Grund des geteilten Workers |
| `20 * * * *` | `app:calendar:push` | Stündlich, nicht weil so schnell etwas abliefe, sondern weil die Registrierung aus *Deployment*-Gründen scheitert, die mit dem Klick, der den Kalender verbunden hat, nichts zu tun haben |
| `* * * * *` | `app:mail:wake-snoozed` | Eine Minute ist die Einheit, in der Menschen eine Weckzeit wählen |
| `* * * * *` | `app:calendar:alerts` | Dasselbe Argument, und zusätzlich ist das Intervall die Schranke dafür, wie spät eine Erinnerung kommen kann |
+26 -1
@@ -1,4 +1,4 @@
<!-- translated-from: internals/mail-ingest.md sha1:b6f9af84349aeff829bc8e6e82f7108d258f7ab6 -->
<!-- translated-from: internals/mail-ingest.md sha1:a58cbc774876056b0dd1036a91fed0d4d107f5a5 -->
# Mail-Ingest
Vom Anbieter in die Datenbank: wie aus Bytes eine Zeile `Message` wird, was mit ihr geschieht,
@@ -522,6 +522,31 @@ tatsächlich verloren. Das ist ein bewusster Handel: Eine Konversation, die in d
zurückkommt, in dem du sie verlassen hast, ist eine, an der du bereits gelernt hast
vorbeizuscrollen.
### „Im Papierkorb“ ist gespeichert, und die Datenbank pflegt es
Eine Konversation ist im Papierkorb, wenn sie Trash trägt und keine ihrer Nachrichten außerhalb
von Trash liegt (#268). Jede Liste, jedes Badge und die Suche lassen eine solche Konversation
weg. Sie lesen dafür `message_thread.binned`, statt die Regel aus den Nachrichten abzuleiten; bei
einem Postfach mit 67k Konversationen kostete das Ableiten etwa 170 ms pro Abfrage.
Die Spalte pflegen Trigger, nicht PHP (`Version20261022160000`). Die Schreibvorgänge, die die
Antwort ändern, sind verstreut: Labels über das ORM, rohes SQL, das Konversationen neu aufbaut
oder ein Label entfernt, `ON DELETE CASCADE`, eine Nachricht, die in eine andere Konversation
wandert, eine Antwort, die in einer Konversation im Papierkorb ankommt. Ein Flag, das PHP setzt,
wäre nur auf den Wegen richtig, an die jemand gedacht hat.
- `message_label` und `thread_label` feuern einmal pro Anweisung und berechnen nur die
Konversationen neu, deren Trash-Zeilen sich geändert haben. Ablegen, Lesen und Labeln kosten
nichts.
- `message` feuert pro Zeile: beim Einfügen nur, wenn die Konversation im Papierkorb ist (eine
neue Nachricht kann sie dort nur herausholen), beim Löschen und bei geändertem `thread_id` für
die Konversation, die die Nachricht verlassen hat.
- Jede Neuberechnung leitet die Antwort von Grund auf ab (`plmail_refresh_thread_binned()`),
deshalb spielt die Reihenfolge der Anweisungen in einem Flush keine Rolle.
Die Entity bildet die Spalte nur lesend ab. Im Speicher kann sie einer noch nicht geflushten
Änderung hinterherhinken, deshalb sollte PHP nicht darauf verzweigen; die Abfragen lesen sie.
## Das Änderungsprotokoll, das JMAP liest
Jede Mail-Änderung schreibt eine Zeile nach `jmap_change_log` (`App\Jmap\State\ChangeLog`). Der
+41 -4
@@ -1,11 +1,11 @@
<!-- translated-from: providers/caldav.md sha1:ccfc56f41acd4b8b77775fff1716870993c07d8e -->
<!-- translated-from: providers/caldav.md sha1:a835199379cc0465712e3e763bf86d73bdc7a274 -->
# CalDAV
CalDAV ist das Standardprotokoll für Kalender auf einem Server, und plMail spricht es mit jedem
Server, der RFC 4791 umsetzt: Nextcloud, Radicale, Baïkal, Fastmail, iCloud, eine Synology-Kiste im
Schrank. Es gibt nirgends etwas zu registrieren und keinen Administrationsschritt — eine Person
verbindet einen Server aus den eigenen Einstellungen heraus, mit einer Adresse, einem Benutzernamen
Schrank. Es gibt nirgends etwas zu registrieren, und eine Administratorin muss nichts einschalten — eine
Person verbindet einen Server aus den eigenen Einstellungen heraus, mit einer Adresse, einem Benutzernamen
und einem Passwort.
**Gmail- und Outlook-Kalender kommen nicht auf diesem Weg herein.** Sie kommen mit dem Mail-Konto,
@@ -18,6 +18,42 @@ Was nach dem Verbinden eines Servers passiert — anhaken, welche Kalender gespi
Zwei-Wege-Synchronisation, schreibgeschützte Kalender —, steht in
[Verbundene Kalender](De-Features-Calendar-Sync).
## Nextcloud: keine zweite Verbindung
Eine Nextcloud, die unter **Einstellungen → Integrationen** für Dateien verbunden ist, bringt auch
ihre Kalender mit. Nextcloud stellt CalDAV unter demselben `/remote.php/dav` mit demselben
App-Passwort bereit, also steht diese Verbindung unter **Einstellungen → Kalender**, und ihre
Kalender werden von dort aus gefunden. Nichts auf dieser Seite ist dafür nötig.
- **Die Adresse** ist die der Nextcloud-Verbindung. Eine Adresse, die eine Administratorin oder ein
Administrator unter **Admin → Integrationen** festgelegt hat, gilt also auch für Kalender,
samt ihrer Ausnahme von der Regel für private Netze weiter unten.
- **Die Suche** beginnt bei `/remote.php/dav` unter dieser Adresse, auch bei Installationen in einem
Unterordner, und weicht erst auf `.well-known` aus, wenn dort nichts antwortet.
- **Trennen** der Nextcloud unter Einstellungen → Integrationen entfernt auch ihre Kalender. Wie bei
jeder Kalenderverbindung kommen Termine, die aus deinen E-Mails statt vom Server stammen, vorher in
deinen Standardkalender.
- **Eine eigene CalDAV-Verbindung**, die du früher zur selben Nextcloud angelegt hast, funktioniert
weiter. Ihre Kalender erscheinen in der Liste der Nextcloud-Verbindung als *Wird bereits über …
gespiegelt*, derselbe Kalender wird also nie doppelt gespiegelt.
Die allgemeine CalDAV-Verbindung unten bleibt für jeden anderen Server.
## Ein Server für alle
Unter **Admin → Integrationen**, im Abschnitt **Kalender**, lässt sich ein CalDAV-Server für alle
festlegen, und der Einrichtungsassistent stellt dieselbe Frage. Ist eine Adresse festgelegt, findet
jede CalDAV-Verbindung ihre Kalender auf diesem Server statt unter der Adresse, die ihre Person
eingetragen hat, und dieser Host ist von der Regel für private Netze
[unten](#adressen-die-plmail-ablehnt) ausgenommen.
Geändert oder geleert wird die Adresse an derselben Stelle, und die Änderung greift, wenn auf einer
Verbindung das nächste Mal Kalender gesucht werden. Bereits gespiegelte Kalender behalten die
Adresse, unter der sie gefunden wurden; nach einem Serverumzug also die Kalender neu suchen. Wer eine
festgelegte Adresse in einem privaten Netz leert, nimmt ihr auch die Ausnahme: Kalender von diesem
Host synchronisieren dann nicht mehr und melden *„Server address must not point at a private
network“*, bis der Host in `INTEGRATIONS_ALLOWED_HOSTS` steht.
## Einen Server verbinden
**Einstellungen → Kalender → CalDAV-Server verbinden.** Vier Felder, in der Reihenfolge, in der die
@@ -108,7 +144,8 @@ Serveradresse:
es landete überall dort im Log, wo die URL landet, und würde stillschweigend die Zugangsdaten der
Verbindung überschreiben.
- Alles, was auf Loopback, Link-local oder einen privaten Bereich auflöst, wird abgelehnt, sofern der
Host nicht in `INTEGRATIONS_ALLOWED_HOSTS` steht.
Host nicht in `INTEGRATIONS_ALLOWED_HOSTS` steht oder der Host einer Serveradresse ist, die unter
**Admin → Integrationen** festgelegt wurde.
Die letzte Regel ist die, der Selbsthostende begegnen. Ein Nextcloud in deinem LAN unter
`192.168.1.10` oder `nextcloud.lan` wird abgelehnt, bis du es erlaubst, und ein Nextcloud ohne
+155
@@ -0,0 +1,155 @@
<!-- translated-from: providers/carddav.md sha1:bc3ce68500077b393156115361373684803ffdbc -->
# CardDAV
CardDAV ist das Standardprotokoll für Adressbücher auf einem Server, und plMail spricht es mit jedem
Server, der RFC 6352 umsetzt: Nextcloud, Radicale, Baïkal, Fastmail, iCloud. Meist ist es derselbe
Server wie bei [CalDAV](De-Providers-Caldav), mit demselben App-Passwort. Du verbindest ihn in deinen eigenen
Einstellungen; ein Schritt für den Administrator ist nicht nötig.
Die Kontakte eines gespiegelten Adressbuchs kommen zu deinen [Kontakten](De-Features-Contacts),
neben denen, die nur in plMail liegen. Dort listest, durchsuchst, bearbeitest, führst zusammen und
löschst du sie wie jeden anderen, und jede Änderung dort wird an den Server zurückgeschrieben.
Außerdem werden sie dir vorgeschlagen, wenn du eine Nachricht adressierst.
## Einen Server verbinden
**Einstellungen → Kontakte → CardDAV-Server verbinden.** Dasselbe Formular wie bei den Kalendern,
mit denselben vier Feldern in derselben Reihenfolge — Serveradresse, Name, Benutzername,
App-Passwort — und derselben bewusst ausgeschalteten Option, das gespeicherte Passwort eines
E-Mail-Kontos wiederzuverwenden. Was jedes Feld erwartet, steht unter
[Einen Server verbinden](De-Providers-Caldav#einen-server-verbinden) auf der CalDAV-Seite; alles dort gilt
auch hier.
**Nextcloud braucht keine CardDAV-Verbindung.** Eine Nextcloud, die unter **Einstellungen →
Integrationen** für Dateien verbunden ist, steht auch unter Einstellungen → Kontakte, mit einer
eigenen Schaltfläche **Adressbücher wählen**, mit demselben App-Passwort. Statt einer
Schaltfläche zum Trennen steht dort *Unter Integrationen eingerichtet*, weil die Verbindung auch
deine Dateien und Kalender hält; trennst du sie dort, gehen ihre Adressbücher mit, und die
Rückfrage sagt, wie viele. Ihre Adressbücher werden unter `/remote.php/dav` gesucht, wie
[ihre Kalender](De-Providers-Caldav#nextcloud-keine-zweite-verbindung). Eine CardDAV-Verbindung, die du von
Hand auf eine Nextcloud gerichtet hast, funktioniert weiter, und deren Kontakte-App zeigt unter
**Kontakte → Einstellungen** eine Adresse pro Buch, falls du nur auf ein einzelnes Buch zeigen
willst. Ein Adressbuch, das schon über eine der beiden gespiegelt wird, steht in der Liste der
anderen als *Wird bereits über … gespiegelt* und lässt sich dort nicht ankreuzen, damit seine
Kontakte nicht doppelt erscheinen.
Speichern verbindet sofort und listet dann die Adressbücher, die der Server anbietet, mit
Kästchen zum Anhaken. Angehakte Bücher werden gespiegelt; ihre erste Synchronisierung wird sofort
eingereiht, sodass die Kontakte innerhalb einer Minute erscheinen und nicht erst beim nächsten
Durchlauf.
**Für jeden anderen Server ist es eine eigene Verbindung, getrennt von CalDAV**, auch auf
demselben Server. Manche Server bieten nur eines von beiden, und manche Menschen haben Kontakte und
Kalender an verschiedenen Orten. Ein Radicale oder Baïkal für beides zu verbinden heißt, das
App-Passwort zweimal einzugeben.
## Die Suche
Genau die CalDAV-Suche mit den Namen der Adressbücher: zuerst die eingefügte Adresse, dann
`/.well-known/carddav`, dann das `addressbook-home-set` des Principals, dann wird das Home
aufgelistet. Eine eingefügte Kalender-URL wird nicht für ein Adressbuch gehalten, und die Kalender
desselben Servers tauchen nie in der Liste der Adressbücher auf. Siehe
[Die Suche, und der `.well-known`-Tanz](De-Providers-Caldav#die-suche-und-der-well-known-tanz).
Es gelten dieselben [abgelehnten Adressen](De-Providers-Caldav#adressen-die-plmail-ablehnt): Ein Server in
deinem LAN braucht seinen Host in `INTEGRATIONS_ALLOWED_HOSTS`, und reines `http://` braucht
`INTEGRATIONS_ALLOW_HTTP`.
## Synchronisation
**Nur Polling.** CardDAV hat keinen Push, also liest `app:contacts:sync --stale` gespiegelte Bücher
alle fünfzehn Minuten (um :11, :26, :41 und :56, versetzt gegen die Mail- und Kalender-Durchläufe,
die sich den Worker teilen). Eine Änderung vom Handy erscheint hier innerhalb einer Viertelstunde;
unter **Einstellungen → Kontakte** gibt es pro Buch einen Knopf zum sofortigen Synchronisieren.
Änderungen werden gelesen wie bei Kalendern: `sync-collection` (RFC 6578), wo der Server es
anbietet, sonst `getctag` plus eine vollständige Liste, wenn sich das ctag bewegt. Nur eine
vollständige Liste löscht lokale Karten, die sie nicht erwähnt; ein Delta tut das nie.
**Was du hier änderst, wird sofort gespeichert und danach gesendet.** Der Kontakt wird als
ausstehend markiert (die Kontaktliste zeigt eine kleine Uhr an ihm), und ein paar Sekunden später
schickt ihn der Worker über die Warteschlange `export`, zusammen mit der Mail, die plMail verlässt.
Beim Senden wird der Server nicht gelesen: Änderungen von anderswo kommen mit der nächsten
Synchronisierung. Ist der Server nicht erreichbar, bleibt die Änderung ausstehend, und diese
Synchronisierung sendet sie zuerst. Jede Synchronisierung sendet ausstehende Änderungen, *bevor* sie
den Server liest, damit die ältere Kopie des Servers eine Änderung nicht auf dem Weg hinaus
überschreibt.
**Jeder Schreibzugriff ist bedingt.** Ein neuer Kontakt wird mit `If-None-Match: *` unter einem aus
seiner UID abgeleiteten Namen per `PUT` angelegt; eine Änderung oder Löschung trägt `If-Match` mit
dem zuletzt gesehenen ETag. Hat sich die Karte zwischendurch auf dem Server geändert, gewinnt die
Version des Servers: Das Adressbuch wird neu gelesen, und die hier gemachte Änderung wird verworfen,
mit einer Warnung im Log, die den Kontakt nennt. Zwei Versionen einer vCard Feld für Feld
zusammenzuführen ist eine eigene Funktion, und wer sie falsch macht, beschädigt ein Adressbuch.
Eine Karte, die der Server rundweg ablehnt (`403`, meist aus einem Adressbuch, das nicht gesagt hat,
dass es schreibgeschützt ist), wird genauso behandelt: Die Kopie des Servers bleibt, die abgelehnte
Änderung landet im Log, und eine einzelne Karte kann den Rest des Adressbuchs nicht am
Synchronisieren hindern. Jeder andere Fehler — der Server nicht erreichbar, sein Speicher voll oder
das Passwort abgelehnt — lässt die Änderung ausstehend, und sie wird bei einer späteren
Synchronisierung erneut gesendet.
**Was plMail nicht anzeigt, rührt es nicht an.** Ein Kontakt hat hier jedes Feld, das unter
[Kontakte](De-Features-Contacts#ein-kontakt) steht, und eine Karte kann mehr tragen, etwa
Eigenschaften, mit denen ein Handy sie an eine App bindet. All das bleibt erhalten. plMail
behält die vCard, die der Server geschickt hat, und wendet eine Änderung auf sie an, wobei nur die
geänderten Eigenschaften angefasst werden. Es baut die Karte nicht aus seinen eigenen Feldern neu.
Eine Adresse oder Nummer, die auf der Karte bleibt, behält ihren Typ (Arbeit, privat), ihre
Gruppierung und die Schreibweise des Servers.
Die Felder werden in jeder Schreibweise gelesen, die gängige Programme schreiben — Apples gruppierte
`X-ABDATE`, `X-ABRELATEDNAMES` und `X-ABLabel`, vCard 4s `ANNIVERSARY` und `RELATED`, `IMPP` und die
ältere `X-JABBER`-Familie, Apples 1604 für einen Geburtstag ohne Jahr —, und eine hier geänderte
Liste wird so geschrieben, wie die meisten sie lesen. Das Foto geht in beide Richtungen: Ein hier
hochgeladenes oder entferntes ersetzt das der Karte, und eines, das hier nicht geändert wurde, bleibt
genau so auf der Karte.
**Einen Kontakt zu löschen löscht seine Karte.** Ein Kontakt, den du hier in den Papierkorb legst,
wird bei der nächsten Synchronisierung auf dem Server gelöscht und bleibt im Papierkorb als einer,
der nur in plMail liegt. Führst du Duplikate zusammen, wird die Karte des behaltenen Kontakts
aktualisiert, und die Karten der zusammengeführten werden gelöscht.
Schreibgeschützte Bücher, etwa ein geteiltes Buch oder das globale Adressbuch eines Servers, werden
gespiegelt und vorgeschlagen, lassen sich hier aber nicht bearbeiten, löschen oder zusammenführen.
Ob ein Buch Schreibzugriffe annimmt, wird beim Server erfragt (`current-user-privilege-set`), nicht
angenommen.
## Vorschläge beim Schreiben
Der Editor und JMAP `Contact/autocomplete` lesen eine gereihte Liste, in dieser Reihenfolge:
1. wie oft die Adresse in deinen E-Mails vorkam;
2. dann, ob sie in deinen Kontakten steht (nur in plMail oder in einem synchronisierten Buch);
3. dann, wie kürzlich sie gesehen wurde, dann der Name.
Eine Adresse, der du oft schreibst, kommt also weiterhin zuerst, und ein Kontakt, dem du nie
geschrieben hast, kommt vor einer Adresse, die du einmal in einen Entwurf getippt hast. Eine
Adresse, die an beiden Stellen steht, wird einmal vorgeschlagen, unter dem Namen aus deinen
Kontakten. Tippst du einen Teil eines Namens, bekommst du jede Adresse dieses Kontakts angeboten;
tippst du einen Teil einer Adresse, nur diese Adresse.
## Fallstricke
**Kalender- und Kontakte-Verbindung sind getrennt.** Eine unter Kalender verbundene Nextcloud
bringt ihre Kontakte nicht mit; verbinde sie unter Kontakte noch einmal.
**Eine hier gemachte Änderung kann an eine anderswo gemachte verloren gehen.** Wurde derselbe
Kontakt zwischen zwei Synchronisierungen auf einem Handy geändert, gewinnt die Version des Handys,
und die Änderung hier wird verworfen (und geloggt). Das Fenster ist die Zeit zwischen dem Speichern
und dem nächsten Lauf des Workers, meist Sekunden.
**Ein Adressbuch abzuwählen oder den Server zu trennen verwirft noch nicht gesendete Änderungen.**
Die Kopien hier verschwinden, mit jedem Foto, das du hier zu ihnen hochgeladen hast; der Server
behält seine eigenen. Zeigt ein Kontakt die Uhr, warte, bis sie verschwindet.
**Ein Server im LAN wird standardmäßig abgelehnt**, wie bei CalDAV. Trag den Host in
`INTEGRATIONS_ALLOWED_HOSTS` ein.
**Einen Kontakt zwischen Adressbüchern zu verschieben wird nicht angeboten.** Auf dem Server ist
das eine Löschung in einer Sammlung und eine Neuanlage in einer anderen; mach es in der Oberfläche
des Servers selbst.
---
*This page is generated from [`docs/de/providers/carddav.md`](https://github.com/karatektus/pl_mail/blob/main/docs/de/providers/carddav.md). Edit it there — changes made here are overwritten on the next push to `main`.*
+30 -3
@@ -1,4 +1,4 @@
<!-- translated-from: providers/google.md sha1:ab22097bf3000ba96411f571d10ade5dd0362ae3 -->
<!-- translated-from: providers/google.md sha1:b48c1f67e02a6658bb083369e5cd0a089c282cb2 -->
# Google
@@ -33,6 +33,11 @@ Alles unter **Einstellungen**, was dadurch möglich wird, beschreiben
3. Aktiviere im selben Projekt die **Google Calendar API**, wenn du Kalender willst. Das ist der
Schritt, den der Admin-Bildschirm meint, wenn er sagt, auf der plMail-Seite sei nichts
einzurichten — der Kalenderzugriff wird vollständig in dieser Konsole erworben.
4. Aktiviere außerdem die **Google People API**, wenn du Kontakte willst. Es ist derselbe Schritt für
das Adressbuch: Ohne ihn funktionieren Mail und Kalender, und das Google-Adressbuch jedes
Benutzers unter **Einstellungen → Kontakte** sagt, dass die Administration die People API
aktivieren muss. Sobald sie aktiviert ist, synchronisieren die Kontakte von selbst; niemand muss
sich neu anmelden.
Notier dir gleich deine **project ID** (Projekt-ID). Sie ist kleingeschrieben und kann von dem
Anzeigenamen abweichen, den du eingetippt hast; das Pub/Sub-Topic weiter unten wird mit der ID
@@ -86,12 +91,13 @@ am Zustimmungsbildschirm scheitert.
## Die Scopes, und wofür jeder da ist
plMail fragt bei der Zustimmung diese vier ab, für jedes Gmail-Konto:
plMail fragt bei der Zustimmung diese fünf ab, für jedes Gmail-Konto:
| Scope | Wofür er da ist |
|---|---|
| `https://mail.google.com/` | Das Postfach selbst: Nachrichten lesen, senden, Labels vergeben und entfernen, verschieben und löschen. Das ist voller Postfachzugriff, und genau das ist ein Mail-Client. |
| `https://www.googleapis.com/auth/calendar` | Kalender auf demselben Konto, lesend **und** schreibend. |
| `https://www.googleapis.com/auth/contacts` | Kontakte auf demselben Konto, lesend **und** schreibend, über die People API. |
| `openid` | Identifiziert das Konto, zu dem das Token gehört. |
| `email` | Liefert die Adresse, nach der plMail das Konto benennt. |
@@ -123,6 +129,27 @@ abgelehnt wird, und sagt es in den Kalendereinstellungen — *„Auf Googles Zus
der Kalenderzugriff abwählen, während E-Mail erlaubt wird. Verbinde das Konto neu und lass das
Kalenderhäkchen gesetzt."*
### Kontakte hängen ebenfalls an derselben Anmeldung
Der Kontakte-Scope reist auf derselben Freigabe, aus denselben Gründen. Ein mit plMail verbundenes
Google-Konto bringt seine Kontakte mit: Sein Adressbuch erscheint unter **Kontakte** neben plMails
eigenem, Googles Kontaktgruppen werden zu Schlagwörtern, und eine Änderung in plMail wird
zurückgeschrieben — Namen, phonetische Namen, Organisation, Geburtstag und weitere Daten,
Beziehungen, Websites, Chat- und SIP-Adressen, eigene Felder und das Foto, dazu Anschriften und
Nummern — und von Google auf die Handys, die damit synchronisieren. Gesendet werden nur die Felder,
die plMail zeigt, also bleibt, was Google darüber hinaus hat (Berufe, Interessen, das Kürzel einer
Organisation), wie es ist. Siehe [Kontakte](De-Features-Contacts).
Ein Konto, das verbunden wurde, **bevor** plMail nach Kontakten gefragt hat, hält eine Freigabe ohne
den Scope. Daran ist nichts kaputt: Mail und Kalender laufen weiter, und **Einstellungen →
Zustand der Konten** zeigt einen Hinweis, *… erneut verbinden, um seine Kontakte zu synchronisieren*, dessen
Knopf dich noch einmal anmeldet und die Berechtigung ergänzt. Das Adressbuch erscheint, sobald das
erledigt ist.
Google stuft den Kontakte-Scope als **sensibel** ein. Das ist eine leichtere Klasse als die
*eingeschränkte* des Mail-Scopes, mit der eine Installation ohnehin leben muss (siehe unten), und
ändert an der Überprüfung nichts.
### Was die Überprüfung „eingeschränkter Scope“ hier bedeutet
`https://mail.google.com/` gewährt Zugriff auf das gesamte Postfach, und Google behandelt Scopes
@@ -296,7 +323,7 @@ kommen* auftaucht, ohne Kalender daran. Verbinde es neu und lass das Häkchen ge
**Die Calendar API zu aktivieren ist nicht dasselbe wie den Kalender-Scope hinzuzufügen**, und das
eine ohne das andere scheitert auf zwei verschiedene Arten: der Scope ohne die API ist ein 403 bei
jedem Kalenderaufruf, und die API ohne den Scope ist ein Token, dem der Kalenderzugriff nie erteilt
wurde.
wurde. Dasselbe gilt für die People API und den Kontakte-Scope.
**Die Domainbestätigung gilt pro Cloud-Projekt, nicht pro Google-Konto.** Die Domain in der Search
Console zu verifizieren ist nur die erste Hälfte; sie muss zusätzlich unter **Domain verification**
+46 -5
@@ -1,4 +1,4 @@
<!-- translated-from: providers/microsoft.md sha1:593c30a706038d59877536d7f264562631d8d6fc -->
<!-- translated-from: providers/microsoft.md sha1:3e529992d3beed2d19d98f73d09643bf23bb54af -->
# Microsoft
@@ -66,6 +66,7 @@ stimmt dem Satz als Ganzem zu; anders als bei Google gibt es keine Teilerteilung
| `Mail.Send` | Senden. In Graphs Modell vom Obigen getrennt, muss also getrennt hinzugefügt werden. |
| `MailboxSettings.ReadWrite` | Master-Kategorien, unter `/me/outlook/masterCategories`. Diese sind **nicht** von `Mail.ReadWrite` abgedeckt: Sie liegen unter der Outlook-Ressource für Benutzereinstellungen und liefern ohne diese Berechtigung `ErrorAccessDenied`. ReadWrite statt Read, weil plMail Kategoriedefinitionen anlegt, wenn ein Label gespiegelt wird. |
| `Calendars.ReadWrite` | Kalender auf demselben Konto, lesend und schreibend. |
| `Contacts.ReadWrite` | Outlook-Kontakte auf demselben Konto, lesend und schreibend: Jeder Kontaktordner ist ein Adressbuch. |
Zwei davon lohnen ein genaueres Verständnis, bevor du dich entscheidest, eine wegzulassen.
@@ -93,6 +94,32 @@ Erscheinen für eine Person keine Kalender, sagt plMail unter **Einstellungen
möglichen Ursachen es ist: Die Berechtigung kann in der App-Registrierung fehlen, oder das Konto hat
schlicht keine Kalender.
### Kontakte hängen ebenfalls an derselben Anmeldung
`Contacts.ReadWrite` reist auf derselben Freigabe. Ein mit plMail verbundenes Microsoft-Konto bringt
seine Outlook-Kontakte mit: Jeder Kontaktordner erscheint als Adressbuch unter **Kontakte**,
Outlooks Kategorien werden zu Schlagwörtern, und eine Änderung in plMail wird zurückgeschrieben.
Siehe [Kontakte](De-Features-Contacts).
Outlooks Kontakt hat feste Plätze, wo plMail Listen hat: drei E-Mail-Adressen, zwei geschäftliche und
zwei private Nummern und eine Mobilnummer, je eine private, geschäftliche und sonstige Anschrift,
einen Ehepartner (oder Partner), einen Vorgesetzten, eine Assistenz und die Namen der Kinder, eine
Website (die geschäftliche, wenn es eine gibt), drei Chat-Adressen und einen Geburtstag. plMail
füllt diese Plätze, die primären zuerst. Ein Eintrag darüber hinaus — eine vierte E-Mail-Adresse,
ein zweiter Partner — wird nicht gesendet und bleibt nur in plMail, bis sich der Kontakt das nächste
Mal in Outlook ändert; dann kommt Outlooks Fassung zurück. Für weitere Daten (einen Jahrestag), einen
phonetischen zweiten Vornamen und eigene Felder hat Outlook gar keinen Platz; sie bleiben in plMail.
Seine Chat-Adressen tragen keinen Dienst: Eine SIP-Adresse kommt als SIP zurück, jede andere als
„Andere“.
Fotos gehen in beide Richtungen, mit einer Ausnahme: Microsoft Graph kann das Foto eines
Outlook-Kontakts ersetzen, aber nicht entfernen, also bleibt ein in plMail entferntes Foto in
Outlook.
Ein Konto, das verbunden wurde, bevor plMail nach Kontakten gefragt hat, hält ein Token ohne die
Berechtigung. Mail und Kalender laufen weiter, und **Einstellungen → Zustand der Konten** zeigt einen Hinweis,
dessen Knopf dich noch einmal anmeldet und sie ergänzt.
## Der Tenant-Wert
`MICROSOFT_OAUTH_TENANT` — oder das Feld **Tenant** unter **Admin → Integrationen → Mail-Anmeldung** —
@@ -193,10 +220,24 @@ der Value ist brauchbar; er wird einmal angezeigt und lässt sich danach nicht m
ersetzen.
**Eine Berechtigung hinzuzufügen wertet bestehende Tokens nicht auf.** Wer sich verbunden hat, bevor
du `Calendars.ReadWrite` oder `MailboxSettings.ReadWrite` ergänzt hast, hält ein Token, das sie nicht
trägt, und daran ändert kein Abwarten etwas. Das Konto muss getrennt und neu verbunden werden, damit
eine frische Zustimmung ein neues Token ausstellt — `app:graph:diagnose` sagt das ausdrücklich, wenn
es die Lücke findet.
du `Calendars.ReadWrite`, `Contacts.ReadWrite` oder `MailboxSettings.ReadWrite` ergänzt hast, hält
ein Token, das sie nicht trägt, und daran ändert kein Abwarten etwas. Das Konto muss sich neu
anmelden, damit eine frische Zustimmung ein neues Token ausstellt — der Knopf zum erneuten Verbinden
unter **Einstellungen → Zustand der Konten** tut das, ohne das Konto zu entfernen, und `app:graph:diagnose` sagt
es ausdrücklich, wenn es die Lücke findet.
**Eine Organisation, die Apps für ihre Benutzer freigibt, lehnt nach einem Update mit einer neuen
Berechtigung jede Anmeldung ab.** In einem Tenant, in dem Benutzer Apps nicht selbst zustimmen
dürfen, hat ein Administrator plMail einmal freigegeben, für die Berechtigungen, die es damals
verlangt hat. Eine plMail-Version, die eine weitere verlangt (`Contacts.ReadWrite`, seit es die
Kontaktsynchronisierung gibt), wird bei der Anmeldung abgelehnt, auch für Mail, mit `AADSTS65001`
oder `AADSTS90094`, bis der Administrator erneut zustimmt: **Microsoft Entra Admin Center →
Unternehmensanwendungen → plMail → Berechtigungen → Administratorzustimmung erteilen**. Tu das vor
dem Update oder bevor sich jemand neu verbindet. Die Fehlermeldung bei der Anmeldung sagt dem
Benutzer, dass er seinen Administrator fragen soll, **Admin → Integrationen → Microsoft-Mail-Anmeldung**
trägt dieselbe Warnung, und ebenso das Angebot zum erneuten Verbinden unter
**Einstellungen → Kontakte**. Private Microsoft-Konten und Tenants, in denen Benutzer Apps selbst
freigeben, sind nicht betroffen.
**Eine funktionierende Identität bei durchweg scheiternden Mail-Endpunkten ist kein Problem der
Zugangsdaten.** Meistens heißt es, dass für das Microsoft-Konto überhaupt kein Outlook-Postfach
+3 -1
@@ -1,4 +1,4 @@
<!-- translated-from: README.md sha1:0672ad1ba0164a9c746efbc168be740b1de378b1 -->
<!-- translated-from: README.md sha1:2b4822814b48a42b23e2129e94c51d2eac8d6548 -->
# plMail-Dokumentation
@@ -37,6 +37,7 @@ wo der Mechanismus dahinter steht.
| [Erinnerungen](De-Features-Calendar-Alerts) | Erinnerungen setzen, wie sie zugestellt werden, was eine frische Installation braucht, damit sie ankommen |
| [Verbundene Kalender](De-Features-Calendar-Sync) | Google-, Microsoft- und CalDAV-Kalender abonnieren, Abgleich in beide Richtungen, ICS-Import, -Export und Feed-Abonnements, doppelte Termine |
| [Teilen und Buchen](De-Features-Calendar-Sharing) | Freigabelinks und was jeder preisgibt, Buchungsseiten, wie eine Buchung ankommt |
| [Kontakte](De-Features-Contacts) | Das Adressbuch: eigene und synchronisierte Kontakte nebeneinander, Bearbeiten, Duplikate und Zusammenführen, vCard- und CSV-Import und -Export |
| [Dateien und Integrationen](De-Features-Integrations) | Anhängen aus und Speichern in Drive, Photos, OneDrive, Dropbox, Nextcloud, Immich und Paperless-ngx |
| [Sicherheit](De-Features-Security) | Dein Passwort ändern, Zwei-Faktor-Authentifizierung, Wiederherstellungscodes, gemerkte Geräte, App-Passwörter, Sitzungen |
| [Andere Clients](De-Features-Clients) | Einen JMAP-Client verbinden, Passwörter pro App, die PWA und Browser-Benachrichtigungen |
@@ -67,6 +68,7 @@ Jede Seite nennt die genaue Konsole, die genauen Häkchen und die genauen Redire
| [Microsoft](De-Providers-Microsoft) | App-Registrierung in Azure, Redirect-URIs, delegierte Berechtigungen, Wahl des Tenants, Graph-Abonnements |
| [IMAP und SMTP](De-Providers-Imap-Smtp) | Gewöhnliche Postfächer, IDLE, und die Einstellungen, über die sich die Server uneinig sind |
| [CalDAV](De-Providers-Caldav) | Discovery, App-Passwörter, und die Server, die getestet wurden |
| [CardDAV](De-Providers-Carddav) | Adressbücher von Nextcloud und anderen Servern: Vorschläge beim Schreiben, Bearbeiten, und Synchronisation in beide Richtungen |
| [ICS-Feeds](De-Providers-Ics-Feeds) | Einen veröffentlichten Kalender per URL abonnieren, und warum manche Adressen abgelehnt werden |
| [Single Sign-on](De-Providers-Oidc) | Anmeldung bei plMail über Authentik, Keycloak oder einen anderen OpenID-Connect-Anbieter: Einrichtung, Verknüpfung von Konten und automatisches Anlegen |
+49
@@ -249,6 +249,55 @@ One caveat plMail states on the page itself: **Outlook sends as the account's pr
regardless of what you choose here.** That is Microsoft's behaviour, changed in your Microsoft
account settings, not here.
### Gmail collecting mail for another account (Gmailify)
Gmail can fetch the mail of another address into your Gmail inbox. When that other address is
connected here as well, every message reaches plMail twice: once through Gmail and once through the
other account's own IMAP sync. plMail files such mail under the other account and merges the two
copies into one, so a message is listed once. The merged message is still the other account's: its
IMAP folder decides whether it is in the Inbox, the bin or another folder, whatever Gmail did with
its own copy. Labels you give it in Gmail show here, and archiving, starring or labelling it here
is also sent to Gmail.
Under each Gmail account on this page, **File mail for my other accounts under them** turns this
on or off. It is on unless you turn it off. Turn it off once Gmail no longer collects that mail,
typically after a domain has moved from Gmail to an IMAP provider of its own. Otherwise Gmail's old
and late copies keep being matched to the other account. With it off:
- The Gmail account keeps only mail addressed to itself. Mail it has for other addresses is not
stored again.
- The other account's own copies stop following Gmail. They lose the labels Gmail put on them,
and a change you make to them is no longer sent to Gmail. The mail itself stays where its IMAP
folder has it. This runs in the background, so on a mailbox that has collected mail for years it
can take a few minutes after the switch is turned off.
- Mail that Gmail filed under the other account, and that the other account's IMAP server never
had, stays as it is and keeps following Gmail. Gmail is the only place that mail exists.
**Best turned off once Gmail has stopped collecting.** While Gmail still collects the other
address's mail, plMail tells that mail apart by its recipients: a message whose To or Cc names the
other address, and not the Gmail address, is left to the other account, even though Gmail stamps
its own address on it as the place it was delivered. A message Bcc'd to the other address names
nobody, so it looks like the Gmail account's own mail and is kept on both accounts. Mailing lists
and newsletters often arrive that way. Stop the collecting in Gmail (Settings → Accounts → Check
mail from other accounts) and those duplicates stop too.
Turning it back on changes nothing that is already stored. Mail that arrives afterwards is matched
again, and so is an older message once Gmail reports a change to it.
**Mail moved by Gmail before v0.3.9.** Earlier versions moved the other account's copy wherever
Gmail moved its own. Mail trashed or archived in Gmail showed in plMail's bin or archive, although
the IMAP server still had it in its Inbox. To put that mail back where its IMAP folder has it, an
administrator runs:
```bash
php bin/console app:backfill gmailify-placement
```
Run it while the workers are quiet. A move you make in plMail updates the labels first and the IMAP
folder a moment later, and a message caught in between would briefly go back to its old folder.
Mail that those earlier versions removed because Gmail had emptied it from its bin is not brought
back by this. The IMAP sync only fetches mail newer than what it already has.
### What each address does by default
Two settings hang off the alias list rather than off the account, because they are properties of the
+29 -4
@@ -302,8 +302,8 @@ appears only when there is something in it, and names how many.
## Integrations
Two things live here: which file services this installation offers, and the OAuth applications users
sign into their mail with.
Three things live here: which file services this installation offers, the server address for its
calendar services, and the OAuth applications users sign into their mail with.
**Mail sign-in** holds the Google and Microsoft client id and secret, the Microsoft tenant, and for
Gmail the Pub/Sub topic and push verification token. Leaving any of them empty falls back to the
@@ -324,6 +324,23 @@ appear.
- **Server address**, for the self-hosted ones. Leave it empty and each user enters their own; set
it and every user is pinned to that server.
**Calendars** lists CalDAV and calendar feeds below the file services, with the same row and the same
**Configure** form. Both are offered to every user whatever is set here, so the row says **Offered
to everyone** and the form has no switch. Nextcloud is not repeated there: it is configured once
among the file services, and its calendars use that address.
- **CalDAV** has the one setting the setup wizard may already have written: **Server address**. Set
it and every CalDAV connection finds its calendars on that server; clear it and each user's own
address counts again. A change applies the next time calendars are found on a connection.
Calendars already mirrored keep the address they were found at, and clearing an address in a
private range also ends its exemption from the private-network rule, so those calendars stop
syncing until the host is in `INTEGRATIONS_ALLOWED_HOSTS`. Once people have connected, the form
says how many connections a change reaches. See [CalDAV](Providers-Caldav).
- **Calendar feed** has no server to pin, because every feed is its own address, so its row has
nothing to configure. Only an address the setup wizard stored before is shown, with a
**Configure** button to clear it: such an address only lets feeds on its host past the
private-network rule.
**Reuse Gmail credentials** and its Microsoft equivalent copy a client id and secret across
server-side, without ever showing the secret. One Google Cloud project also covers Drive and Photos;
one Entra registration also covers OneDrive. Copying the credential does **not** grant the extra
@@ -581,7 +598,7 @@ on the host itself applies. plMail cannot read that variable, so an empty box is
nothing. Useful after a restart, and useful for finding out what a cold load actually costs on your
hardware before you decide how long to hold it.
### The four features are separate switches
### The five features are separate switches
They have very different costs, and very different consequences when the model is wrong.
@@ -591,6 +608,7 @@ They have very different costs, and very different consequences when the model i
| **Sort mail into tabs** | Fills in a category when no rule recognised the message | On every message that arrives |
| **Help me write** | Offers drafts and rewrites in the composer | Only when you ask |
| **Thread summaries** | Writes a short account of a long conversation | Only when somebody asks for one |
| **Contact help** | Reads a contact out of pasted text, and suggests updates to a contact from the signature of their latest mail | Only when somebody presses **Improve with AI** |
Sorting is the one to be careful with: it runs unattended. It can only ever act as a tie-breaker —
if a header, a Gmail label or the fact that you have written to somebody already decided the
@@ -614,7 +632,7 @@ Two things keep sorting from getting in the way of mail arriving:
Writing help never replaces what you wrote. It appends, so the original is always still there.
Summaries are the most expensive of the four. They use the same writing model, but a whole
Summaries are the most expensive of the five. They use the same writing model, but a whole
conversation is a much larger prompt than a draft: on a 30B model and one GPU, expect around a
minute for the first summary after a quiet spell and around half that once the model is warm. The
GPU is held for the whole of it, so on a shared machine a summary is felt by everybody. Nothing is
@@ -622,6 +640,13 @@ ever summarised on its own — no arriving message, no nightly pass — and a su
written is kept and shown again, so opening the same conversation twice costs nothing the second
time.
Contact help sends text about somebody else: what a person pasted into a new contact, or a saved
contact together with the signature block of the newest mail that contact sent — the part under
`-- ` or under the closing ("Best regards"), never the body of the mail. A mail with neither sends
nothing from that mail. Nothing it suggests is saved until the person ticks it and saves the
contact. Without it, pasting still works: plMail reads the text by rules, and only the button that
asks the model goes away.
### Searching by meaning needs one pass first
Semantic search can only find mail it has indexed, and switching the feature on indexes nothing on
+13 -2
@@ -20,8 +20,19 @@ also see your calendars there, because plMail asks for calendar access on the sa
mailbox access. There is no second sign-in, no second application to register, and no second thing
to configure — the account shows a **Find calendars on…** button and that is the whole of it.
**Connections made for calendars alone.** A **CalDAV server** — Nextcloud, Radicale, Baïkal,
Fastmail, iCloud, a box in a cupboard — or a **calendar address**, which is any published `.ics`.
**Connections made for calendars alone.** A **CalDAV server** — Radicale, Baïkal, Fastmail,
iCloud, a box in a cupboard — or a **calendar address**, which is any published `.ics`.
**Your Nextcloud connection.** If you connected Nextcloud for files under **Settings →
Integrations**, it is listed here too, with its own **Find calendars on…** button. The same app
password reaches your Nextcloud calendars, so there is no address or password to type a second
time. It is marked *Set up in Integrations* instead of offering a disconnect button: the
connection holds your files as well, so it is disconnected there, and that confirmation says how
many calendars go with it.
A calendar already mirrored through another connection — a CalDAV connection you made by hand to
the same Nextcloud, say — is shown as *Already mirrored through …* and cannot be ticked a second
time, so its events are not synced twice.
If the section says *No account or connection here carries calendars yet*, you have IMAP accounts and
nothing else; connect a CalDAV server or subscribe to an address.
+29 -7
@@ -70,12 +70,23 @@ looking at, the view switcher, and a **New event** button. Previous and Next ste
month view, by seven days in the 7-day view, and by a day everywhere else — agenda included, because it
is a rolling list rather than a page.
**On a narrow screen the toolbar tightens rather than scrolling.** Below about 590 pixels the view
switcher shows icons, and the date takes a short form: the month without its year while it is this
year's (**October**), a week as **Oct 20–26**, a day as **Oct 20**. On the narrowest phones **Today**
becomes today's date in a small box.
The full page opens on **7 days**. The docked pane opens on **Agenda** — it is 380 pixels of a shared
row, and a month grid in that width is a lot of empty cells. The pane still offers all four views,
as icons rather than words, because the version that dropped the switcher entirely left the pane
able to show nothing but its agenda.
In month view a cell draws as many of its day's entries as fit and fades the last one where it runs
**In month view an entry is its title first**, on up to two lines, and under it the time it runs
and where: **Quartalsplanung Q4**, then **10:00–11:00 · Raum Elbe**. In a narrower day the end time
and the place go, and the title gets a third line in a smaller size; long words are hyphenated in
the language plMail is set to. An invitation you have not answered yet has a dashed bar, so the
week's open invitations can be seen at a glance.
A cell draws as many of its day's entries as fit and fades the last one where it runs
out, rather than counting them. It used to say **N more**, and the count could not be right: a cell's
height is the window's divided by six and an entry is one line or two depending on whether it has a
time, so the number was always a guess about a box nobody had measured. **The date is a link** — it
@@ -84,12 +95,16 @@ opens that day, which is where the rest of them are.
**Resting the pointer on a day floats that whole day out.** The cell becomes a small panel, wide
enough for the full titles and long enough for every entry. Nothing in it moves as it opens — it
grows right and down from exactly where the list already was — so the entry you were looking at is
still under the pointer.
still under the pointer. On a touch screen, which has no pointer to rest, **the first tap on a day
opens it** and does nothing else; the entries in it open on the next tap.
**Where a cell is too narrow for words it stops using them.** Seven columns of a phone are about
forty pixels each, which is not a width a title or even a time can finish in, so below that an entry
is drawn as a bar in its calendar's colour — how many, and which calendars, at a glance. Tapping the
date opens the day, where they are written out.
**Where a cell is too narrow for words it stops using them, and the day is listed under the
month instead.** Seven columns of a phone are about fifty pixels each, which is not a width a title
or even a time can finish in, so there an entry is drawn as a bar in its calendar's colour — how
many, and which calendars, at a glance — and the grid shrinks to make room for a list of one day
underneath, written out the way the agenda writes it. It starts on today; **tapping any day, bars
included, lists that day instead**. The list has its own **+**, which opens a new event on that day,
and the toolbar's **New event** adds to that day too.
The agenda groups its entries under a heading per day, which stays at the top of the list while that
day's events are on screen. The start and end time — or **All day** — sit in a lane of their own down
@@ -127,6 +142,10 @@ Month and Agenda have no time axis in either shape. A month cell is a couple of
and has no room to say where in the day anything is; an agenda's whole value is that it skips the
empty time, which is exactly the axis a grid would draw.
**The red current-time line moves with the clock**, once a minute, and when the date changes the
view draws itself again — the line, the tint on today and the circle on today's date in the month
all move to the new day — so a calendar left open beside the mail stays true.
**The grid is drawn on your clock.** The hours down the side, where the red current-time line sits,
and the time a new event is proposed at when you double-click empty space all follow the timezone in
**Settings → General**. That is the same zone every other timestamp in the app is read against.
@@ -162,7 +181,10 @@ toolbar and is a single full-width column.
**New event** in the toolbar, the **+** on a day heading, or clicking an existing event, all open the
same editor. It is a modal over whatever page you were on — the calendar page, or a mail view with
the calendar docked beside it — and saving puts you back there rather than dragging you to the
calendar.
calendar. On a phone it is the whole screen, as the compose window is.
A **+** on a day opens the editor on that day at the next full hour. A day that has already gone keeps
its date: a meeting written down after it happened lands on the day it happened, not on today.
The fields are **Title**, **Starts**, **Ends**, **All day**, **Calendars**, **Repeat**, **Location**,
**Description** and **Reminders**, with **Apply to** appearing when the event repeats. An event
+197
@@ -0,0 +1,197 @@
# Contacts
plMail keeps an address book: the people you write to, with their addresses, phone numbers, postal
addresses, birthdays, a photo and notes. It is one list, wherever a contact lives. Some are plMail's own and
exist only here. Others come from an address book on a server, and changes go both ways:
- a **Google** or **Microsoft** mail account brings its contacts with it, on the same sign-in as its
mail and calendars — nothing to connect (see [Google](Providers-Google) and
[Microsoft](Providers-Microsoft));
- your [Nextcloud](Providers-Carddav#connecting-a-server), or any other CardDAV server, is
connected under **Settings → Contacts**.
An account connected before plMail synced contacts needs one more sign-in to allow them: **Settings
→ Contacts** and **Settings → Account health** both offer it.
Open it with the address-book icon beside the calendar button. Like the calendar button it is a
three-way switch: the address book beside the mail, then full width with your mail behind it, then
closed again. **Settings → Contacts → Behaviour** can turn it into a two-way one (**Full width
only** or **Beside mail only**), as for the calendar. On a phone, **Contacts** in the menu opens the
full page.
The docked address book is the whole of it: search, open, edit, add and merge contacts there while
your mail stays where it was. Drag its edge to make it wider or narrower (or use the arrow keys on
the edge; a double-click puts it back). Drag it past its widest and let go, and it goes full width;
past its narrowest, and it closes. plMail remembers the width and the position. The arrows in its
header make it full width, and the × closes it. Opening a message from a contact at full width
brings the mail back beside it.
On a wide monitor (1920 pixels across or more) the address book and the calendar can be docked side
by side. On a smaller screen, such as a laptop's, there is room for one of them beside the mail, so
opening one puts the other away. Below 1024 pixels the icon opens the full page.
## The list
Every contact as a card: photo or initials, name, job and company, and the main email address.
Search finds a contact by any of its names, its company or any of its addresses. Tags (you set them
on a contact) appear as chips above the list and narrow it to one tag.
Once you have a synced address book, a second row of chips chooses where to look: **All books**,
**Not synced** (plMail's own contacts), or one address book. A synced contact carries a small
cloud, or a lock if its address book is read-only. A clock means an edit made here is waiting to be
sent to the server.
## A contact
The contact's page shows its details, its notes, related contacts, and **Reference emails**: every
message to or from the contact that has not been deleted, in any folder. Clicking one opens it in
the mail beside the contact, which stays as it was; on the full page the mail slides in from the
left.
**New contact** opens **quick add**: one box to type or paste anything about the person into — a
name, an email signature, a line like `Mia Hoffmann <mia@example.net>`, an address, or a whole
vCard. It is read as you type, and what it finds appears below as rows: each can be unticked,
retyped, or given another type with one click (a work number that is a home number, a line taken
for the job title that is really the company). A line that could not be placed is asked about
rather than guessed, a greeting above a signature is left out, and an address that is already on
one of your contacts is pointed out. A vCard is read field by field and nothing is guessed. **All
fields** opens the full editor with everything found already filled in.
The **editor** shows the name as one field; its parts (prefix, first, middle and last name,
suffix) fill themselves as you type and open under **Edit parts**. Once you edit a part by hand it
stays, and the page says when the name and its parts no longer match. Email and phone end in a
dashed row — type into it and it becomes a row, typed as home or work from the address, or as a
mobile from the number, until you pick a type yourself. The star marks the preferred one. Every
other kind of detail shows only when the contact has one; the chips under **Add** bring the rest.
Once something has changed, a bar at the bottom says how much and offers **Save** and **Discard**.
**Paste details** in the editor reads pasted text the same way and compares it with the contact
before anything changes: what is already there is greyed out, what is new is ticked, and what
differs from what is saved ("Replaces …", or a different type for the same number) is left for you
to tick. **Apply** puts the ticked details into the form and highlights them; nothing is saved
until you save.
When your administrator has switched on **Contact help** (Admin → AI), both pages have **Improve
with AI**. On quick add it gives the text a second reading — the company the rules could not tell
from a slogan, a fax number written as `Fax -99` — and marks what it changed, with **Undo AI
changes** to go back. In the editor it sends the contact and the signature of the newest mail the
contact sent you, and shows its suggestions, each with its reason, in the same list as pasted
details. You can switch it off for yourself under Settings → Assistant.
A contact holds:
- its name in parts (prefix, first, middle and last name, suffix), a nickname, and how the names are
spoken (**Phonetic spelling**, for names a phone should sort or read out differently);
- company, department, job title and office;
- email addresses, phone numbers and postal addresses, each labelled and one of each marked primary;
- a birthday and other dates (an anniversary, say) — the year can be left out;
- **People**: the names of a spouse, children, a manager, an assistant and so on;
- websites, chat addresses (SIP, XMPP, Skype, Teams, Matrix and others), and **custom fields** of
your own naming;
- notes, tags and a photo.
A label that came from the server and is not one of plMail's own (a "Godmother" typed on a phone)
is shown as it is and kept when you save.
If you have a synced address book you can choose where a new contact goes under **Save to**: one
button per address book when you have up to four, a menu when you have more. **plMail** keeps it in
plMail only. Otherwise it goes to that address book's server and from there to every device that
syncs it; the line under the buttons says which. A read-only address book is never offered.
Editing works the same for both kinds. For a synced contact, the edit is saved here at once and sent
to the server straight after, and from there to your phone. Anything plMail has no field for, such
as the properties a phone uses to link a contact to an app, stays on the server's copy untouched.
Google and Outlook have no place for some fields; see **Things that bite**. See [CardDAV](Providers-Carddav#sync) for how
edits and conflicts are handled.
A contact from a **read-only** address book (a shared book, a company directory) can be read and
written to, but not changed or deleted here. Its page says where it comes from.
**Photos** are JPG, PNG or WebP up to 5 MB, and kept at up to 512 pixels on the longer side. They go
both ways: a photo on the server is shown here, and a photo uploaded or removed here is sent to the
server. In the editor, the camera on the photo uploads one and **Remove photo** at the bottom removes
it.
## Deleting
**Delete** moves a contact to the bin. Deleting a synced contact also deletes its card on the
server, and the contact stays in the bin as one of plMail's own.
## Duplicates
**Duplicates** lists pairs that look like the same person, with how sure the match is and why
(the same email address, the same phone number, a similar name). Pick the one to keep and
**Merge**. The other one's addresses, numbers and tags move to the kept contact, and its notes are
added below the kept contact's. A merge can be undone for 90 days.
Synced contacts merge like any other. The kept one's card is updated, and the merged one's card is
deleted from its server, so an undo brings the merged contact back as one of plMail's own. Contacts
from a read-only address book cannot be merged.
## Import and export
**Import** takes a `.vcf` (vCard, as every phone and address book exports) or a `.csv` file. You see
a preview first. Imported contacts are plMail's own, and you land on the duplicates page afterwards
to merge anything you already had.
**Export** downloads every contact as one `.vcf` file, photos included. A synced contact is exported
as its server's card with your edits in it, so the file keeps what plMail has no field for. The CSV
export is a summary for a spreadsheet — one email, one phone and one website per contact — and reads
back in; use vCard to move contacts whole.
## While reading and writing mail
A message's sender, and every address in its details (To, Cc, Reply-To), shows whether it is already
in your contacts, with a button to add it if not. The button asks who it is:
- **New contact**: check the name taken from the mail and save. *More details* opens the full
editor with the name and address filled in.
- **Add to an existing contact**: search your contacts as you type and pick one. The address is
added to that contact, so somebody writing from a second address stays one person. The search
starts with the sender's name. Contacts in read-only address books are not offered.
Once saved, the contact opens in the contacts pane beside the mail. If the pane was closed it opens,
and if it was open it goes to that contact. *More details* opens the editor there too. On a phone
there is no pane: *More details* opens the editor as a page, and after a save the button turns into
a link to the contact.
When you address a message, your contacts are suggested along with the addresses plMail has
learned from your mail. The order is explained under
[suggestions while writing](Providers-Carddav#suggestions-while-writing).
## On a phone
The plMail apps read and edit the same contacts over JMAP (`Contact/get`, `Contact/query`,
`Contact/set`). Each contact says which address book it is in and whether it can be changed. An app
cannot change a contact from a read-only book, and its edits to a synced contact go on to the
server like edits made on the web.
## Things that bite
**The bin has no page yet.** A deleted contact is kept, but nothing on the web brings it back. Merge
instead of deleting when you are tidying up duplicates: a merge can be undone.
**An import makes plMail's own contacts.** To put a `.vcf` file into a synced address book, import
it on the server (in Nextcloud, under Contacts), and the contacts arrive here with the next sync.
**A contact cannot move between address books**, or from plMail's own into a synced one. Create it
in the other place and delete the old one.
**Google and Outlook cannot hold everything.** Google keeps every field above. Outlook has fixed
places: three email addresses, two business and two home numbers and a mobile, one address of each
kind, one spouse, manager and assistant and any number of children, one website, three chat
addresses and a birthday. It has no other dates, no phonetic middle name and no custom fields. What
does not fit is not sent and stays only in plMail until the contact next changes in Outlook (see
[Microsoft](Providers-Microsoft#contacts-are-on-the-same-sign-in-too)).
**Outlook keeps a photo removed here.** Microsoft's interface can replace an Outlook contact's photo
but not remove it, so a photo removed in plMail comes back the next time the contact changes in
Outlook. Remove it in Outlook.
**Custom fields and the office are plMail's own on a CardDAV server.** vCard has no property for
either, so they are stored on the card under plMail's own names (`X-PLMAIL-FIELD`,
`X-PLMAIL-OFFICE`). They survive the round trip, but a phone does not show them.
---
*This page is generated from [`docs/features/contacts.md`](https://github.com/karatektus/pl_mail/blob/main/docs/features/contacts.md). Edit it there — changes made here are overwritten on the next push to `main`.*
+71 -4
@@ -299,6 +299,12 @@ card. Dated ones queue
up in the calendar's **Happening Soon** panel, under **On your radar**; a conversation that yielded
something also shows its cards in a **Found in this conversation** strip above the messages.
Mail in Spam yields no cards, and neither does mail you move there later: its cards leave the panel
and the strip above the mail list, and a spam conversation shows none above its messages, because a
fake delivery notice is exactly what spam imitates. **Not spam** (or moving it to the inbox) brings
them back. The bin is different: deleting a shipping confirmation does not stop the parcel, so its
cards stay.
A shop that never names a carrier still yields a parcel. Amazon states an order number and a link
into its own tracker and no tracking number anywhere, so that is what the card carries — and a
delivery day promised in words rather than digits ("arriving today", "Arriving Monday") is resolved
@@ -397,8 +403,10 @@ provider recorded an `Authentication-Results` header. Both it and **Print** open
HTML bodies are rendered inline rather than in an iframe. Before that happens the body is put
through a sanitiser once, at ingest: `cid:` references are rewritten to plMail's own attachment
route, `<style>` blocks are flattened onto the elements they applied to, and scripts, forms,
iframes and classes are dropped. Links are forced to open away from the app.
route, `<style>` blocks are flattened onto the elements they applied to, and scripts, iframes and
classes are dropped. A form is taken apart rather than dropped: what is in it stays, its text
fields and menus go, and the mail looks the way its sender meant it to in a mail program without
forms. Links are forced to open away from the app.
### Summarising a long conversation
@@ -547,6 +555,9 @@ whose body only ever says `help.wirhub.de`.
`spam`), `archive` (or `archived`) and `snoozed`. Quoted values are kept together, so
`from:"Ada Lovelace"` works.
Search leaves out Spam and the bin unless you ask for them with `in:spam` or `in:trash`, the same
as the lists do. The suggestions as you type leave both out.
Suggestions appear as you type, and so do **actual results** — the ten most recent matching
conversations, under the operator suggestions, from about the third character. They are a preview
rather than the answer: they run only the passes that are fast enough to spend on every keystroke,
@@ -646,6 +657,34 @@ On a phone the same sync is a pull: drag the list down from its top, past the in
the indicator spins with it. Without touch, the sync button is the equivalent: it is a named button
for a keyboard or a screen reader, and the sync toast says when the run starts and when it ends.
On a phone a row swipes too. Drag it to the right and it does what the right direction is set to; by
default that marks it read, or unread if it already is. Drag it to the left and, by default, it is
archived. What each direction does is set under **Settings → Mail → Swipe actions**, below. Past a third of
the row's width the action waits behind the row, and pressing it does the thing. Past two thirds, or a
quick flick past the first third, it happens as you let go. Touching anything else closes an open row,
and that touch does nothing more. Read / unread and archive are offered only on ordinary mail: never on
a draft, and never in the archive, the bin or spam. Star is offered on every row, drafts included. Without touch, the row's checkbox (its avatar) and
the toolbar do the same work, **Archive** and **More** on a selection included.
Each direction has its own setting under **Settings → Mail → Swipe actions**, a select for right and one
for left. The choices are read / unread, archive, star, and nothing. Both directions may be set to the
same choice. Trash is not one of them (see *Things that bite*). A star is a toggle on the row, so a
second swipe takes it off again, as it does for read / unread; archive offers its **Undo**.
On a phone, pressing and holding a row also opens its actions. Hold a row still for about half a second
and an action sheet rises from the bottom of the screen with every action the row offers: select it,
mark it read or unread, archive it, star it, move it (a conversation, not a single message), label it,
snooze it, mark it as spam, delete it,
and, in the bin or in spam, move it back to the inbox and, if you have switched on **Settings → Mail →
Disposal → Delete-now buttons**, delete it for good. The entries are the ones the row's own buttons
offer, in the same places, and each does what that button does. **Move to**, **Label as** and **Snooze**
open their pickers over the sheet, as they do on the toolbar. The sheet closes on a tap on the dimmed
page, on Escape, or after an action, and the page behind it does not respond while it is open. A
background refresh leaves it alone, and a refresh that takes the row out of the list closes it. Where
the browser can vibrate (Android Chrome), opening the sheet gives a short buzz; iOS has no vibration for
web pages, so there is none there. Reduced motion makes the sheet appear without its slide. Without
touch, the row's own buttons and the toolbar do the same work.
### Keyboard shortcuts
The single keys Gmail uses work here too. Press **?** anywhere for the full list, or the
@@ -704,6 +743,9 @@ conversation back and takes away a filter the same click created.
Above a selection the button moves everything selected to Spam and offers no filter: a selection
has no one sender. The button is not shown for mail that is already in Spam or in the bin.
An open conversation in Spam has a **Not spam** button in its toolbar instead. It moves the
conversation back to the inbox, the same as **Move to → Inbox**.
Moving mail to spam tells plMail nothing about the next message. plMail has no spam filter of its
own that learns; whether your provider treats the move as a signal is up to the provider. A filter
is how you make it stick here.
@@ -799,6 +841,10 @@ says how many it held back, with two ways to show them — **Show images** for t
sender's messages say so under the body, with **Stop showing** to take it back. Mail you sent
yourself shows its pictures without asking.
Shown pictures always come through plMail, and only over an encrypted connection: a picture the
mail links as `http://` is fetched as `https://`. The rare host that cannot answer that way leaves
an empty box.
**Settings → Mail → Images**, **Always show remote images**, turns the question off for every
sender. It is off until you turn it on. Senders can then tell that you opened their mail, but not
your address: the pictures are fetched by plMail and passed on to you, never by your browser. The
@@ -1173,10 +1219,31 @@ included.
**Pull to refresh is for a finger at the top of the list, and only there.** A mouse, a trackpad and
any window at the tablet breakpoint or wider never pull. A pull starts only while the list is
scrolled to its very top, and not while the sidebar is open: from further down, the same drag just
scrolls. Reduced motion removes the indicator's slide and nothing else, so the release point is the
scrolled to its very top, and not while the sidebar, an open row or the action sheet is up: from further
down, the same drag just scrolls. Reduced motion removes the indicator's slide and nothing else, so the release point is the
same.
**A swipe is a finger's gesture on a phone, and its directions are physical.** A mouse never swipes,
at any width, and nothing swipes at the tablet breakpoint or wider. Right means towards the right
edge of the screen whatever the language, because no right-to-left locale ships. A reader who adds
one has to decide how the two sides mirror.
**A refresh leaves a row you are swiping, or have open, where it is.** Such a row keeps its state while
a sync or an update redraws the list, and the change to that row arrives when you close it. A row that
is only pressed, or that a long press has opened into the action sheet, is redrawn as usual. A refresh
that removes the row takes it off the list, and closes the sheet with it.
**Trash is not a swipe.** A full drag has no tap in front of it, and a trashed row has no undo of its
own, so deleting stays with the row's buttons and the bin, where a tap comes first. Trash is not one of
the **Settings → Mail → Swipe actions** choices either: a value stored for it reads as that direction's
default, so a restored backup cannot bring it back.
**A long press opens the sheet, and a phone's rows are not dragged.** Below the tablet breakpoint a
thread row is not a native drag: HTML5 drag never worked from a finger, and turning it off keeps iOS
from lifting a row during the half-second hold. A mouse still drags a row as before. The sheet offers
trash on a row in the inbox, where the row's own trash button does, and it has no Undo, as that button
has none.
**A signature image has to be small to travel with the mail.** A picture in a *signature* is stored
inside it and sent as an inline part, up to 256 KB each and 768 KB per signature; see
[Signature](#signature). Anything larger, an SVG, or an image that could not be fetched stays a
+2
@@ -35,6 +35,7 @@ mechanism behind it.
| [Reminders](Features-Calendar-Alerts) | Setting alerts, how they are delivered, what a fresh install needs before they arrive |
| [Connected calendars](Features-Calendar-Sync) | Subscribing to Google, Microsoft and CalDAV calendars, two-way sync, ICS import, export and feed subscriptions, duplicate meetings |
| [Sharing and booking](Features-Calendar-Sharing) | Share links and what each reveals, appointment pages, how a booking arrives |
| [Contacts](Features-Contacts) | The address book: your own contacts and synced ones side by side, editing, duplicates and merging, vCard and CSV import and export |
| [Files and integrations](Features-Integrations) | Attaching from and saving to Drive, Photos, OneDrive, Dropbox, Nextcloud, Immich and Paperless-ngx |
| [Security](Features-Security) | Changing your password, two-factor authentication, recovery codes, remembered devices, app passwords, sessions |
| [Other clients](Features-Clients) | Connecting a JMAP client, per-app passwords, the PWA and browser notifications |
@@ -65,6 +66,7 @@ Each page names the exact console, the exact checkboxes and the exact redirect U
| [Microsoft](Providers-Microsoft) | Azure app registration, redirect URIs, delegated permissions, tenant choice, Graph subscriptions |
| [IMAP and SMTP](Providers-Imap-Smtp) | Plain mailboxes, IDLE, and the settings servers disagree about |
| [CalDAV](Providers-Caldav) | Discovery, app passwords, and the servers that have been tested |
| [CardDAV](Providers-Carddav) | Address books from Nextcloud and other servers: suggestions while writing, editing, and two-way sync |
| [ICS feeds](Providers-Ics-Feeds) | Subscribing to a published calendar by URL, and why some addresses are refused |
| [Single sign-on](Providers-Oidc) | Signing in to plMail through Authentik, Keycloak or another OpenID Connect provider: setup, account linking, and auto-creation |
+1
@@ -103,6 +103,7 @@ one container consuming `scheduler_default`. **Without that container, none of t
| every minute | `app:calendar:alerts` |
| `*/15` | `app:mail:sync` |
| `7-59/15` | `app:calendar:sync --stale` |
| `11-59/15` | `app:contacts:sync --stale` |
| hourly at :20 | `app:calendar:push` |
| daily 03:50 | `app:calendar:materialise` |
| daily 04:00 | `app:push:renew --repair` |
+1
@@ -212,6 +212,7 @@ in before, with logs and orphaned blobs growing without bound.
|---|---|---|
| `*/15 * * * *` | `app:mail:sync` | Neither Gmail push nor Graph subscriptions guarantee delivery and IDLE connections drop; polling is the backstop |
| `7-59/15 * * * *` | `app:calendar:sync --stale` | The mechanism for CalDAV and ICS feeds, the backstop for Google and Graph. Offset off the quarter hour so it does not stack on the mail sweep — they share one worker |
| `11-59/15 * * * *` | `app:contacts:sync --stale` | CardDAV address books have no push at all, so this is the only way changes made on a phone arrive. Offset off both sweeps above for the same shared-worker reason |
| `20 * * * *` | `app:calendar:push` | Hourly, not because anything expires that fast, but because registration fails for *deployment* reasons that have nothing to do with the click that connected the calendar |
| `* * * * *` | `app:mail:wake-snoozed` | A minute is the unit people pick a wake time in |
| `* * * * *` | `app:calendar:alerts` | Same argument, plus the interval is the bound on how late a reminder can be |
+22
@@ -461,6 +461,28 @@ carrying it would look like it is in two places at once to anything counting loc
deliberate trade: a thread that returns in the state you left it in is one you have already
learned to scroll past.
### "In the bin" is stored, and the database keeps it
A conversation is in the bin when it carries Trash and none of its messages is outside Trash
(#268). Every list, every badge and search leave such a conversation out. They read
`message_thread.binned` instead of working the rule out from the messages; on a mailbox of 67k
conversations, working it out took about 170 ms per query.
Triggers maintain the column, not PHP (`Version20261022160000`). The writes that change the
answer are spread out: labels through the ORM, raw SQL that rebuilds threads or unlinks a label,
`ON DELETE CASCADE`, a message moving to another thread, a reply arriving in a conversation in
the bin. A flag set from PHP would be right only on the paths someone remembered.
- `message_label` and `thread_label` fire once per statement and recompute only the threads
whose Trash rows changed. Filing, reading and labelling mail cost nothing.
- `message` fires per row: on insert only when the thread is in the bin (a new message can only
take it out), on delete and on a change of `thread_id` for the thread the message left.
- Each recompute derives the answer from scratch (`plmail_refresh_thread_binned()`), so the
order of the statements in a flush does not matter.
The entity maps the column read-only. In memory it can lag behind a change that has not been
flushed, so nothing in PHP should branch on it; the queries read it.
## The change log JMAP reads
Every mail mutation writes a row to `jmap_change_log` (`App\Jmap\State\ChangeLog`). The
+38 -3
@@ -2,8 +2,8 @@
CalDAV is the standard protocol for calendars on a server, and plMail speaks it to any server that
implements RFC 4791: Nextcloud, Radicale, Baïkal, Fastmail, iCloud, a Synology box in a cupboard.
There is nothing to register with anybody and no administrator step — a user connects a server from
their own settings with an address, a username and a password.
There is nothing to register with anybody and nothing an administrator has to switch on — a user
connects a server from their own settings with an address, a username and a password.
**Gmail and Outlook calendars do not come in this way.** They arrive with the mail account, on the
grant it already holds, and need only the calendar permission ticked on the app registration — see
@@ -13,6 +13,40 @@ pointing it at Google's CalDAV endpoint is the first thing somebody tries.
What happens after a server is connected — ticking which calendars to mirror, two-way sync,
read-only calendars — is in [Connected calendars](Features-Calendar-Sync).
## Nextcloud: no second connection
A Nextcloud connected for files under **Settings → Integrations** also gives you its calendars.
Nextcloud serves CalDAV under the same `/remote.php/dav` with the same app password, so that
connection is listed in **Settings → Calendars** and its calendars are found from there. Nothing
on this page needs doing for it.
- **The address** is the one the Nextcloud connection uses, so an address an administrator pinned
in **Admin → Integrations** applies to calendars as well, including its exemption from the
private-network rule below.
- **Discovery** starts at `/remote.php/dav` under that address, subfolder installs included, and
only falls back to `.well-known` when nothing answers there.
- **Disconnecting** Nextcloud in Settings → Integrations removes its calendars too. As with any
calendar connection, events that came from your mail rather than from the server are moved to
your default calendar first.
- **A separate CalDAV connection** you made earlier to the same Nextcloud keeps working. Its
calendars show as *Already mirrored through …* on the Nextcloud connection's list, so the same
calendar is never mirrored twice.
The generic CalDAV connection below stays for every other server.
## One server for everybody
An administrator can pin one CalDAV server in **Admin → Integrations**, under **Calendars**, and the
setup wizard asks the same question. With an address pinned, every CalDAV connection finds its
calendars on that server rather than at the address its user typed, and that host is exempt from the
private-network rule [below](#addresses-plmail-will-refuse).
The address is changed or cleared in the same place, and the change applies the next time calendars
are found on a connection. Calendars already mirrored keep the address they were found at, so after
a server moves, find its calendars again. Clearing a pinned address in a private range takes its
exemption with it: calendars mirrored from that host then stop syncing with *"Server address must not
point at a private network"* until the host is listed in `INTEGRATIONS_ALLOWED_HOSTS`.
## Connecting a server
**Settings → Calendars → Connect a CalDAV server.** Four fields, in the order the questions actually
@@ -95,7 +129,8 @@ as every other user-supplied server address:
- A username or password embedded in the URL is refused outright — it would be logged wherever the
URL is, and would silently override the credentials on the connection.
- Anything resolving into loopback, link-local or a private range is refused unless the host is
listed in `INTEGRATIONS_ALLOWED_HOSTS`.
listed in `INTEGRATIONS_ALLOWED_HOSTS`, or is the host of a server address an administrator
pinned in **Admin → Integrations**.
That last rule is the one a self-hoster meets. A Nextcloud on your LAN at `192.168.1.10` or
`nextcloud.lan` is refused until you allow it, and a Nextcloud with no certificate needs the http
+135
@@ -0,0 +1,135 @@
# CardDAV
CardDAV is the standard protocol for address books on a server, and plMail speaks it to any server
that implements RFC 6352: Nextcloud, Radicale, Baïkal, Fastmail, iCloud. It is usually the same
server as [CalDAV](Providers-Caldav), on the same app password. A user connects one from their own
settings; there is no administrator step.
A mirrored address book's contacts join your [contacts](Features-Contacts), alongside
plMail's own. They are listed, searched, edited, merged and deleted there like any other, and every
change made there is written back to the server. They are also suggested when you address a
message.
## Connecting a server
**Settings → Contacts → Connect a CardDAV server.** The same form as for calendars, with the same
four fields in the same order — server address, name, username, app password — and the same
deliberately-off option to reuse a mail account's stored password. See
[Connecting a server](Providers-Caldav#connecting-a-server) on the CalDAV page for what each one wants;
everything there applies here.
**Nextcloud needs no CardDAV connection.** A Nextcloud connected for files under **Settings →
Integrations** is listed in Settings → Contacts too, with its own **Choose address books** button,
on the same app password. It is marked *Set up in Integrations* rather than offering a disconnect
button, because the connection also holds your files and calendars; disconnecting it there removes
its address books along with them, and the confirmation says how many. Its address books are
looked up under `/remote.php/dav`, as for [its calendars](Providers-Caldav#nextcloud-no-second-connection).
A CardDAV connection pointed at a Nextcloud by hand keeps working, and its contacts app shows a
per-book address under **Contacts → Settings** if you want to point at one book only. An address
book already mirrored through one of the two is shown as *Already mirrored through …* on the
other's list and cannot be ticked there, so its contacts are not listed twice.
Saving connects immediately and then lists the address books the server offers, with tick boxes.
Ticked books are mirrored; their first sync is queued at once, so the contacts appear within a
minute rather than at the next sweep.
**For any other server it is a separate connection from CalDAV**, even on the same server. Some
servers carry only one of the two, and a person may keep their contacts and their calendars in
different places. Connecting a Radicale or a Baïkal for both means entering the app password twice.
## Discovery
Exactly the CalDAV discovery with the address-book names: the pasted address first, then
`/.well-known/carddav`, then the principal's `addressbook-home-set`, then the home is listed. A
pasted calendar URL is not taken for an address book, and the same server's calendars are never
offered in the address-book list. See
[Discovery, and the `.well-known` dance](Providers-Caldav#discovery-and-the-well-known-dance).
The same [refused addresses](Providers-Caldav#addresses-plmail-will-refuse) apply: a server on your LAN
needs its host in `INTEGRATIONS_ALLOWED_HOSTS`, and plain `http://` needs `INTEGRATIONS_ALLOW_HTTP`.
## Sync
**Polling only.** CardDAV has no push, so mirrored books are read by `app:contacts:sync --stale`
every fifteen minutes (at :11, :26, :41 and :56, offset from the mail and calendar sweeps that share
the worker). A change made on a phone appears here within a quarter of an hour; **Settings →
Contacts** has a sync-now button per book.
Changes are read the way calendars are: `sync-collection` (RFC 6578) where the server advertises it,
otherwise `getctag` plus a full listing when the ctag moves. Only a full listing deletes local cards
it did not mention; a delta never does.
**Edits made here are saved at once and sent afterwards.** The contact is marked pending (the
Contacts list shows a small clock on it), and a couple of seconds later the worker sends it on the
`export` queue, with the mail leaving plMail. Sending does not read the server: changes made
elsewhere arrive with the next sync. If the server can't be reached, the edit stays pending and
that sync sends it first. Each sync sends pending edits *before* reading the server, so the
server's older copy never overwrites an edit on its way out.
**Every write is conditional.** A new contact is `PUT` with `If-None-Match: *` under a name derived
from its UID; an edit or a delete carries `If-Match` with the etag last seen. If the card changed on
the server in the meantime, the server's version wins: the address book is read again and the edit
made here is dropped, with a warning in the log naming the contact. Merging two versions of a vCard
field by field is a feature of its own, and getting it wrong corrupts an address book.
A card the server refuses outright (`403`, usually from a book that did not say it was read-only) is
handled the same way: the server's copy is kept and the refused edit is logged, so one card cannot
stop the rest of the book from syncing. Any other failure — the server unreachable, out of space, or
rejecting the password — keeps the edit pending, and it is sent again on a later sync.
**What plMail does not show, it does not touch.** A contact here has every field listed under
[Contacts](Features-Contacts#a-contact), and a card can carry more, such as the properties a
phone uses to link a contact to an app. All of it is kept. plMail keeps the vCard the server sent
and applies an edit to it, changing only the properties that were edited. It does not rebuild the
card from its own fields. An address or a number that is still on the card keeps its type (work,
home), its grouping and the server's own formatting.
The fields are read in every spelling the common clients write — Apple's grouped `X-ABDATE`,
`X-ABRELATEDNAMES` and `X-ABLabel`, vCard 4's `ANNIVERSARY` and `RELATED`, `IMPP` and the older
`X-JABBER` family, Apple's 1604 for a birthday without a year — and a list changed here is written
the way most of them read it. The photo goes both ways: one uploaded or removed here replaces the
card's, and one that was not changed here stays on the card exactly as it was.
**Deleting a contact deletes its card.** A contact moved to the bin here is deleted on the server at
the next sync, and stays in the bin as one of plMail's own. Merging
duplicates updates the kept contact's card and deletes the merged ones' cards.
Read-only books, such as a shared book or a server's global address list, are mirrored and
suggested, and cannot be edited, deleted or merged here. Whether a book accepts writes is asked of
the server (`current-user-privilege-set`), not assumed.
## Suggestions while writing
The composer and JMAP `Contact/autocomplete` read one ranked list, in this order:
1. how often the address has been seen in your mail;
2. then whether it is in your contacts (plMail's own or a synced book);
3. then how recently it was seen, then the name.
So an address you write to often still comes first, and a contact you have never mailed comes before
an address you once typed into a draft. An address in both places is suggested once, under the name
from your contacts. Typing part of a name offers every address of that contact; typing part of an
address offers only that address.
## Things that bite
**The calendar and contacts connections are separate.** Connecting Nextcloud under Calendars does
not bring its contacts in; connect it again under Contacts.
**An edit made here can be lost to an edit made elsewhere.** If the same contact was changed on a
phone between two syncs, the phone's version wins and the change made here is dropped (and logged).
The window is the time between saving and the worker's next run, usually seconds.
**Unticking an address book, or disconnecting the server, discards edits not yet sent.** The copies
here go, along with any photo uploaded to them here; the server keeps its own. If a contact shows
the pending clock, wait for it to clear first.
**A LAN server is refused by default**, as for CalDAV. Add the host to `INTEGRATIONS_ALLOWED_HOSTS`.
**Moving a contact between address books is not offered.** On the server it is a delete in one
collection and a create in another; do it in the server's own interface.
---
*This page is generated from [`docs/providers/carddav.md`](https://github.com/karatektus/pl_mail/blob/main/docs/providers/carddav.md). Edit it there — changes made here are overwritten on the next push to `main`.*
+27 -2
@@ -31,6 +31,10 @@ Everything under **Settings** that this enables is described in
3. Enable the **Google Calendar API** on the same project if you want calendars. This is the step
the admin screen means when it says there is nothing to set up on the plMail side — calendar
access is bought entirely in this console.
4. Enable the **Google People API** too if you want contacts. It is the same kind of step for the
address book: without it, mail and calendars work, and each user's Google address book in
**Settings → Contacts** says that the administrator needs to enable the People API. Once it is
enabled, contacts sync on their own; nobody has to sign in again.
Note your **project ID** while you are here. It is lower-case and may differ from the display name
you typed; the Pub/Sub topic below is named with the ID, not the name.
@@ -79,12 +83,13 @@ it, because that combination looks configured and fails at the consent screen.
## The scopes, and what each one is for
plMail asks for these four at consent time, for every Gmail account:
plMail asks for these five at consent time, for every Gmail account:
| Scope | What it is for |
|---|---|
| `https://mail.google.com/` | The mailbox itself: reading messages, sending, applying and removing labels, moving and deleting. This is full mailbox access, and it is what a mail client is. |
| `https://www.googleapis.com/auth/calendar` | Calendars on the same account, read **and** write. |
| `https://www.googleapis.com/auth/contacts` | Contacts on the same account, read **and** write, through the People API. |
| `openid` | Identifies the account the token belongs to. |
| `email` | Supplies the address, which is what plMail names the account after. |
@@ -114,6 +119,25 @@ finds out when a calendar call is refused, and says so in the calendar settings
screen lets you untick calendar access while allowing mail. Reconnect the account and leave the
calendar box ticked."*
### Contacts are on the same sign-in too
The contacts scope rides the same grant, for the same reasons. A Google account connected to plMail
brings its contacts with it: its address book appears under **Contacts** beside plMail's own, with
Google's contact groups as tags, and an edit made in plMail is written back — names, phonetic names,
organisation, birthday and other dates, relations, websites, chat and SIP addresses, custom fields
and the photo, as well as addresses and numbers — and from Google to the phones that sync with it.
Only the fields plMail shows are sent, so what Google holds beyond them (occupations, interests, an
organisation's symbol) is left as it is. See [Contacts](Features-Contacts).
An account connected **before** plMail asked for contacts holds a grant without the scope. Nothing
is wrong with it: mail and calendars carry on, and **Settings → Account health** shows a notice, *Connect
… again to sync its contacts*, whose button signs in once more and adds the permission. Its address
book appears as soon as that is done.
Google classes the contacts scope as **sensitive**. That is a lighter class than the mail scope's
*restricted*, which an installation already has to live with (see below), so it changes nothing
about verification.
### What "restricted scope" verification means here
`https://mail.google.com/` grants access to the whole mailbox, and Google treats scopes of that
@@ -274,7 +298,8 @@ from* with no calendars on it. Reconnect and leave the box ticked.
**Enabling the Calendar API is not the same as adding the calendar scope**, and doing one without
the other fails in two different ways: the scope without the API is a 403 on every calendar call,
and the API without the scope is a token that was never granted calendar access.
and the API without the scope is a token that was never granted calendar access. The same goes for
the People API and the contacts scope.
**Domain verification is per Cloud project, not per Google account.** Verifying the domain in Search
Console is only the first half; it also has to be added under **Domain verification** in the same
+39 -3
@@ -57,6 +57,7 @@ the set as a whole; unlike Google there is no partial grant to handle.
| `Mail.Send` | Sending. Separate from the above in Graph's model, so it has to be added separately. |
| `MailboxSettings.ReadWrite` | Master categories, at `/me/outlook/masterCategories`. These are **not** covered by `Mail.ReadWrite`: they live under the Outlook user-settings resource and return `ErrorAccessDenied` without this. ReadWrite rather than Read because plMail creates category definitions when a label is mirrored. |
| `Calendars.ReadWrite` | Calendars on the same account, read and write. |
| `Contacts.ReadWrite` | Outlook contacts on the same account, read and write: each contact folder is an address book. |
Two of them are worth understanding before you decide to skip one.
@@ -83,6 +84,29 @@ If calendars do not appear for a user, plMail says which of the possible causes
**Settings → Calendars**: the permission may not be on the app registration, or the account may
genuinely have no calendars.
### Contacts are on the same sign-in too
`Contacts.ReadWrite` rides the same grant. A Microsoft account connected to plMail brings its
Outlook contacts with it: each contact folder appears as an address book under **Contacts**, with
Outlook's categories as tags, and an edit made in plMail is written back. See
[Contacts](Features-Contacts).
Outlook's contact has fixed places where plMail has lists: three email addresses, two business and
two home numbers and one mobile, one home, one business and one other address, one spouse (or
partner), one manager, one assistant and the children's names, one website (the work one if there
is one), three chat addresses and a birthday. plMail fills those places, primary first. An entry
beyond them — a fourth email address, a second partner — is not sent, and stays only in plMail until
the contact next changes in Outlook, when Outlook's version comes back. Outlook has no place at all
for other dates (an anniversary), a phonetic middle name or custom fields; those stay in plMail. Its
chat addresses carry no service: a SIP address comes back as SIP, any other as "other".
Photos go both ways, with one exception: Microsoft Graph can replace an Outlook contact's photo but
not remove it, so a photo removed in plMail stays in Outlook.
An account connected before plMail asked for contacts holds a token without the permission. Mail
and calendars carry on, and **Settings → Account health** shows a notice whose button signs in once more
and adds it.
## The tenant value
`MICROSOFT_OAUTH_TENANT` — or the **Tenant** field in **Admin → Integrations → Mail sign-in** —
@@ -175,9 +199,21 @@ misconfiguration here.
usable; it is displayed once and cannot be retrieved afterwards, only replaced.
**Adding a permission does not upgrade tokens that already exist.** A user who connected before you
added `Calendars.ReadWrite` or `MailboxSettings.ReadWrite` holds a token that does not carry it, and
no amount of waiting changes that. The account has to be disconnected and reconnected so a fresh
consent issues a new token — `app:graph:diagnose` says so explicitly when it finds the gap.
added `Calendars.ReadWrite`, `Contacts.ReadWrite` or `MailboxSettings.ReadWrite` holds a token that
does not carry it, and no amount of waiting changes that. The account has to sign in again so a
fresh consent issues a new token — the reconnect button in **Settings → Account health** does that without
removing the account, and `app:graph:diagnose` says so explicitly when it finds the gap.
**An organisation that approves apps for its users refuses every sign-in after an upgrade that
adds a permission.** In a tenant where users may not consent to apps themselves, an administrator
approved plMail once, for the permissions it asked for then. A plMail version that asks for one
more (`Contacts.ReadWrite`, when contact sync arrived) is refused at sign-in, mail included, with
`AADSTS65001` or `AADSTS90094`, until the administrator approves again: **Microsoft Entra admin
centre → Enterprise applications → plMail → Permissions → Grant admin consent**. Do it before
upgrading, or before anyone reconnects. The sign-in error tells the user to ask their administrator,
**Admin → Integrations → Microsoft mail sign-in** carries the same warning, and so does the
reconnect offer in **Settings → Contacts**. Personal Microsoft accounts and tenants where users
approve apps themselves are not affected.
**Identity working while every mail endpoint fails is not a credentials problem.** It usually means
the Microsoft account has no Outlook mailbox provisioned at all, which happens with accounts created
+2
@@ -13,6 +13,7 @@
- [Reminders](Features-Calendar-Alerts)
- [Connected calendars](Features-Calendar-Sync)
- [Sharing and booking](Features-Calendar-Sharing)
- [Contacts](Features-Contacts)
- [Files and integrations](Features-Integrations)
- [Security](Features-Security)
- [Other clients](Features-Clients)
@@ -37,6 +38,7 @@
- [Microsoft](Providers-Microsoft)
- [IMAP and SMTP](Providers-Imap-Smtp)
- [CalDAV](Providers-Caldav)
- [CardDAV](Providers-Carddav)
- [ICS feeds](Providers-Ics-Feeds)
- [Single sign-on](Providers-Oidc)