Problem/Motivation

I have a node with a title containing an ampersand. After enabling this module, the ampersand is HTML encoded in the JSON:API response. Other string fields are also double encoded.

Steps to reproduce

Create a node with a title containing an ampersand.

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

longwave created an issue. See original summary.

batigolix made their first commit to this issue’s fork.

batigolix’s picture

Status: Active » Needs review
Issue tags: +finalist-sprint
batigolix’s picture

Priority: Major » Normal
arjenk’s picture

Status: Needs review » Needs work

Needs a re-merge, so setting the status back to needs work.

The solution looks good, one suggestion:

preg_match('/<[^>]+>|(?:src|href)=["\']?\//i', $value)

Core does in Html::transformRootRelativeUrlsToAbsolute() rewrite more attributes than src/href: data, srcset, .. Could we include them as well? Or simplify to an if for any tag: if (!str_contains($value, '<'))

And it would be nice to have this covered in a unittest.

longwave’s picture

Is there a way of narrowing the normalizer so it only applies to field types that should contain formatted markup in the first place?

arjenk’s picture

Status: Needs work » Needs review

Good question, the solution now looks for a setting on the field 'text source', like core does in https://git.drupalcode.org/project/drupal/-/blob/11.4.5/core/modules/tex.... I think that makes the regex if() redundant, so removed.

Tested this with titles like 'Tom & Jerry <3' and 'a < b > c', which were previously broken. Also int and bools are not converted to strings anymore. One side effect, NULL strings now come back as NULL, was "".

Had to merge 8.x-1.x, tested on Drupal 11.4.5 / PHP 8.4, all unittests green. The old unit tests ran, but the helper reimplemented the conversion instead of calling normalize(), so they never tested the class; fixed that too.

andres alvarez made their first commit to this issue’s fork.

andres alvarez’s picture

Tested the current MR branch (3530605-) end-to-end on a fresh Drupal 11 site (DDEV, standard profile, JSON:API enabled) to validate the fix beyond the unit tests.

Created an article node with:
- Title: Tom & Jerry <3 "quotes"
- Body (full_html):

Some text with internal link and Only local images are allowed.

Fetched it via /jsonapi/node/article/{uuid} and confirmed:

- title: returned exactly as stored, no double-encoding. The original bug (ampersands/angle brackets in plain string fields getting mangled) is fixed.
- body.value: relative URLs correctly rewritten to absolute (/internal → https://.../internal, /sites/default/files/pic.jpg → https://.../sites/default/files/pic.jpg).

One limitation found and confirmed not fixable within this normalizer's current design: body.processed (the pre-rendered HTML that many REST/JSON:API consumers read instead of value) is not rewritten. Root cause: core's \Drupal\text\TextProcessed (the real class backing the processed computed property) implements neither StringInterface (so getSupportedTypes() never dispatches this normalizer to it) nor getCastedValue() (so forcing it through would fatal in the parent PrimitiveDataNormalizer::normalize()).

Fixing that properly would require explicitly registering TextProcessed::class in getSupportedTypes(), bypassing parent::normalize() for that branch, and manually reattaching the cacheable metadata (cache tags/contexts/max-age) that TextProcessed exposes via CacheableDependencyInterface — a materially different and riskier change than what this issue set out to fix. I've left the reasoning documented in a docblock on isFormattedText() so it isn't silently reintroduced, and updated the README to make the scope explicit.

Given processed was never handled before this MR either, I'd suggest tracking it as a separate follow-up issue rather than blocking this one on it. Happy to open that if there's interest.

arjenk’s picture

Thanks for the quick reply and the end-to-end test. I have good news: the processed property is already covered by #3360137: Only affecting "value" not "processed" property (MR !6 adds a TextProcessedNormalizer for this), so no new issue is needed. I should have mentioned that i guess.

Your README additions are a good improvement. One request: replace the advice to read value instead of processed with a reference to #3360137; that MR must then update the README.

andres alvarez’s picture

Updated the README to point at #3360137 instead of the "read 'value' instead" workaround, since that issue already adds proper 'processed' support.

batigolix’s picture

Status: Needs review » Reviewed & tested by the community

Many thanks for testing and reviewing. I revert the most recent documentation changes as they are related to the other issue #3360137: Only affecting "value" not "processed" property

batigolix’s picture

Status: Reviewed & tested by the community » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

  • batigolix committed b3517e1b on 8.x-1.x
    fix: #3530605 Normalizer causes double encoding on unrelated fields
    
    By...

  • batigolix committed b3517e1b on 2.1.x
    fix: #3530605 Normalizer causes double encoding on unrelated fields
    
    By...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.