github getgrav/grav-plugin-email 5.1.0

7 hours ago

New

  • Email actions and plugins can now set custom headers on a message with a headers parameter, a map of header name to value, applied after everything else. The pair this was added for is List-Unsubscribe and List-Unsubscribe-Post, which together are RFC 8058 one-click unsubscribe: the unsubscribe button next to the sender name in Gmail and Outlook, and the thing a bulk sender is now expected to have. Setting a header that is already there replaces it, a value may be a list for a header that is allowed to repeat, and a name or value that cannot be written is skipped and logged rather than failing the whole send. Plugins building their own message can pass the same list to applyHeaders(), and can ask Email::supportsParameter('headers') first instead of comparing version numbers
  • A test suite, run with tests/vendor/bin/phpunit after composer install inside tests/. It installs into tests/vendor from its own tests/composer.json, so the vendor directory the plugin ships stays free of development packages
  • A provider contract under classes/Providers/, so that everything a mail provider knows about itself — how its delivery webhooks are verified and read, how a webhook is created from a pasted API key, what a sending domain's DNS has to say, and what its transport does to custom headers on the way out — lives in that provider's own grav-plugin-email-<provider> plugin rather than in whatever add-on happened to need the answer first. A transport plugin implements Providers\Provider and registers itself on the new onEmailProviders event; Email::providers() answers the registry, Email::providerFor($engine) and Email::providerByKey($key) find one, and a caller asks Email::supportsFeature('providers') first rather than comparing version numbers. A transport with no delivery API registers nothing, which is a complete answer: a store can then say plainly that the transport cannot report deliveries instead of showing a webhook address nothing will ever post to. Written up for plugin authors in docs/providers.md
  • Email::buildMailerFor($engine) builds a Symfony mailer for a named engine rather than only for the one the site is configured with, through the same transport builder, the same onEmailTransportDsn event and the same DSNs as before. Nothing sends through it yet; it is there so that a site which one day wants some of its mail to leave through a second provider has somewhere to ask for that mailer, without that day being the day the transport builder gets rewritten
  • Providers\Event::$hard now has a documented second meaning on a dropped event, which is the difference between a subscriber who is gone and one who happened to be on the list the morning the store ran out of quota. hard = true means the provider refused the address — it is on that provider's suppression list, or bounced, complained or unsubscribed there — and a store may treat it as permanent. hard = false or null means the provider refused this one message: a daily quota, a virus scan, content it did not like. The address is fine. Event::isRefusedAddress() is the question a suppression list should be asking, and docs/providers.md sets out which of each provider's refusals is which
  • Providers\SendHeader names the header a store stamps its send id into, so that every provider answers the same one and nothing has to hard-code a name. It is X-Grav-Send-Id unless the site sets providers.send_header in the Email plugin's configuration, or an add-on calls SendHeader::override() to name it for the request. The reading of it lives there too — out of a map of custom args or user variables, a {name, value} header list, or SES's message tags — because seven transport plugins were each about to keep their own copy of the same twenty lines. SendHeader::metadataHeader() is the Postmark twin, which is the same value under the X-PM-Metadata- prefix that is the only way metadata reaches a Postmark webhook

Don't miss a new grav-plugin-email release

NewReleases is sending notifications on new releases.