Skip to content

Flex-Repeater: Slot-Werte roh lesen, Entities nur noch als Rückfall umwandeln - #463

Merged
skerbis merged 1 commit into
FriendsOfREDAXO:mainfrom
anveno:fix/repeater-entities-vor-json
Oct 7, 2026
Merged

skerbis merged 1 commit into
FriendsOfREDAXO:mainfrom
anveno:fix/repeater-entities-vor-json

Conversation

@anveno

@anveno anveno commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Betroffen: MForm 10.0.0, 10.0.1 und main (ff6f911), REDAXO 5.21.4, PHP 8.4. Der Zweig 9.x (9.5.2) hat
dieselben Stellen, dort nicht nachgestellt.

Problem

Beim Lesen eines Slot-Werts werden Entities umgewandelt, bevor das JSON gelesen wird. Für den escapten
Platzhalter (decode('REX_VALUE[1]')) ist das richtig. Per Slot-ID (decode(1)) kommt der Wert aber roh aus
der Datenbank, und Entities darin gehören zum Inhalt der Felder.

  1. Ausgabe (MFormOutputHelper::decodeRaw(), damit decode(), values(), value(), isRepeater()):
    Steht in einem Feld ein ", ist das JSON nach dem Umwandeln ungültig und decode() liefert []. Das
    ganze Modul bleibt im Frontend leer. TinyMCE schreibt Anführungszeichen in Attributen genau so, z. B. bei
    einem Linktitel Sagt "Hallo". Als Text getippte Tags (<h2>) kommen als echtes Markup zurück.
  2. Eingabemaske (MFormValueHandler::loadRexVars()): htmlspecialchars_decode() vor dem json_decode().
    Beim Öffnen eines Slices steht getippter Text als Markup im Editor und wird beim Speichern so übernommen,
    Teile gehen verloren.

Reproduktion

Repeater mit TinyMCE-Feld, Ausgabe über MFormRepeaterHelper::decode(1).

  • Link mit Titel Sagt "Hallo" speichern → Frontend leer.
  • Text <h2>Titel</h2> tippen und speichern → im Frontend eine Überschrift; Slice öffnen und wieder speichern
    → gespeichert ist die Überschrift statt des Texts.

Änderung

  • decodeRaw() liest zuerst das rohe JSON. Nur wenn das scheitert, werden wie bisher Entities umgewandelt und
    der <br>-Rückfall angewendet. Der Aufruf mit dem escapten Platzhalter liefert dasselbe wie vorher.
  • dataVersion() nutzt decodeRaw().
  • MFormValueHandler liest ohne htmlspecialchars_decode(). Mit ENT_NOQUOTES wandelt es nur &lt;, &gt;
    und &amp; um, also kein Zeichen der JSON-Struktur: Es hat nie entschieden, ob ein Wert lesbar ist, sondern
    nur die Feldwerte verändert.

Tests

RepeaterEntityDecodeTest (Unit) und ValueHandlerEntityTest (REDAXO): ohne die Änderung 6 rot, mit ihr
läuft die ganze Suite. Im Browser vorher und nachher geprüft.

Hinweise

  • Wer sich darauf verlassen hat, dass decode(1) Entities in den Werten umwandelt, bekommt die Werte jetzt
    so, wie sie gespeichert sind.
  • Dasselbe Muster steht noch in MBlockToRepeaterConverter::convertData(), rex_var_custom_link_multi und im
    YForm-Value custom_link_multi; hier nicht angefasst.
  • CHANGELOG und Version habe ich offen gelassen.
  • MBlock-Module (MBlock 4.6.8 mit MForm-Feldern) im Browser geprüft: Verhalten mit und ohne die Änderung
    gleich, MBlock lädt seine Werte selbst.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Fehlerbehebungen
    • Gespeicherte HTML-Entities bleiben beim Laden und in Repeater-Eingabefeldern als Text erhalten, statt als Markup interpretiert zu werden.
    • Repeater-Werte mit Entities, Anführungszeichen oder Zeilenumbrüchen werden zuverlässig verarbeitet.

…mwandeln

Beim Lesen eines Slot-Werts wurden Entities umgewandelt, bevor das JSON
gelesen wurde. Für den escapten Platzhalter (decode('REX_VALUE[1]')) ist das
richtig; per Slot-Id kommt der Wert aber roh aus der Datenbank, und Entities
darin gehören zum Inhalt der Felder.

Ausgabe: Ein &quot; in einem Feld (Linktitel mit Anführungszeichen aus
TinyMCE) machte das JSON ungültig, decode() lieferte [] und das Modul blieb
leer. Als Text getippte Tags (&lt;h2&gt;) kamen als Markup zurück.
Eingabemaske: Beim Öffnen eines Slices stand getippter Text als Markup im
Editor und wurde so gespeichert.

