fix(audit M1, L2): Meta-Schlüssel-Optionen gegen alle Schleifen der Vorfahrenkette prüfen; queryType wie sanitize_key normalisieren, terms/users als Term-/Benutzer-Schleife

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-19 23:47:58 +02:00
co-authored by Claude Sonnet 5.5
parent 52208764f0
commit 522fc8ff4d
9 changed files with 246 additions and 35 deletions
@@ -373,4 +373,70 @@ final class DiviLoopWriteTest extends DiviWriteAbilityTestCase
]);
self::assertStringContainsString('außerhalb jeder Divi-Schleife', implode(' | ', $off['warnings']));
}
private function useChainPage(string $outerType): void
{
$meta = VariableRef::serialize('content', 'loop_post_meta_key_manual_custom_field', ['loop_meta_key' => 'telefon']);
$loop = static fn (string $type): array => self::loop(['enable' => 'on', 'queryType' => $type]);
$this->original = '<!-- wp:divi/placeholder -->' . self::block('section', $loop($outerType), self::block(
'row',
$loop('post_types'),
self::block('column', [], self::text('<p>Fest</p>') . self::text($meta))
)) . '<!-- /wp:divi/placeholder -->';
$this->post->post_content = $this->original;
}
/** Audit M1: a post loop inside a term loop may be empty; the term loop would then read post meta by term ID. */
public function testPostMetaInAPostLoopInsideATermLoopIsRefusedOnInsertAndUpdate(): void
{
$this->useChainPage('post_taxonomies');
$meta = VariableRef::serialize('content', 'loop_post_meta_key_manual_custom_field', ['loop_meta_key' => 'telefon']);
$insert = $this->insertInto('s0.r0.c0', ['nodes' => [['type' => 'text', 'content' => $meta]]]);
self::assertStringContainsString('passt nicht zur Schleife', self::problems($insert));
$update = $this->changeAll([['id' => 's0.r0.c0.m0', 'set' => ['content' => $meta]]]);
self::assertStringContainsString('passt nicht zur Schleife', self::problems($update));
self::assertSame([], $this->saved);
}
public function testPostMetaInAPostLoopInsideAPostLoopIsAccepted(): void
{
$this->useChainPage('post_types');
$meta = VariableRef::serialize('content', 'loop_post_meta_key_manual_custom_field', ['loop_meta_key' => 'telefon']);
$insert = $this->insertInto('s0.r0.c0', ['nodes' => [['type' => 'text', 'content' => $meta]]]);
self::assertTrue($insert['ok'], (string) json_encode($insert));
$update = $this->changeAll([['id' => 's0.r0.c0.m0', 'set' => ['content' => $meta]]]);
self::assertTrue($update['ok'], (string) json_encode($update));
}
/** Audit M1: the update warning follows the chain, not only the innermost loop. */
public function testUpdateWarnsWhenAnOuterLoopTurnsIntoAnotherKindUnderMetaContent(): void
{
$this->useChainPage('post_types');
$result = $this->changeAll([
['id' => 's0', 'attrs' => ['module.advanced.loop' => ['desktop' => self::TERM_LOOP]]],
]);
self::assertTrue($result['ok'], (string) json_encode($result));
self::assertStringContainsString('loop_post_meta_key_manual_custom_field', implode(' | ', $result['warnings']));
}
/** Audit L2: "terms" is a term loop for Divi, an unsupported target "can show data of another object". */
public function testUpdateWarningNamesTheRealKindOfAnAliasAndNeverCallsAnUnsupportedLoopEmpty(): void
{
$alias = $this->changeAll([
['id' => 's0.r0.c0', 'attrs' => ['module.advanced.loop' => ['desktop' => ['enable' => 'on', 'queryType' => 'Terms']]]],
]);
self::assertStringContainsString('Typ: Terme (vorher: Beiträge)', implode(' | ', $alias['warnings']));
$this->post->post_content = $this->original;
$other = $this->changeAll([
['id' => 's0.r0.c0', 'attrs' => ['module.advanced.loop' => ['desktop' => ['enable' => 'on', 'queryType' => 'repeater_x']]]],
]);
$text = implode(' | ', $other['warnings']);
self::assertStringContainsString('kann Daten eines anderen Objekts zeigen', $text);
self::assertStringNotContainsString('zeigt sie dort leer', $text);
}
}