joeyk
March 18, 2026, 2:59pm
1
My idea is:
Add option to test email opens right from the contact timeline.
Show some kind of overlay on tracked pages if the person who views it is a Mautic admin
I think these groups of people would benefit from this idea:
Marketers, especially first time users of Mautic who don’t EXACTLY KNOW how Mautic works.
Why I think they would benefit from this idea:
If you don’t know how Mautic works if you are a Mautic admin in the same browser, you might think tracking is not working properly. Same with using a web based email tool. You might not know that your email opens are not registered. It is pretty time consuming to test a campaign if it relies on opens and you have to jump between windows.
Any code or resources to support this idea:
5.x ← jos0405:feature/email-timeline-test-open
opened 01:55PM - 18 Mar 26 UTC
Adds a Test Open button to Email Sent timeline entries (is_read=0). Calls EmailM… odel::hitEmail() with the Stat entity directly, identical to a real tracking pixel hit, so campaigns, points and webhooks all fire.
No subscriber changes needed - item.id and item.isRead are already present in the extra.stat array passed to the timeline template.
| Q | A
| -------------------------------------- | ---
| Bug fix? (use the a.b branch) | ❌
| New feature/enhancement? (use the a.x branch) | ✔️
| Deprecations? | ❌
| BC breaks? (use the c.x branch) | ❌
| Automated tests included? | ❌
| Related user documentation PR URL | mautic/user-documentation#...
| Related developer documentation PR URL | mautic/developer-documentation-new#...
| Issue(s) addressed | Fixes #...
# feat(EmailBundle): add "Test Open" button to contact timeline
## Summary
Adds a **Test Open** button to `Email Sent` entries in the contact timeline
when the email has not yet been opened (`is_read = 0`).
Clicking it calls `EmailModel::hitEmail()` with the `Stat` entity directly —
the exact same codepath as a real tracking pixel hit — so all downstream
events fire correctly: campaign branches, point triggers, webhooks, and
`email_stats` open details.
## Motivation
- QA / testing campaign branches that depend on email opens without needing
a real mail client to open the email
- Demonstrating Mautic behaviour to clients
- Debugging tracking pixel issues (confirms the open pipeline works end-to-end)
## Files changed
### `app/bundles/EmailBundle/Config/config.php`
New GET route `mautic_email_test_open` declared before the `{objectAction}`
wildcard so Symfony's router matches the literal `testopen` segment correctly.
### `app/bundles/EmailBundle/Controller/EmailController.php`
New `testOpenAction(int $objectId)` method:
- Checks `email:emails:edit` permission
- Fetches the `Stat` entity by PK
- Guards against already-read stats (returns a notice flash, no double-fire)
- Builds a minimal synthetic `Request` with `IP: 127.0.0.1` and
`User-Agent: Mautic/TestOpen`
- Calls `$model->hitEmail($stat, $fakeRequest, false, false)` — identical to
a real pixel hit, fires all downstream events
- Redirects back to the contact view with a success flash
### `app/bundles/EmailBundle/Resources/views/SubscribedEvents/Timeline/index.html.twig`
Adds a confirmation-dialog button at the bottom of each `Email Sent` entry
where `item.isRead` is falsy. Uses `data-toggle="confirmation"` and
`data-confirm-callback="executeAction"` — consistent with other action links
in Mautic (e.g. dashboard delete).
### `app/bundles/EmailBundle/Translations/en_US/messages.ini`
Four new keys:
- `mautic.email.timeline.test_open`
- `mautic.email.timeline.test_open.confirm`
- `mautic.email.timeline.test_open.success`
- `mautic.email.timeline.test_open.already_read`
## Implementation notes
- No subscriber changes required — `item.id` and `item.isRead` are already
present in the `extra.stat` array passed to the timeline template.
- `$activeRequest = false` in the `hitEmail()` call bypasses the
session-based duplicate-open guard, which would otherwise block a
backend-initiated open.
- The button disappears after a real or simulated open (condition on
`item.isRead`), so it cannot be accidentally fired twice.
- Failed sends (`item.isFailed`) are excluded from showing the button.
- No CSRF token needed — consistent with other Mautic confirmation-dialog
action links.
## Testing steps
---
### 📋 Steps to test this PR:
1. Open this PR on Gitpod or pull down for testing locally (see docs on testing PRs [here](https://contribute.mautic.org/contributing-to-mautic/tester))
1. Send a test email to a contact via **Send Example**. Open the contact timeline — expand the `Email Sent` entry
3. **Test Open** button is visible
4. Click → confirmation dialog → confirm
5. Flash message: *"Email open event simulated successfully."*
6. Timeline now shows an `Email Read` entry
7. Campaign actions / points tied to email opens have fired
8. The **Test Open** button is gone from the `Email Sent` entry
9. On an already-opened entry — button does not appear
10. On a failed send entry — button does not appear
Are you willing to work on this idea?
Yes, see the PR above (it is for M5, but can be rebased on M7) if it gains tracktion.
What skills and resources do you need to explore this further?
I need the opinion of the community.
Here is a bit broader description. This PR adds 2 things:
Test Email Open button in the contact timeline:
Once you click on it, you’ll see a confirmation window:
If you click on YES, the event registers, like a person would have opened the email. Test event is recorded, and the campaign check for email open passes:
This allows you to test a campaign properly WITHOUT need to juggle with multiple browser windows and sessions.
Overlay is displayed if you are a Mautic admin with the same session. A little warning, that your actions won’t be tracked. It looks like this:
Any comment and suggestion is appreciated.
Joey