Corndog for Craft CMS

Usage

The log

Corndog → Email log is the whole plugin in one screen: a thirty-day summary, a per-day chart of sent, held and failed, and the messages themselves, most recent first.

Filter by status, by sender, or by free text — the search reaches the subject, every recipient, the sender address and the error text, so "find the email we sent to this customer" is one box.

Each row shows a status light, the subject, who it went to, which code sent it, and the tracking counts if the message carries any:

StatusMeans
SentThe transport accepted it. Not the same as delivered — no plugin can tell you that.
FailedThe transport refused it, or threw. The reason is on the row.
Not sentHeld by Corndog, because stop sending is on.
UnfinishedThe process died part way through the send.

Export CSV exports whatever the filters are showing, up to 10,000 rows. Delete on a row removes it; the button at the bottom of the list deletes everything the current filters match — so on a screen filtered to failures, it clears the failures and nothing else.

One message

The message screen has three parts.

What was in it — recipients, cc, bcc, reply-to, subject, size, and attachment names, types and sizes. Corndog records what was attached; it never stores the file.

Where it came from — the part the log exists for:

  • SenderCraft Commerce, Formie, System message: forgot_password, Template: newsletter/_weekly.twig
  • Called from — the class, method, file and line
  • Queue job — when the send happened in the queue, the job class
  • Context — web, console or queue
  • Transport and Message-ID — what carried it, and the string to give a host when you ask them what happened to it
  • Triggered by, Request and IP — who was logged in, and what URL or console command they were on

When a customer gets two order confirmations, this is the field that says one came from Commerce and the other from a form notification.

What was sent — the rendered body in a sandboxed frame, the HTML source, the plain-text part and the headers. The frame is sandboxed and served with its own content-security policy, because an email is a whole HTML document written by somebody else and it has no business restyling your control panel. This section needs the Read message bodies permission.

Resending

Resend… opens the message as a form. Change the recipient, the subject or either body, and send it again. The resend is logged like any other message and links back to what it was a resend of.

Two things it will not do: resend attachments, which were never stored, and resend a message whose body was not kept, which it refuses rather than sending a blank email to a customer.

Sending a test

Corndog → Send a test sends a real message through the site's real mail settings, with any extra headers you want on it — X-PM-Message-Stream, X-Mailgun-Tag, a custom Reply-To.

Craft's own test-email button answers one question: did the transport accept a message addressed to you. This one answers the question after it, because the test lands in the log with its transport, its headers, its Message-ID and, if it failed, what the server said.

Tracking

With trackOpens on, HTML messages get a 1×1 image before the closing </body>. With trackClicks on, http and https links are rewritten to pass through your site first. Both are off by default; see Configuration.

Each message gets its own random 32-character key, stored on its log row. A leaked pixel URL says nothing about the site, and deleting the log entry deletes the tracking with it.

Click targets are signed with your Craft security key and checked on the way back out. An unsigned or edited target is not followed — otherwise the endpoint would be an open redirect on your own domain, which is exactly what a phishing campaign goes looking for.

Left alone, always: mailto:, tel:, in-page anchors, anything matching ignoredLinkPatterns, anything carrying data-corndog-ignore, and any link still holding an unrendered Twig tag.

<a href="https://example.com/unsubscribe" data-corndog-ignore>Unsubscribe</a>

The message screen lists every open and click with its time, so you can see the sequence rather than just a number.

Read an open rate as a floor, not a measurement. Mail clients block images; some prefetch them. An unopened email and a cautious reader look identical from here.

Stopping and redirecting mail

Three switches, all in the settings, all applied before the message is captured — so the log shows what actually happened rather than what was originally intended:

  • Stop sending — everything is logged, nothing leaves the server
  • Send everything to — one address replaces every recipient
  • Blind-copy everything to — an archive copy of every message

Twig

{% for message in craft.corndog.recent(10) %}
    {{ message.subject }} → {{ message.toLine }} ({{ message.statusLabel }})
{% endfor %}

{{ craft.corndog.total({ status: 'failed' }) }}   {# how many have failed, ever #}
{{ craft.corndog.failures(60) }}                  {# failures in the last hour #}
{{ craft.corndog.summary(30).sent }}
{{ craft.corndog.daily(30)|json_encode|raw }}     {# for your own chart #}
{{ craft.corndog.message(1234).subject }}

recent() and messages() take the same options the log screen uses: status, source, search, siteId, limit, offset, sort, direction.

The list query does not select bodies — message(id) does. That is what keeps a large log a fast screen.

The dashboard widget

Email is a dashboard widget: sent and failed over a window you choose, and the five most recent messages. It is the answer to "is mail still going out" without opening the plugin.

Console

php craft corndog/email/stats --days=30   # what has gone out, and what has not
php craft corndog/email/test you@example.com
php craft corndog/email/prune             # apply the retention settings now
php craft corndog/email/prune --dry-run   # say what would go, and delete nothing
php craft corndog/email/clear             # delete the whole log

stats prints the totals, a breakdown by sender, and the five most recent failures with their reasons — which makes it the fastest way to answer "is this site's mail working" over SSH.