Repository navigation
wordpress.org readiness + CI/release pipeline - #1
Merged
Merged
Conversation
Plugin: - Unique prefixes (outbox_mail_log / OutboxMailLog) as required by the plugin review - All SQL identifiers via $wpdb->prepare() %i, direct-query use documented - Bundled translation only as fallback to translate.wordpress.org packs - Sender taken from the From header for API mailers (pre_wp_mail) - Remote-content notice no longer triggered by plain links - Full wordpress.org readme, directory icon/banners/screenshots Pipeline (.github/workflows/ci.yml, release.yml): - php -l 7.4-8.5, PHPCS WordPress-Extra + VIP + PHPCompatibility, PHPStan level 8 - PHPUnit + Brain Monkey, WordPress Playground integration self-test and Playwright browser tests on oldest and newest PHP/WP - Official Plugin Check action + pinned Plugin Check PHPCS rules - ESLint no-unsanitized, gitleaks, composer/npm audit, actionlint, zizmor - Reproducible, verified release ZIP; tag release to GitHub and wordpress.org SVN Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
AccessLint reviews pull requests for accessibility issues before they ship. Reviews on private repositories need a paid plan, but this pull request gets a review on us. Results will appear here shortly, and new pushes to this pull request will be re-reviewed so you can see fixes confirmed. Keep automated WCAG reviews running on this repo: https://app.accesslint.com/accounts/lcswll?source=payment_required |
- "OUTBOX" is a registered software trademark (Outbox Technology CRB); guideline 17 forbids slugs starting with third-party trademarks. New name/slug: Mailspur – Email Log / mailspur-email-log, prefix mailspur. - Plugin URI points to the public repository; repo-checks --online now verifies every URI in the header and readme (the old one was a 404). - Admin tab navigation fully escaped (no bare echo of conditionals). - debug_backtrace() documented as a feature (source detection). - Readme: "What makes it different" section (guideline 18) and source link (guideline 4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Linux the Playground CLI can stay alive after run-blueprint finished, which let the CI job run into its 30-minute limit without any output. The run now ends as soon as selftest.php has written its report, has a hard timeout and prints the Playground output in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- CI e2e ran into the 30-minute limit: Playground's php-wasm asyncify build
crashes on Node 22 with PHP 7.4 ("RuntimeError: unreachable") and the CLI
then hangs. Reproduced on Linux (WSL); green on Node 24. CI uses Node 24,
e2e.mjs refuses older Node, job limit lowered to 15 minutes.
- Preview: remote src/srcset/background/poster, <link href>, CSS url() and
@import are defused before rendering while remote content is blocked, so
the browser never even attempts the request; the CSP stays as second layer.
Linux Chromium had attempted the tracking-pixel request (blocked by CSP).
- Plugin Check action may post its PR comment (pull-requests: write, job only).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
✅ WordPress Plugin Check Report
📊 ReportAll checks passed! No errors or warnings found. 🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check |
…inks stay errors gnu.org dropped the request from the GitHub runner (fetch failed), which failed the readiness job although the License URI is fine. Now: 3 attempts with a 15 s timeout; HTTP 4xx/5xx still fails, an unreachable host is a warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Read-only, resumable import of existing logs into Mailspur (Settings → "Import from other plugins"), so a switch keeps the history: WP Mail Logging, Email Log, Check & Log Email, FluentSMTP, Post SMTP, SureMail, WP Mail Catcher, WP Mail Log. - One Source class per plugin, mapped to the format each plugin actually writes (research of current code): site-local vs UTC vs "local epoch" timestamps, WP Mail Logging's literal ",\n" lists, esc_html'd subjects, serialized FluentSMTP/SureMail/Post SMTP columns, status/error semantics. - Foreign serialized data is unserialized with allowed_classes=false (no PHP object injection), truncated data is handled. - Batches of 250 via REST (admins only), per-source lock, stored progress: re-running only adds new rows, undo removes an import. - Entries older than the retention period are skipped; secrets are redacted like for logged mails (shared Redactor). - List sorted by send date (created_at) instead of id, retention/max-entries cleanup delete by date – imported old entries get new ids. New indexes (status, created_at) and (source); DB version 2. - Tests: 10 mapping unit tests, Playground integration test with the real WP Mail Logging and Email Log plugins plus fixture tables for the others (42 assertions incl. time zones, no duplicates, read-only, undo, retention), browser test of the import UI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mails logged by two plugins in parallel, or already logged by Mailspur, were imported again. Before each insert the importer now looks for an entry from ANOTHER source with the same recipient set and subject sent within ±120 s (indexed created_at window + LIKE pre-filter, comparison in PHP that tolerates truncated / entity-escaped subjects and display names). Mailspur's own entry always wins; rows of the same source are never merged. Duplicates are counted and shown separately; window filterable via mailspur_import_duplicate_window (0 disables). Tests: 6 unit tests, integration test with the real WP Mail Logging and Email Log logging the same mails Mailspur logs (three copies → one entry, undo + re-import from the other plugin). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Module registry (src/Modules.php, Mailspur\Module) loaded first in boot.
- Logger: mailspur_meta filter in phases capture/phpmailer/result (+ import),
mailspur_finalize_row before the single final UPDATE, mailspur_logged action;
a failing module never loses the status update.
- Schema v3: meta (JSON), notes, size, raw columns; status HELD (3) for mails
deliberately not delivered (staging).
- REST: mailspur_rest_summary / mailspur_rest_item payload filters, meta in detail.
- Admin: tab registry + mailspur_render_tab_{key}, mailspur_admin_enqueue,
settings defaults/sanitize filters, settings sections and after-form actions.
- admin.js: window.mailspur API (tabs, row decorators, details, dialog actions,
list listeners); list renders on DOMContentLoaded after module scripts.
- Translations per module in i18n/<locale>/*.json; e2e auto-discovers
tests/e2e/features/*.php and tests/e2e/seed.d/*.php.
- Uninstall/deactivation clean every mailspur_* option, transient, cron hook
and table generically.
- docs/MODULES.md documents the contract.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Static rule engine (mailspur_finalize_row, also for imports): HTML in text/plain, Gmail clipping, relative URLs, dev/staging hosts, unreplaced placeholders, mojibake, sender problems, bulk without List-Unsubscribe, recipient domain typos, link text/target mismatch, subject and alt checks. Codes + params in meta "notes", count in the notes column. Extensible via mailspur_note_rules / mailspur_note_texts. - Dynamic checks when an entry is opened: MX/null MX per recipient domain (cached 1 day, filter mailspur_notes_mx_lookup) and repeated sending. - Error explanations for PHPMailer, SMTP reply codes and API mailers. - UI: severity badge in the list, error hint, Notes dialog tab, explanation with steps under the error; settings to disable and ignore codes. - German translations, unit tests, Playground feature test, Playwright spec. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Sender check (SPF, DKIM, DMARC, MX) via dns_get_record() for the default sender and the most frequent From domains of the log, traffic lights + suggested records, cached 1 h (admin REST route). - Staging mode: off / hold (log only) / redirect to test addresses; held entries get status held, redirected entries keep the original recipients in meta; admin bar item and Mail Log notices; suggestion on non-production sites (dismissible). - Dialog actions "Send now" (release a held mail, bypassing staging once) and "Send to...". - Rest::resend() accepts an optional validated "to" (array or comma list, max. 10, admins only). - Unit, Playground integration (tests/e2e/features/delivery.php) and Playwright tests; German strings. - ui.spec.js: wait for import.js's reload before navigating (race under load, net::ERR_ABORTED). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collects per mail (meta.trace): timeline (capture / phpmailer_init / result), transport (mailer, SMTP host/port/encryption/auth, masked username; API handler behind a pre_wp_mail short-circuit), origin (file:line, function, hook stack), request context (type, method, path without query, user) and an optional SMTP transcript with masked credentials and omitted message data. "Download .eml" in the dialog: exact raw MIME source when the opt-in setting is on (stored gzip+base64 in the raw column), otherwise a marked reconstruction from the logged, redacted fields. Streamed as message/rfc822 through REST (rest_pre_serve_request). Includes unit tests, a Playground integration test and a Playwright spec. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ation - List filters for source (GET /sources with labels, cached), format (HTML/text), attachments and notes; part of the URL state, behind a 'More filters' toggle. Repository::filter() shares the WHERE clause between list, export and CLI. - Streamed, keyset-paginated CSV/JSON export of the current filter (admin-post, nonce, view capability), formula-injection-safe CSV with BOM. - wp mailspur list|show|resend|stats|purge|export|import (logic in Commands). - Anonymise entries after N days on the daily cleanup (before deletion), masked recipients, marker in meta; badge and dialog note in the log. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Integration fixes: - Staging hold now runs first on pre_wp_mail (PHP_INT_MIN), so API mailers hooked there see the mail as handled and do not deliver on staging copies; covered by a new integration check. - One shared WP_REST_Request/WP_REST_Response test stub (union of the two module copies), loaded by tests/bootstrap.php. - Module list and generated translations merged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…idget, monitoring alerts - Statistics tab: range presets + custom range, KPI tiles with deltas, stacked daily/weekly volume chart, failure-rate line, weekday x hour heatmap, top sources/domains/subjects linking into the filtered log. Hand-rolled SVG, keyboard + table views, no library. - GET /stats: covering-index aggregation in UTC hours, converted to local days with wp_timezone() (DST-safe), bounded top-list sample, transient cache with generation-based invalidation. - Dashboard widget (last 7 days, server-rendered SVG). - Opt-in alerts: failure spike and silence (14-day baseline), email + webhook (Slack/Discord/JSON via wp_safe_remote_post), cooldown, recovery, history, test alert; WP-Cron every 15 minutes, cheap counter at send time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Core resend refuses anonymised entries (409) – the UI hid the button, the REST route did not; covered by the workflow integration test. - Statistics: top-sender bars link to the log's new source filter. - readme.txt: all module features, privacy note for opt-in webhooks, FAQ, six screenshot captions. - Directory screenshots: log with note badges, preview, trace of a real web request (lost-password form), notes + explained error, statistics with 30 days of on-demand history (tests/e2e/screenshot-seed.php, never used by the specs), settings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The value is a fixture: the test asserts it never appears in the SMTP transcript. Documented in .gitleaksignore (history) and inline (future). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chromium on Linux logs "Blocked script execution in 'about:srcdoc'" when a sandboxed preview contains scripts – that is the protection working. The notes/trace specs already ignored these; delivery, insights and workflow now do too (delivery failed on CI because of it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…output - Dashboard widget chart is printed through wp_kses() with an explicit SVG/table allowlist instead of a "built from escaped parts" ignore. - .eml download: documented why the body is not escaped (file download, message/rfc822 attachment) and send X-Content-Type-Options: nosniff. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
%iidentifiers in all SQL, full readme, directory assets (icon, banners, screenshots), translation fallback instead ofload_plugin_textdomain().pre_wp_mail), remote-content notice no longer triggered by plain links.ci.yml): php -l 7.4–8.5 · PHPCS WordPress-Extra/VIP/PHPCompatibility · PHPStan level 8 · PHPUnit · Playground integration self-test + Playwright browser tests (PHP 7.4/WP 6.5 and PHP 8.4/latest) · official Plugin Check action + pinned Plugin Check PHPCS rules · ESLint no-unsanitized · gitleaks · composer/npm audit · actionlint + zizmor · reproducible, verified release ZIP.release.yml): tagvX.Y.Z→ full CI → GitHub release with ZIP + SHA-256 → wordpress.org SVN deploy (off untilWPORG_DEPLOY=true+ SVN secrets).Verified locally
npm run verifyall green; e2e on PHP 8.3/WP 7.1 and PHP 7.4/WP 6.5 (45 integration assertions, 7 browser tests); Plugin Check 0 findings; actionlint/zizmor/gitleaks clean.🤖 Generated with Claude Code