-
released this
2026-05-23 23:54:14 +02:00 | 67 commits to main since this releaseNewsletter alpha (still alpha — needs field testing)
Step 9 — tracking pipeline. Every outbound newsletter gets a per-recipient rewrite pass before send: image URLs and link hrefs are routed through HMAC-signed REST endpoints (
/wp-json/lrob-etk/v1/nl/track/{img,click}/<token>). Newsletters without any<img>get a 1×1 open pixel appended. Test sends (separate code path) bypass the pipeline entirely.Counters. Open + click events bump per-recipient (
newsletter_recipients.opens/clicks) + per-newsletter aggregate (opens_count/unique,clicks_count/unique) + per-subscriber lifetime stats (total_opened/clicked,last_engagement_at,sends_since_engagement). Mirror keys onwp_usersuser_meta for WP-user recipients.Cold-subscribers sub-tab in Subscribers admin filters by
sends_since_engagement >= threshold(default 5). Settings page exposes the threshold + an opt-in to count opens as engagement (off by default — Apple MPP server-side image loads inflate opens to ~100%). Daily retention cron prunestracking_eventspastlrob_etk_nl_tracking_retention_days(default 365).Also in this release
- Newsletter list polish from v0.3.1: sub-tabs (In preparation / Sent / Trash) with live counts, trash system (
wp_trash_post+ Restore + Empty-trash), recipients drawer (paginatednewsletter_recipientsview with status-filter chips + email/name search + cross-link to Logging entries on failed rows). Newly-created newsletters sort to the top. - Display fix:
NewsletterRepository::list_allnow selects the opens/clicks counter columns so the card actually shows what the tracking endpoint records. - fr_FR translations complete (1142 strings).
Still alpha — please report any issue on GitHub.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Newsletter list polish from v0.3.1: sub-tabs (In preparation / Sent / Trash) with live counts, trash system (
-
released this
2026-05-23 12:00:01 +02:00 | 69 commits to main since this release🚧 Alpha — do not use in production yet. The Newsletter module is feature-complete enough to test end-to-end but hasn't seen real-world send loads. Field testing welcome; feedback / bug reports via GitHub issues.
What's in this release
The Newsletter module ships with everything needed to actually run a campaign on a small list:
- Subscribe forms with double-opt-in confirmation, RFC 8058 one-click unsubscribe, captcha + honeypot + time-trap, customisable templates.
- Categories + Lists: opt-out by category at the recipient level; manual lists for targeting.
- Per-recipient preferences page (public token URL + WP-profile section + Gutenberg block + shortcode).
- Onboarding templates (confirmation / reminder / refuse-acknowledgment) edited in the block editor with a token registry.
- Trash + refuse polish for subscribers.
- Newsletter authoring via inline cards on the Newsletters admin view — every newsletter's settings, audience, sender identity, schedule, and send actions live on its card. Gutenberg is content-only. Send button is state-aware: "Send now" ⇄ "Schedule" ⇄ "Unschedule" (red).
- Send pipeline with chunked recipient materialization, claim-based send loop (race-safe between AJAX and Cron), per-recipient status snapshot, real-time progress bar.
- WP-Cron safety-net + pause/resume/abort + scheduled-send cron handoff.
- SMTP circuit-breaker: consecutive-failure threshold trips the newsletter to `paused` with a red banner explaining "fix SMTP then Resume". "Retry failed (N)" bulk action re-queues failed recipients for another attempt.
- Logging integration: failures are kept in the logs table, successes are pruned by default (data already lives in `newsletter_recipients`), per-newsletter "Log every send" override. Logs page has a tri-state Newsletter filter. Recipients drawer cross-links to each failed row's log entry.
- WP-Cron health diagnostic at the bottom of the Newsletters view: last observed tick, next scheduled tick, `DISABLE_WP_CRON` flag, colour-coded verdict, manual Refresh button + opt-in 10s auto-refresh toggle. Tooltip explains what a cron is for non-technical users.
- Live clock-tick + adaptive server-poll on the cards: relative-time displays update every second when a deadline is imminent, fall back to minute/hour cadence further out. Server-poll runs every 10s only when an interesting card exists; pauses when the tab is hidden; localStorage dedup so multiple tabs don't multiply server load.
- Inline LRob tip when the site relies on pseudo-cron, suggesting a server cron for reliability.
Other changes
- Logging module schema migration v1→v2 (renamed reserved `campaign_id` → `newsletter_id`).
- French translation sweep — 1113 entries, only the .po file header left untranslated.
- `release.sh` polish: JS syntax check via `node --check`, dead-CSS scan with peel-once heuristic, per-language msgfmt stats, file-type + LoC stats.
Coming next
- Step 9 — Tracking: image-rewriter (primary open signal via media URLs) + GIF fallback for image-less emails + link rewriter for clicks + per-subscriber lifetime stats + "Cold subscribers" sub-tab with optional auto-cleanup.
- Step 10 — Bounce handling.
- Step 11 — Import wizard (CSV / JSON full-restore / Newsletter-plugin importer).
Install
- Download `lrob-email-toolkit-0.3.0.zip` below.
- Upload via WP admin → Plugins → Add New → Upload Plugin.
- Enable the Newsletter module under Email Toolkit. (Requires SMTP module to be enabled for actual sending.)
The auto-updater (shipped in 0.2.0) will pick this release up automatically on existing installs within an hour.
Requires
- WordPress 6.0+
- PHP 8.1+
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
released this
2026-05-22 02:20:38 +02:00 | 92 commits to main since this releaseFoundation work for the upcoming Newsletter module: the Contact Form's form-builder (drag-and-drop editor, structure normalisation, field renderers, anti-spam wiring) has been extracted from
src/Modules/ContactForm/into a new sharedsrc/Forms/namespace. Newsletter (landing in v0.3.0) will mount the same editor with its own field types and CPT — zero JS duplication.Plus the small WP 7.0 cosmetic fix that was sitting on
mainbetween v0.2.0 and now.Form-builder refactor
- New
LRob\EmailToolkit\Forms\namespace undersrc/Forms/:FormStructure,FormContext,FormEditorRenderer,FieldRenderHelpers,FieldTypeInterface,FieldTypeRegistry, plus the 10 stock field types (Text, Email, Textarea, Number, Phone, Date, Select, Radio, Checkbox, Submit) underFields/. FieldTypeRegistryis keyed by CPT slug — each consumer module declares its allowed types for its own form CPT. Contact Form keeps all 11 of its types (including its module-specific captcha); Newsletter will register a smaller set plus its own picker types.Plugin::boot()wires the registry into the container before module boot, so modules can register types immediately.FormContextis host-neutral:start($form_id, $instance, $name_prefix, $id_prefix, $editor)accepts module-specific prefixes. Contact Form passeslrob_etk_cf/lrob-etk-cf; Newsletter will passlrob_etk_nl/lrob-etk-nl.- Contact Form's captcha field is now
src/Modules/ContactForm/Fields/CaptchaField.php— captcha routing stays tied to its module's meta key + Captcha-module context.
Asset rename + CSS namespace flip
- Editor JS renamed
admin/js/contact-form-fields-editor.js→admin/js/form-fields-editor.js(handle:lrob-etk-form-fields-editor). - Form-builder DOM classes renamed
lrob-etk-cf-*→lrob-etk-form-*(form wrapper, row/col/field, label/helper/error, overlay/insert/edit-shell, options, submit, required toggles, inline-settings strip, type picker, preset modifier — about 35 classes across CSS/JS/PHP). - Module-specific admin chrome keeps its own prefix: Contact Form's form cards, recipients picker, modals, submissions panel etc. stay
lrob-etk-cf-*; Newsletter's admin chrome will belrob-etk-nl-*. Per-module captcha field types keep their own CSS namespace too (lrob-etk-cf-captcha-*for Contact Form).
Other
- WP 7.0 button line-height (the 0.2.1 fix bundled into this release): override the new core rule
.button .dashicons { line-height: 1.9 }so toolkit buttons with leading icons keep their vertical centering.
Design spec
newsletter.mdat the repo root documents the full Newsletter module design — data model, send pipeline, tracking, import/export, system email templates as a CPT, scale targets, implementation slicing. Referenced fromCLAUDE.md. Worth reading if you want a preview of what v0.3.0 will land.No behavior change
Existing Contact Forms continue to work unchanged. No DB migration. CSS rename is a like-for-like flip with no functional impact. Form-builder editor, frontend rendering, submissions, anti-spam, captcha — all behaviorally identical.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- New
-
released this
2026-05-21 03:06:05 +02:00 | 96 commits to main since this releaseFirst release with self-hosted plugin updates: install once, get future versions through the WordPress Updates page like any other plugin.
This release also closes out the Contact Form story with a full submissions inbox, privacy/retention controls, and the cascade-delete modal.
Headline
- Self-hosted GitHub-release updates.
src/AutoUpdate/Updater.phphookspre_set_site_transient_update_plugins+plugins_apito surface new releases of this plugin as standard WordPress updates. 1-hour cache, with the cache bypassed when the admin is on the Updates page or clicks "Check again" — so explicit update checks always hit GitHub directly. Mirrorswp-lrob-calendar's pattern.
Contact Form submissions inbox
- Inbox view at Contact Forms → "View submissions" — filter by form / status (delivered / received / spam-blocked / failed) / captcha outcome / date range / free text. Read-only detail view with humanized field labels and a collapsed technical metadata section (IP, UA, referer, log link).
- Cross-links both ways with Email Logs. Log rows whose source is a contact form now expose a "View submission" icon; submission detail links back to the outbound email log.
- Form card stat pill ("N received · M blocked →") and a global stats panel at the bottom of the Forms page.
- Dashboard tiles: Delivered (30d) and Spam blocked (30d), the latter split into honeypot vs captcha.
Privacy & retention (new "Storage" header button)
- Save submissions toggle — global default + per-form tri-state override. When off, the notification email still goes out; captcha and honeypot still run; no row is written and the form drops out of the inbox.
- IP storage — hashed by default for privacy/GDPR friendliness, with an opt-in "Store raw IP" mode for sites that need it for abuse investigation.
- Retention — separate windows for delivered submissions (default: forever) and spam-blocked rows (default: 90 days). Daily cron purges older rows.
- Form delete cascade modal — replaces the bare confirm() with three explicit choices: cancel / delete form only (orphaned submissions stay in the inbox labelled "Deleted form #N") / delete form + N submissions.
Captcha analytics
- Per-route counters.
CaptchaService::verify()upserts a small(day, route_key, outcome)stats table on every check. Surfaces as 30-day "blocked · passed" pills next to each challenge and identity on the Captcha settings page.
Architecture
- View-routing for hidden admin pages. Both the Submissions inbox and the Plugin Data page are now sub-views of their natural parent (
?page=lrob-etk-cform&view=submissionsand?page=lrob-etk&view=data) — no separate submenu, no CSS-hide. Replaces theadd_submenu_page+remove_submenu_pagepattern which broke direct-URL access on a 403 (get_admin_page_cap()walks$submenufor the cap lookup). - French translations filled in for all the new strings.
Upgrade
Drop-in. The Contact Form schema gains an additive
ip_addresscolumn (default empty string — privacy-first); existing rows untouched. Captcha schema gains a small aggregate stats table. Both migrations are idempotent dbDelta + safe to roll back.If you're using the toolkit in production, this is the first release where you can stop manually re-uploading the zip — the next bump will arrive through Dashboard → Updates.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- Self-hosted GitHub-release updates.
-
v0.1.2 — security audit follow-ups Pre-release
released this
2026-05-20 17:51:20 +02:00 | 101 commits to main since this releasePre-release. Hardening follow-ups from the post-public-release security audit. No data migrations, no schema changes — safe to upgrade in place.
What changed
- Captcha fails closed on misconfiguration. When a configured captcha route can't resolve (deleted identity, inactive identity, AUTH_KEY rotated since credentials were saved), submissions are now rejected with a clear error message instead of silently passing — the previous behaviour let bots bypass a captcha the admin thought was active.
- Admin notice for broken captcha routes. A persistent warning banner now appears on every admin page when any routing-map entry can't resolve, with a one-click link to the Captcha settings page. Admins learn about misconfiguration immediately instead of via support tickets.
- Defense-in-depth CR/LF stripping on email resend.
Resender::build_headers()now sanitises every stored header component before reconstructing outbound headers. Not exploitable today (PHPMailer normalises on the way in) but the same code path will be reused by the upcoming IMAP-save / mail-receive features which will introduce attacker-controlled data into those columns — closing the vector now.
Upgrade
Drop-in. No DB changes, no settings changes, no feature toggles.
If the upgrade surfaces a "Captcha misconfiguration" admin notice immediately after activation, that's the new health check doing its job — open the Captcha settings page and either re-pick a working challenge or re-enter the affected provider's credentials.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
v0.1.1 — public beta Pre-release
released this
2026-05-20 16:24:37 +02:00 | 102 commits to main since this releaseFirst public pre-release. Three modules production-usable today, schema may still evolve before 1.0.
Shipped
- SMTP — multi-identity routing, per-source rules, encrypted credentials, native
mail()fallback, default TLS/465. - Email Logging — log/search/filter/resend, retention, dashboard activity charts.
- Contact Form — from-scratch WYSIWYG editor (drag/drop, columns, inline settings, autosave, undo/redo), starter templates, stacked anti-spam (honeypot + time-trap + rate-limit + captcha), submission logging.
- Captcha (service module, always on) — per-context routing with multi-identity per provider. Built-in: math + picture-recognition. Hosted: hCaptcha. Turnstile / reCAPTCHA designed to plug in.
Languages
- 🇬🇧 English
- 🇫🇷 French — 100% coverage
Install
- Download
lrob-email-toolkit-0.1.1.zipbelow. - WordPress admin → Plugins → Add New → Upload Plugin → Activate.
- Email Toolkit → enable the modules you need (all off by default).
Coming next
- IMAP "Save to Sent" archive (v0.2)
- Newsletter module (v0.3)
- Cross-feature captcha for comments / newsletter signup / lost password / registration (v0.5)
- Webhook Integrations module (v0.6)
- Public auto-update via GitHub releases (v1.0)
Notes
- Pre-1.0 — migrations between versions are idempotent, downgrades are not supported.
- SMTP / captcha credentials encrypted at rest with AES-256-GCM (derived from
AUTH_KEY). ConfigureAUTH_KEYinwp-config.phpbefore saving any secret. - Requires PHP 8.1+ / WordPress 6.0+.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
- SMTP — multi-identity routing, per-source rules, encrypted credentials, native