Email newsletter management for WordPress — subscribers, mailing lists, templates, a visual editor, and a cron-based sending queue in one clean admin workspace.
Mail System adds a dedicated WordPress admin area for managing subscribers, mailing lists, email templates, and campaign delivery. It includes a visual drag-and-drop email editor, a sending queue with configurable throughput, SMTP configuration, WP-Cron integration, CSV/JSON import and export, a subscription form shortcode, and translation support for English, Bulgarian, and German.
The plugin ships without a required build step and works out of the box without running Composer on production.
| Area | What it gives you |
|---|---|
| Subscribers | Add, edit, and delete subscribers with active, inactive, and unsubscribed statuses. |
| Mailing lists | Organize subscribers into named lists and send campaigns to one or more lists. |
| Email templates | Reusable templates with a visual drag-and-drop editor for composing campaigns. |
| New campaign | Compose a campaign from a template, select target lists, and queue or publish immediately. |
| One-time email | Send a single ad-hoc email to a specific subscriber without creating a campaign. |
| Sending queue | Emails are queued and dispatched by WP-Cron at a configurable rate (default: 10/minute). |
| Email analytics | Review sent totals, unique opens, unique clickers, CTR/CTOR, repeat activity, and per-link performance. |
| SMTP | Configure an external SMTP server with SSL or TLS for reliable delivery. |
| Import / Export | Bulk import and export subscribers and lists in CSV or JSON format. |
| Subscription shortcode | [mskd_subscribe_form] renders a signup form on any page or post. |
| Internationalization | PHP and PO/MO translations for English (default), Bulgarian, and German. |
| Requirement | Version |
|---|---|
| WordPress | 5.0 or newer |
| PHP | 7.4 or newer |
| Composer | Development only — not required on production |
| Screen | Slug | Purpose |
|---|---|---|
| Dashboard | mskd-dashboard |
Subscriber and queue statistics at a glance. |
| Subscribers | mskd-subscribers |
Browse, add, edit, and delete subscribers. |
| Lists | mskd-lists |
Create and manage mailing lists. |
| Templates | mskd-templates |
Save and manage reusable email templates. |
| New campaign | mskd-compose |
Compose and queue a newsletter campaign. |
| One-time email | mskd-one-time-email |
Send a single email to one subscriber. |
| Queue | mskd-queue |
Inspect delivery status and per-campaign open/click analytics. |
| Settings | mskd-settings |
Configure SMTP, sending rate, and plugin options. |
| Import / Export | mskd-import-export |
Bulk import or export subscribers and lists (CSV or JSON). |
| Shortcodes | mskd-shortcodes |
Reference for available shortcodes and parameters. |
Place the plugin directory inside WordPress:
wp-content/plugins/mail-systemActivate the plugin in WordPress:
wp plugin activate mail-systemYou can also activate it from Plugins in the WordPress admin. No Composer or vendor/ directory is required — the plugin includes its own autoloader and works out of the box.
- Open
Emailsin the WordPress admin sidebar. - Go to
Settingsand set the sending rate (emails per minute) appropriate for your host. - Enable SMTP if you want to use an external mail server instead of
wp_mail().- Fill in the SMTP host, port, encryption (SSL or TLS), and credentials.
- Common providers: Gmail (
smtp.gmail.com:587, TLS, App Password), Mailgun (smtp.mailgun.org:587, TLS), SendGrid (smtp.sendgrid.net:587, TLS, usernameapikey). - Use the Send test email button to verify the connection.
- Go to
Listsand create at least one mailing list. - Go to
Subscribersand add subscribers, or useImport / Exportto bulk-import from CSV. - Go to
Templatesto create a reusable email template using the visual editor. - Go to
New campaign, select a template and target lists, and send or queue the campaign.
WordPress traffic-based cron can delay scheduled email delivery on quiet sites. For production, use a real system cron job and disable the traffic trigger.
Add this to wp-config.php:
define( 'DISABLE_WP_CRON', true );Run WordPress cron every minute from the server:
* * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1The plugin registers this event:
| Event | Interval | Purpose |
|---|---|---|
mskd_process_queue |
Every minute | Dispatch queued emails up to the configured rate limit. |
Place the subscription form on any page or post:
[mskd_subscribe_form]
With parameters:
[mskd_subscribe_form list_id="1" title="Subscribe to our newsletter"]
| Parameter | Description | Default |
|---|---|---|
list_id |
ID of the list to subscribe to | 0 (no list) |
title |
Form heading | "Subscribe" |
Use these placeholders inside campaign and template bodies:
| Placeholder | Description |
|---|---|
{first_name} |
Subscriber's first name |
{last_name} |
Subscriber's last name |
{email} |
Subscriber's email address |
{unsubscribe_link} |
Unsubscribe anchor tag |
{unsubscribe_url} |
Raw unsubscribe URL |
The plugin creates custom tables using the active WordPress table prefix.
| Table | Purpose |
|---|---|
mskd_subscribers |
Subscriber records and statuses. |
mskd_lists |
Mailing list definitions. |
mskd_subscriber_list |
Many-to-many subscriber-to-list relationships. |
mskd_queue |
Queued email jobs and delivery status. |
mskd_clicks |
Per-recipient, per-link click aggregates with privacy-safe display URLs. |
The plugin stores settings in mskd_settings and database versioning in mskd_db_version.
Every newly queued recipient receives a unique, non-identifying tracking URL. When the recipient's email client loads the invisible image, the queue row records its first-open timestamp and increments its pixel-load count. The Queue screen reports unique opens and calculates open rate against successfully sent emails.
Open data is approximate. Email clients that block remote images can cause missed opens, while privacy proxies and image prefetching can load the pixel before a recipient reads the message. The plugin does not store IP addresses or user-agent strings for these events.
Eligible http:// and https:// links are routed through a recipient-specific, HMAC-signed redirect URL. A valid click records first/last timestamps and a repeat-click count, then redirects to the original destination. Clicks also infer an open when the tracking pixel was blocked. The Queue screen reports unique clickers, total clicks, CTR, CTOR, per-recipient activity, and per-link performance. Stored reporting labels retain only the destination origin (scheme, host, and port), and no IP address, user-agent, device, or location data is retained.
Click aggregates follow the queue's lifecycle: clearing all campaigns clears their click rows, and uninstalling the plugin drops the click analytics table.
Unsubscribe and confirmation links, non-web schemes, and anchors carrying data-mskd-no-track are never rewritten. Messages sent with BCC are intentionally left untracked because the To and BCC recipients share one message body; this prevents BCC activity from being attributed to the primary recipient. Click counts remain approximate because security scanners and email clients may prefetch tracked links before a person clicks them.
Install development dependencies:
composer installCheck local Docker containers first:
docker ps -aRun PHPUnit in the WordPress test container:
docker exec -w /var/www/html/wp-content/plugins/mail-system php vendor/bin/phpunit -c phpunit.xmlRun WordPress coding standards:
docker exec -w /var/www/html/wp-content/plugins/mail-system php composer phpcsAuto-fix coding standards violations:
docker exec -w /var/www/html/wp-content/plugins/mail-system php composer phpcbfCompile .po translation files to .mo:
docker exec -w /var/www/html/wp-content/plugins/mail-system php composer translationsThere is no frontend build step. Admin CSS and JavaScript live directly in admin/css and admin/js.
mail-system.php Plugin bootstrap and activation hooks
includes/class-activator.php Activation: table creation and cron scheduling
includes/class-mskd-deactivator.php Deactivation: cron teardown
includes/Admin/ Admin menu, controllers, assets, and notices
includes/services/ Business logic: email, subscriber, list, queue, SMTP, import/export
admin/partials/ WordPress admin templates
admin/css/ Admin styles
admin/js/ Admin screens and interactions
admin/editor/ Visual email editor assets
public/ Front-end shortcode rendering
tests/ PHPUnit test suite
languages/ POT, PO, and MO translation files
Generate the translation template:
docker exec -w /var/www/html/wp-content/plugins/mail-system php wp i18n make-pot . languages/mail-system.pot --exclude=vendor,node_modules --allow-rootCompile a .po file to .mo after translating:
msgfmt -o languages/mail-system-{locale}.mo languages/mail-system-{locale}.poBundled translations: English (en_US, default), Bulgarian (bg_BG), German (de_DE).
Uninstalling the plugin drops all custom tables (mskd_subscribers, mskd_lists, mskd_subscriber_list, mskd_queue), deletes stored options, and clears the scheduled cron event.
Mail System is licensed under GPL-2.0-or-later.
Copyright (C) 2026 Katsarov Design.