Issue #3586369: "Global context" checkbox description renders a literal <br> instead of a line break
Description
Fixes issue #3586369: the "Global context" checkbox on the AI Context item edit form rendered a literal <br> instead of a line break in its description.
Root cause 1
AiContextScopeGlobal::getItemFormDescription() The method cast its TranslatableMarkup return value to (string), discarding the object that implements MarkupInterface — the only thing that tells Twig's autoescape not to Html::escape() the string. Fixed by returning the TranslatableMarkup directly.
Note on scope vs. the original issue text: since #3586369 was filed, issue #3586361 (closed) renamed this method from getFormDescription() to getItemFormDescription() and promoted it from a protected, single-caller method to a public method declared on AiContextScopeInterface, now implemented by 4 plugins (AiContextScopeBase default, AiContextScopeTag, AiContextScopeTargetEntity, AiContextScopeSiteSection). So the fix's real surface is larger than the original issue's proposed patch:
AiContextScopeInterface::getItemFormDescription()— return type widenedstring→string|TranslatableMarkup; corrected a docblock that incorrectly claimed#descriptionis filtered withXss::filterAdmin()(it's actually plain Twig autoescape).AiContextScopeBase::getItemFormDescription()(default fallback) — same return-type widening, required because PHP checks return-type covariance against the immediate parent class, not just the interface (caught by PHPStan).AiContextScopeTag,AiContextScopeTargetEntity,AiContextScopeSiteSection— untouched;: stringremains a valid covariant narrowing.
Root cause 2
AiContextItemForm.php (found during manual testing, not part of the original issue) buildForm() rebuilds the Global checkbox's #description to append a global-item-limit warning. It cast the (now-fixed) TranslatableMarkup to (string) and reinserted it via a :base placeholder in a new $this->t() call. Drupal's : placeholder always runs Html::escape(), even on MarkupInterface values — unlike @/%, which skip escaping when the value is MarkupInterface. That re-introduced the same literal-<br> bug through a second path. Fixed by keeping $base_description as the original TranslatableMarkup (no cast) and switching the placeholder from :base to @base in all three warning strings.
Test changes
AiContextScopeGlobalTest::testItemFormDescriptionKeepsSaveWarning()— added a(string)cast around thegetItemFormDescription()call, since the file hasdeclare(strict_types=1)andassertStringContainsString()requires astringargument, not aTranslatableMarkupobject.
Testing instructions
- Install
ai_context. - Go to
/admin/config/ai/context/items/add(or edit any existing item). - Open the Context Scope section.
- The description under the Global context checkbox now renders both line breaks correctly instead of literal
<br>.
Checklist
- I have updated the MR title to use format:
Issue #1234: My issue title. - I have updated the MR description to include:
Closes #1234 - I have performed a self-review of my own code
- I have added or updated tests, or explained in the description why this change is not covered by tests
- I have updated documentation for any new or changed functionality
- I have written testing instructions and verified them locally
- I have noted any required post-merge steps (config imports, cache rebuilds, manual changes)
- This MR contains no breaking API or hook changes, or they are explicitly documented in the description
AI Compliance
Note
Check the one that best describes your usage, or leave all unchecked if AI was not significantly used.
- AI Assisted Code
Mainly written by a human; AI used for autocomplete or partial generation under full human supervision. - AI Generated Code
Mainly generated by AI, reviewed and approved by a human before this MR was created. - Vibe Coded
Generated by AI and only functionally reviewed before this MR was created.
Closes #3586369