Skip to content

Club Mailer

Lets each WordPress user connect their own mail provider — Outlook / Microsoft 365 or Gmail / Google Workspace — and send tagged wp_mail() messages as themselves, through the provider's own API — not through the site's SMTP or transactional-mail account. A member connects one provider at a time; connecting a second one replaces the first.

It is a companion to Club Player Manager, not part of it. It stores nothing about players, guardians or squads — only which mailbox each WordPress user has connected — and works whether or not the calling code is Club Player Manager itself.

What it does

  1. A member connects their own mailbox, from their WordPress profile page, in two clicks and an OAuth consent screen. No admin involvement per member.
  2. Other code tags a message for that member. wp_mail() calls carrying an X-Club-Mailer-Send-As: <user id or email> header are diverted through that member's mailbox, sent from their address, and land in their own Sent Items. Everything else — core, WooCommerce, MailPoet, and every untagged wp_mail() call — is left alone.
  3. The site's admin registers an app per provider, once. Everything after that is self-service for members.

Requirements

  • WordPress 6.5+, PHP 8.2+
  • Pretty permalinks (Settings → Permalinks, anything but "Plain") — each OAuth redirect URI is a clean, query-free URL
  • HTTPS — both Entra and Google Cloud Console reject http:// redirect URIs for a web app (bare localhost is the one exception)
  • No Composer dependencies, and no build step

Setup

Settings → Club Mailer walks through registering a Microsoft Entra app (tenant, client ID, client secret) and/or a Google Cloud OAuth client (client ID, client secret), and gives the exact redirect URI each one needs pasted into it. A club only using one provider can leave the other's fields blank — members are only offered a connect button for a provider the admin has configured. Credentials can be stored on the settings screen, or pinned outside the database with CLUB_MAILER_TENANT, CLUB_MAILER_CLIENT_ID, CLUB_MAILER_CLIENT_SECRET (Outlook) and CLUB_MAILER_GOOGLE_CLIENT_ID, CLUB_MAILER_GOOGLE_CLIENT_SECRET (Gmail) constants in wp-config.php — any of these overrides its stored value and the settings screen says so.

Once that is done, a member connects from Users → Profile → Send mail as yourself.

Admin

Settings → Club Mailer, gated on manage_options — deliberately not widened toward cpm_manage_all the way most other satellite screens are, because the page stores OAuth client secrets capable of sending mail as any member who connects a mailbox.

Using it from another plugin

wp_mail(
    'recipient@example.com',
    'Subject line',
    'Message body',
    array( 'X-Club-Mailer-Send-As: ' . $user_id )
);

Two functions exist only while this plugin is active, so function_exists() doubles as the "is the mailer available?" check:

  • club_mailer_user_connected( $user_id ) — whether that user has a connected mailbox, on whichever provider they connected.
  • club_mailer_user_address( $user_id ) — the address a tagged send will come from. Often not the user's WordPress profile email; returns '' when not yet known rather than falling back to it. Check with function_exists() separately — an older copy of this plugin may lack it.

Data model

No custom tables. Per member: _club_mailer_active_provider (outlook or google), and for whichever provider that names, _club_mailer_{provider}_refresh (the encrypted OAuth refresh token) and _club_mailer_{provider}_address (the mailbox address, best-effort, backfilled on the next token refresh if unknown) user meta. A member connected before this plugin supported more than one provider has no _club_mailer_active_provider meta; club_mailer_user_connected() and club_mailer_user_address() treat that as outlook, since that was the only provider then. Site-wide: the club_mailer_settings option, holding one sub-array of credentials per provider.

Development

Everything runs from the repository root, where the shared dev tooling lives:

composer test          # every plugin's suite
composer test:mailer   # this plugin only
composer lint           # every plugin's phpcs ruleset
composer lint:mailer    # this plugin only
composer build -- club-mailer   # dist/club-mailer/club-mailer-<version>.zip

Layout

club-mailer.php                       Bootstrap: constants, require order, hooks
uninstall.php                         Deletes the option and every member's meta

includes/
  public-api.php                      club_mailer_user_connected()/user_address()
  interface-club-mailer-provider.php  The provider contract
  class-club-mailer-outlook-provider.php  Microsoft Graph: auth URL, token exchange,
                                       send, mailbox address lookup
  class-club-mailer-gmail-provider.php    Gmail API: auth URL, token exchange, MIME send,
                                       mailbox address lookup
  class-club-mailer-settings.php      Options: get() (merged over defaults, wp-config
                                       constants win), save() — one sub-array per provider
  class-club-mailer-connections.php   Profile UI, connect/disconnect, each provider's OAuth
                                       callback, the pre_wp_mail divert
  admin/                              The one settings screen, plus the orchestrator

templates/admin-settings.php
assets/css/  assets/js/
tests/Unit/  tests/Integration/  tests/Support/