decodeRaw() liest zuerst das rohe JSON und wandelt Entities nur um, wenn das
scheitert; der Aufruf mit dem escapten Platzhalter liefert dasselbe wie
bisher. dataVersion() nutzt decodeRaw(). MFormValueHandler liest ohne
htmlspecialchars_decode(): Mit ENT_NOQUOTES ändert es kein Zeichen der
JSON-Struktur, sondern nur die Feldwerte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 221761dc-f8a8-4791-b0f7-5063392c0fb6
📥 Commits

Reviewing files that changed from the base of the PR and between ff6f911 and ec59ec9.

📒 Files selected for processing (5)
  • lib/MForm/Handler/MFormValueHandler.php
  • lib/MForm/Repeater/MFormRepeaterHelper.php
  • lib/MForm/Utils/MFormOutputHelper.php
  • tests/Redaxo/Handler/ValueHandlerEntityTest.php
  • tests/Unit/Repeater/RepeaterEntityDecodeTest.php

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


Moin

Walkthrough

Die Änderung dekodiert gültiges JSON zuerst unverändert. Dadurch bleiben HTML-Entities in gespeicherten Werten und Repeater-Daten erhalten. Neue Tests prüfen das Laden von Werten und verschiedene Repeater-Eingaben.

Changes

Entity-Erhaltung beim JSON-Decoding

Layer / File(s) Summary
JSON-Decoding und Entity-Erhaltung
lib/MForm/Utils/MFormOutputHelper.php, lib/MForm/Handler/MFormValueHandler.php, lib/MForm/Repeater/MFormRepeaterHelper.php, tests/Redaxo/Handler/ValueHandlerEntityTest.php, tests/Unit/Repeater/RepeaterEntityDecodeTest.php
decodeRaw() versucht zuerst, den unveränderten Text als JSON zu dekodieren. Entity-Dekodierung und <br>-Behandlung erfolgen erst, wenn das nicht gelingt. loadRexVars() übergibt gespeicherte Werte unverändert. dataVersion() verwendet decodeRaw(). Neue Tests prüfen gespeicherte Werte, Repeater-Daten und Platzhalter.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Bug fix

Suggested reviewers: skerbis

Merge Risk: ⚪ Minimal · up to ec59e

No actionable merge-blocking issue is established; the change is ready for normal merge checks.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 5 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Moin. Der Titel beschreibt die Hauptänderung: Flex-Repeater lesen Slot-Werte roh und wandeln Entities nur bei Bedarf als Rückfall um.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@anveno
anveno requested a review from skerbis October 7, 2026 09:17
@skerbis
skerbis merged commit 1578b14 into FriendsOfREDAXO:main Oct 7, 2026
5 checks passed
@skerbis

skerbis commented Oct 7, 2026

Copy link
Copy Markdown
Member

Danke, @anveno! Ich habe den PR nach dem Merge noch einmal gegengeprüft und zwei Dinge ergänzt.

Gegencheck

  • Unit- und REDAXO-Suite laufen grün, inklusive der neuen Tests.
  • Gegen echte Daten: Ich habe alle 77 JSON-Slice-Werte einer Produktiv-Instanz einmal mit dem alten und einmal mit dem neuen decodeRaw() gelesen. Kein Wert, der vorher lesbar war, ist jetzt unlesbar. Der escapte Platzhalter ('REX_VALUE[x]' mit nl2br()) liefert bei allen 77 dasselbe wie vorher. Abweichungen gibt es nur bei fünf Werten aus dem HTML-Editor: &nbsp; und &amp; in einem href bleiben jetzt stehen. Das ist korrektes HTML und wird gleich gerendert.
  • In der Eingabemaske ändert sich für keinen Wert, ob er lesbar ist. Das bestätigt deine Begründung zu ENT_NOQUOTES.

Was ich anders bzw. zusätzlich gemacht habe

Releases: 10.0.2 und 9.5.3.

🤖 Generated with Claude Code

skerbis added a commit that referenced this pull request Oct 7, 2026
…lt Entities, Release 10.0.2 (#464)

Die Flex-Tab-Leiste hob Bootstraps float:right von .pull-right auf.
Rechte Reiter bekommen jetzt order:1, der erste davon margin-left:auto.

MBlockToRepeaterConverter::convertData() liest wie decodeRaw() (#463)
zuerst roh; ein &quot; im Editor-Inhalt liess die Konvertierung sonst
scheitern. CHANGELOG nennt jetzt auch #463, Doku zu decode()/values()
angepasst.

Fixes #462

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
skerbis added a commit that referenced this pull request Oct 7, 2026
…kport #463), Release 9.5.3 (#465)

Per Slot-Id kommt der Wert roh aus der Datenbank; decode() wandelte
Entities vor dem json_decode() um. Ein &quot; aus TinyMCE machte das JSON
ungueltig (Modul leer), &lt;h2&gt; als Text kam als Markup zurueck.
decode() und MBlockToRepeaterConverter::convertData() lesen jetzt zuerst
roh und wandeln Entities nur als Rueckfall um; MFormValueHandler liest
ohne htmlspecialchars_decode().

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants