Skip to content

Issue 214 Workflow Contains Ignored Capitals

Ed Mozley edited this page Oct 27, 2026 · 1 revision

A workflow "contains" condition did not match capitals

Reported: #214 by duongtuanan, October 2026 Β· Module: Workflows Β· Fixed in: the release after 4.2.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
user@abc.com abc.com match
User@ABC.com abc.com no match
user@abc.com ABC.com no match
user@abc.com 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 John.Smith@ABC.com. 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 urgent did 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, not stripos: the mb_ version knows the capitals of other alphabets, so Γ€rger matches Γ„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.

Related

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally