Repository navigation
v0.3.9
Image: ghcr.io/karatektus/pl_mail:0.3.9
Contacts: an address book, with your Google, Outlook, Nextcloud or CardDAV contacts in it. A new Contacts page (in the sidebar) lists, searches and tags your contacts, shows each one's details and the mail you exchanged, and creates, edits and deletes them: names in parts and how they are spoken, organisation and office, postal addresses, birthdays and other dates, related people, websites, chat addresses, custom fields, notes and a photo. The button beside the calendar's docks the whole address book beside the mail, resizable like the calendar, and on a wide monitor the two sit side by side. Duplicates are found and merged, with 90 days to undo, and contacts import from and export to vCard and CSV. A message's sender shows whether they are in your contacts, with one click to add them. Address books on a server come in too: a Google or Microsoft mail account brings its contacts on the same sign-in as its mail and calendars (an account connected before this asks once, in Settings → Account health, to sign in again and allow them), a Nextcloud connected under Settings → Integrations is listed under Settings → Contacts with no second connection to make, and Radicale, Baïkal, Fastmail or iCloud connect there over CardDAV. Their contacts sit beside plMail's own, a filter shows one book at a time, and every change made here — the photo included — is sent back to the server and from there to your phone. Anything plMail has no field for stays on the card. Changes made elsewhere arrive within fifteen minutes (app:contacts:sync --stale). Contacts are suggested when you address a message, and over JMAP (Contact/get, Contact/query, Contact/set) for the apps. See Contacts and CardDAV. #157, by @jo2904 and @DanversKara.
Migration: adds the address_book and contacts_* tables; nothing existing changes. For contacts on Google accounts, enable the People API in the Cloud project and add the .../auth/contacts scope to the consent screen; for Microsoft accounts, add the Contacts.ReadWrite delegated permission to the app registration — see Google and Microsoft. Without the People API, mail and calendars work as before, and a Google address book says the API needs enabling. A Microsoft 365 organisation where only administrators approve apps refuses every sign-in to plMail after this upgrade, mail included, until an administrator approves the new permission: Microsoft Entra admin centre → Enterprise applications → plMail → Permissions → Grant admin consent. Do it before upgrading. Personal Microsoft accounts are not affected.
Pictures and videos open in a preview when you click them, like PDFs. Clicking an image attachment (JPEG, PNG, GIF, WebP, AVIF, BMP, ICO) or a video (MP4, WebM, Ogg, QuickTime) now opens it in the same dialog as the PDF reader instead of downloading it. Videos play with the browser's own controls and can be seeked without loading the whole file. Every attachment that opens a preview, PDFs included, has a download button on the right side of its chip, so downloading is still one click. If the browser can't show the file after all, the dialog says so and offers the download. Other attachments download on click as before. No schema change. #295.
A simpler contact editor, and new contacts from whatever you paste. New contact is now one box: type or paste a name, an email signature, a Name <address> line, an address or a vCard, and the details it finds appear below it, each one easy to untick, retype or give another type with one click (a work number that is a home number). Lines it cannot place are asked about instead of guessed. The editor shows the name as one field whose parts fill themselves, email and phone rows that appear as you type, and only the kinds of detail a contact has, with the rest a click away; a bar at the bottom says what changed. Paste details in the editor compares pasted text with the contact before anything changes: new details ticked, ones that differ left for you to decide.
Improve with AI, for contacts. With the new Contact help switch on (Admin → AI, off by default), quick add can give pasted text a second reading by the AI, with what it changed marked and undoable, and the editor can suggest updates to a contact from the signature of the newest mail they sent you, each with its reason. Only the signature block of that mail is sent, never its body, and nothing is saved until you tick it and save. Everyone can switch it off for themselves under Settings → Assistant.
Migration: one migration adds the switch's two columns (ai_settings.contact_help_enabled, "user".ai_contact_help_off), both off by default.
On a phone, archiving a mail or marking it read meant opening it first. The row's buttons show on hover, and a phone has no hover, so they never appeared in the list. A row now swipes: drag it to the right to mark it read or unread, and to the left to archive it, past a third of its width to reveal the action or past two thirds to do it straight away. It is for a finger on a phone-width screen only; a mouse and wider windows behave as before. Read / unread and archive are offered only on ordinary mail, never on a draft or in the archive, the bin or spam, as the row's own buttons do; star is offered on every row. Trashing is not a swipe. Each direction can be changed under Settings → Mail → Swipe actions (next entry). No schema change, no deployment change. #147, by @Flouss.
On a phone, a swipe to the right or to the left always did the same thing. The two directions were read or unread and archive, with no way to change either. Settings → Mail → Swipe actions now sets each direction to read or unread, archive, star, or nothing, and the defaults are the same as before. Trash is not one of the choices: a swipe commits without a tap, and a trashed row has no Undo. A value stored for trash reads as the direction's default. No schema change: the two choices live in the settings bag, and the config backup carries them. #148, by @Flouss.
On a phone, a mail row's actions were only reachable through buttons that appear on hover, and a phone has no hover. Archive, delete, snooze, move and label were in the row's strip, which a phone never shows. Holding a row for about half a second now opens an action sheet with every action the row offers, the same ones the strip and the swipe offer, in the same places. Move to (on a conversation, not a single message), Label as and Snooze open their pickers over the sheet, and Select ticks the row for a bulk action. A row is no longer a native drag on a phone, so iOS does not lift it during the hold. Trash in the sheet has no Undo, as the strip's trash button has none, and delete forever is offered only with the delete-now buttons switched on under Settings → Mail → Disposal. The sheet is only for a finger on a phone-width screen; a mouse and wider windows behave as before. Android Chrome gives a short vibration when the sheet opens; iOS has no vibration for web pages. No schema change, no deployment change. #149, by @Flouss.
Mail trashed or archived in Gmail no longer leaves the Inbox of the account Gmail collected it for. When a Gmail account collects mail for another connected account (Gmailify), plMail merges the Gmail copy into that account's own IMAP copy, and the merge applied Gmail's TRASH, SPAM, INBOX, SENT and DRAFT too. Mail still in the IMAP server's Inbox showed as trashed or archived and dropped out of the Inbox list. The IMAP folder now decides where such a message sits; Gmail's other labels still merge. Mail that Gmail deleted for good, by emptying its bin, was also removed from the other account here, although its IMAP server still had it, and the IMAP sync never fetched it again. That copy now only loses its link to Gmail. Mail already moved this way is put back with app:backfill gmailify-placement; mail already removed is not brought back. No schema change. See Accounts. #203, by @phdelodder.
A Gmail account can stop filing mail under your other accounts. A Gmail account that collects mail for another connected address files that mail under the other account (Gmailify), and nothing in Settings could turn this off. After a domain moved from Gmail to an IMAP provider of its own, Gmail went on matching old and late copies to it. Settings → Aliases now has a switch under each Gmail account, on by default. Turned off, the Gmail account keeps only mail addressed to itself, and the other account's own IMAP copies lose their link to Gmail, in a background job on the maintenance queue. Mail Gmail filed there that the IMAP server never had keeps it. While Gmail is still collecting, mail whose To or Cc names the other address is left to that account even though Gmail stamps its own address on it; mail Bcc'd there names nobody and is kept on both, so the switch is best turned off once Gmail has stopped collecting. No schema change: the choice lives in the account's settings, as it did before there was a switch. #205.
Admin → Integrations lists the calendar services, so a CalDAV server pinned during setup can be changed later. The setup wizard could pin one CalDAV server address for everybody, and once setup was finished nothing showed that address again. CalDAV and calendar feeds now have their own group under the file services, with the same row, setup notes and Configure form, where the address can be changed or cleared. A change applies the next time calendars are found on a connection; calendars already mirrored keep the address they were found at. Both services are offered to every user as before, so their rows say so and the form has no on/off switch. Once people have connected, the CalDAV form says how many connections a change reaches. A calendar feed has no server to pin, so its row offers nothing to configure unless an older address is stored, which it shows so it can be cleared. Nextcloud stays with the file services, where its calendars' address is already set. The server address field now accepts only an http or https address. No schema change. #212, by @phdelodder.
A Gmail sync no longer reads every Gmail id you have stored. To tell new mail from mail it already had, each Gmail sync loaded the id of every message stored for you, and every one it had declined. It did this after every history window, including the many that bring nothing, and on every page of a listing. On a large mailbox that is thousands of rows every few minutes, and more as the mailbox grows. It now asks only about the ids Gmail has just listed, and nothing when there are none. No schema change. #223, reported by @phdelodder.
Lists, unread badges and search answer faster on a large mailbox. Every list and badge leaves out conversations that are entirely in the bin (#268), and each of them worked that out from the messages on every query: about 170 ms each on a mailbox of 67k conversations, and the sidebar asks about ten times. Whether a conversation is in the bin is now stored on it and kept by the database. On the same mailbox the Inbox count dropped to 35 ms, the unified Inbox page to 52 ms, and most other lists and badges to between 1 and 20 ms. The results are the same on all 41 lists, badges and searches compared. #223.
Migration: adds message_thread.binned with the triggers that keep it, and fills it for the conversations already stored. That took under a second on 67k conversations.
Nextcloud calendars come with the Nextcloud connection. A Nextcloud connected for files under Settings → Integrations is now listed in Settings → Calendars, and Find calendars on… lists its calendars with the same app password. There is no second address or password to type. The address is the connection's own, or the one an administrator pinned in Admin → Integrations; a pinned address on a private network now works for calendars too, where before it was refused as a private address. Disconnecting Nextcloud removes its calendars as well, and the confirmation says how many; events that came from mail are moved to the default calendar first, as when a CalDAV server is disconnected. A calendar already mirrored through a separate CalDAV connection to the same Nextcloud shows as Already mirrored through … and cannot be ticked a second time. Existing CalDAV connections are left as they are. No schema change. #236, by @jo2904.
Pictures in mail show up again where they were missing. A BAUHAUS review request showed neither its logo nor its product photos, even with images allowed, for three reasons. Pictures linked as http:// were never asked for, because plMail only fetches over an encrypted connection; they are now fetched as https://. Everything inside a form was removed along with the form, here the whole first product with its photo, name and stars; the form's content now stays and only its text fields go. And a card a mail hides by default and switches on with its stylesheet stayed hidden; plMail now follows the stylesheet as a browser does. Mail already in the mailbox is repaired on request: php bin/console app:backfill safe-html-hidden-cards writes the shown copy of every message with a form or such a stylesheet again from the sender's original. The http:// fix needs nothing. When plMail refuses a picture's address, the log now says why. No schema change. #271, by @pmdevelopment.
When notifications can't be turned on, Settings now says why. Every way that turning on notifications can fail used to end in the same "Something went wrong. Try again." Now the pane says which step failed: the browser's background worker, the browser's push service, or the server. It also gives the browser's error name or the server's HTTP status, and writes the full error to the browser console. For Brave, where push needs "Use Google services for push messaging" switched on, the push-service message says so. No schema change. #277.
A sync no longer fails after mail with an attachment was deleted on the server. When Gmail, Outlook or an IMAP server reported a message with an attachment as deleted, the sync that removed it failed on its next database write: "A new entity was found through the relationship 'MessagePart#message'". The retry went through, but the failed run did not record the sync, and on IMAP every folder after the one that tripped was skipped until the next run. Deleting mail now goes the same way whether the server, a discarded draft or "Delete forever" asked for it. As a result, a conversation that loses its last message is removed instead of left behind empty, and one deleted for good drops the labels only its deleted messages carried. No schema change. #299.
Spam stays in Spam: off the radar and out of search. Cards read out of mail in Spam showed in the Happening Soon panel and the strip above the mail list, beside the real ones, though a fake delivery notice is exactly what spam imitates. They are now left out, and a spam conversation no longer shows its cards above the message; mail in the bin keeps its cards, since deleting a shipping confirmation does not stop the parcel. Search, and the results as you type, likewise leave out Spam unless you search with in:spam, as they already did for the bin. No schema change. #304.
A Not spam button. An open conversation in Spam has a Not spam button in its toolbar, which moves it back to the inbox in one click instead of through Move to. No schema change.
-
"Add to contacts" on a mail now asks whether the address belongs to somebody new or to a contact you already have. You can search for that contact as you type, or name a new one and open the full editor for more details. The contact then opens in the contacts pane beside the mail. The button is on every address in a message's details too, not only the sender.
-
The contacts button in the top bar now steps through three positions, like the calendar button: the address book beside the mail, full width with the mail behind it, and closed. Dragging the pane's edge past either end snaps it to full width or closes it. Settings → Contacts → Behaviour can leave one of the open positions out. On a wide screen the sidebar no longer has a Contacts row; on a phone it stays in the menu.
A sync that can't reach Gmail or Outlook no longer fills the log with stack traces. When the worker could not connect to the provider at all, every retry of the account sync was logged as a warning with three nested stack traces. An outage that lasted ended at CRITICAL, with the job parked on the failed queue. Now the sync waits and tries again on the same schedule as before, with one info line per attempt. If the provider is still unreachable after about eight minutes, the sync is dropped with a single warning. Nothing is lost: the next sync resumes from the same point. Fetching particular messages still retries and fails as before, because nothing would list those again. #332
Pressing Compose before adding a mailbox says so. With no mail account yet, the compose window answered "The compose window could not be opened. Please try again.", which no retry could fix. It now shows "No mail account yet" with a button to add one, as it was meant to, and the button opens Settings → Mail accounts.
A conversation with one message in the bin stays in your other lists, and so does new mail in it. Deleting one message of a conversation took the whole conversation out of the Inbox, its tab, its labels, All Mail, Unread and search, because the conversation then carried the Trash label. A reply that arrived in it afterwards, by its references or by its subject, was unread mail the phone app showed and the web did not. A conversation now leaves those lists only once every message of it is in the bin. One that you deleted whole still leaves its labels, as before. A partly deleted conversation is listed in Trash too, and opening it still shows the deleted messages with the rest. No schema change. #268.
A long message scrolls in the expanded compose window. Expanded, the editor stayed 170px tall and cut a longer message off mid-line, with nothing to scroll. It now fills the window and grows with the text, and the window scrolls. A short message still fills the window, so clicking under the text still puts the cursor there. The plain-text field fills the expanded window as well. #262.
The month view says what is on a day. An entry in a month cell was its time on one line and its title cut to a single line under it, which at a normal width left "Lea…" and "Ihre…", and for an invitation a lone "…". It is now the title first, on up to two lines, and under it the time it runs and where. In a narrower day the end time and the place go so the title gets a third line, long words are hyphenated, and an invitation you have not answered has a dashed bar. On a phone, where a day is too narrow for words and its entries are bars, the grid makes room for the chosen day listed in full underneath: today at first, then whichever day you tap. The list has a + that adds an event on that day, and the toolbar's New event does too. On a touch screen with room for words, the first tap on a day opens it and the next opens an entry. No schema change. #253.
The calendar's toolbar and the bar at the top fit a phone. On a 320–375px screen the toolbar ran off its card and the top bar's icons slid over the logo. The toolbar now switches to icons and a short date ("October", "Oct 20–26") when it is narrow, and the top bar tightens and moves Sync now, What's new and Send feedback into the user menu, below a divider of their own. #253.
Dialogs are the whole screen on a phone, as the compose window is, with less padding, so the event editor's times, day buttons and repeat menu fit. #253.
The red now line moves, and the calendar turns over at midnight. The current-time line in the day and 7-day views was drawn when the page loaded and stayed there, and the month's circle on today stayed on yesterday until something reloaded the page. The line now moves every minute, and when the date changes the view redraws itself. #253.
A new event on a day that has gone stays on that day. Pressing + on a past day opened the editor on today, because a past date was pulled to the next hour. It now keeps its date, at the hour it would have had; today and future days are unchanged. #253.
A reply sent from plMail shows its quote to the person who gets it. The composer folds the quoted mail under a reply out of the way while you write, and sent it still folded: the "show quoted text" button was taken out, but the quote kept the display: none that hid it. So a reply sent without opening the quote reached Gmail, Outlook and Apple Mail users with the history invisible, and in plMail the "···" opened onto nothing. Replies go out unfolded now, and mail already received that way opens properly in plMail. Copies already delivered to other mail programs stay as they went out. No schema change. #257.
The preview line in the list shows what the newest mail says. A short reply ("Passt!") previewed as the mail it quoted, often your own, because the preview skipped short lines to find the first long one and landed in the quote. The quoted history and a -- signature are cut off first now, in HTML and plain-text mail alike. Only bulk mail (promotions, updates, social) still skips its "View in browser" lines; a person's mail starts at its first line, greeting included. The Android and iOS apps get the same preview. No schema change. #257.
A message marked read or unread in the phone app is shown that way on the web too. Marking a message read or unread over JMAP changed the message but not its conversation's unread count, which is what the web list draws in bold from, so a message marked unread on the phone stayed read in the browser and one marked read there stayed bold. It now goes the same way as the web's own read button, and so does an automatic read receipt where the mailbox sends one. Marking a single message of a longer conversation read or unread on the web also set the conversation's count from that one message alone; it is now counted from all of them. A conversation whose count is already wrong is put right the next time one of its messages is marked. No schema change. #266, by @phdelodder.
Trashing a conversation with one message already in the bin no longer fails. On an IMAP account, the message that was already in the Trash was sent to the server as a move from Trash into Trash, which the server refused with "NO Not permitted in EXAMINE state". The rest of the conversation moved, but the refusal was logged as an error and recorded on the account as a change the server would not take, and the binned message lost its address until the next sync found it again. A message that is already where a trash, archive or restore sends it now stays put and keeps its address; its labels change as before. No schema change. #269, by @phdelodder.