Issue 214 Workflow Contains Ignored Capitals - edmozley/freeitsm GitHub Wiki
A workflow "contains" condition did not match capitals
Reported: #214 by duongtuanan, October 2026 · Module: Workflows · Fixed in: 4.3.0 (changelog #2647)
What you saw
A workflow on ticket.created with the condition Requester email contains abc.com reported Skipped (conditions did not match) for a ticket from somebody at abc.com.
What was actually wrong
Two things look the same in the run list, and only one of them is a bug.
Not a bug: the other tickets. Every new ticket starts a run of every ticket.created workflow. A ticket from any other domain is correctly skipped, and its run is listed with the same words. On a busy desk the list is mostly skipped runs, and that is expected. Open a run to see why: each condition is shown with the Actual value it compared against.
The bug: capitals and stray spaces. The test was PHP's str_contains():
case 'contains': return is_string($actual) && is_string($value) && $value !== '' && str_contains($actual, $value);
str_contains() is exact, so:
| Requester | Value typed | Old result |
|---|---|---|
[email protected] |
abc.com |
match |
[email protected] |
abc.com |
no match |
[email protected] |
ABC.com |
no match |
[email protected] |
abc.com (pasted, with a space) |
no match |
The address in the payload is the one the email arrived with, capitals and all, and Outlook and Exchange commonly send [email protected]. An email domain has no case: ABC.com and abc.com are the same address. So the condition could fail on exactly the ticket it was written for.
🔑 It was not only email. A subject condition contains
urgentdid not fire for URGENT: printer down. Any text condition behaved this way — the email case is just where it is most plainly wrong.
The fix
One function, used by both contains and does not contain:
private static function textContains($actual, $value): bool
{
if (!is_string($actual) || !is_string($value)) return false;
$value = trim($value);
return $value !== '' && mb_stripos($actual, $value) !== false;
}
mb_stripos, notstripos: themb_version knows the capitals of other alphabets, soärgermatchesÄrger.- The typed value is trimmed. The payload's own value is not: what arrived is what arrived.
- A blank value still matches nothing, as before.
Why not a setting? Mail rules in Outlook and Gmail match this way, and nobody writing contains urgent means "but not URGENT". A case-sensitive option would be a setting whose only use is to rebuild this bug.
What is unchanged: equals and is one of are still exact. They compare ids as well as text, and changing them is a separate decision.
📁 Files changed
| File | Change |
|---|---|
🟦 workflow/includes/engine.php |
textContains(); contains / not_contains use it |
🟩 tests/workflow-contains.php |
new, 11 checks |
🟨 lang/en/workflow.php |
the Conditions help says so |
How it was proved
tests/workflow-contains.php calls the engine's own evaluateOperator() with the cases above, plus a subject in capitals, non-English capitals, a missing value and a blank value. With str_contains() put back, 6 of the 11 checks fail; with the fix, all pass.
Before changing behaviour that existing workflows rely on, every contains condition in a real install's run history was re-checked with the new rule: 78 conditions, none would have come out differently.
Not proven
- The reporter's own run. Opening it would show the Actual value; the cases above are the ones that produce the report, but which one they hit is not known.