feat: DynamicContentRules/VariableRef mit LoopScope – loop_* nur in passender Schleife (X2, X5)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
3d12043b4f
commit
f8912c255d
@@ -15,7 +15,8 @@ defined('ABSPATH') || exit;
|
||||
*
|
||||
* Allowed names: a non-color global variable or a theme font (no settings, DESIGN_SETTINGS,
|
||||
* S27); custom_meta_<key> with META_KEY (no leading "_", ruling S12; settings from the map entry
|
||||
* of that exact name, else META_SETTINGS); or a key of the dynamic content map.
|
||||
* of that exact name, else META_SETTINGS); a loop_* key of the map only inside a Divi loop of its
|
||||
* kind (constructor argument $loop, rulings X2/X5 – see loopFieldsFor()); or another key of the map.
|
||||
*
|
||||
* Dynamic content map (ruling S22), constructor argument $map:
|
||||
* array<string, array<string, string>> option name => [setting key => field type]
|
||||
@@ -27,7 +28,7 @@ defined('ABSPATH') || exit;
|
||||
* Default (null): DEFAULT_DYNAMIC_CONTENT. Task 5 builds the map from Divi's live registry
|
||||
* DynamicContentOptions::get_options($post_id, 'edit') – option name => its "fields" (key =>
|
||||
* field "type"). Always, whatever the map says: "enable_html" is rejected (VariableRef), loop_*
|
||||
* names only when the map names them, custom_meta_<key> names and the values of settings whose
|
||||
* names only in a loop of their kind, custom_meta_<key> names and the values of settings whose
|
||||
* key ends in "meta_key" (meta_key, select_meta_key, loop_meta_key …) obey META_KEY after Divi's
|
||||
* own prefix stripping (rulings S29, S30), and no string – before or after cleaning (ruling S28)
|
||||
* – may contain a reference or ")$". A map of another shape fails in the constructor with
|
||||
@@ -86,13 +87,17 @@ final class DynamicContentRules
|
||||
/** @var array<string, array<string, string>> */
|
||||
private array $map;
|
||||
|
||||
private LoopScope $loop;
|
||||
|
||||
/**
|
||||
* @param array<array-key, mixed>|null $map See the class docblock.
|
||||
* @param array<array-key, mixed>|null $map See the class docblock.
|
||||
* @param LoopScope|null $loop The Divi loop the content sits in; null = none (no loop_* names).
|
||||
* @throws InvalidArgumentException When the map does not have that shape.
|
||||
*/
|
||||
public function __construct(private DesignSystem $design, ?array $map = null)
|
||||
public function __construct(private DesignSystem $design, ?array $map = null, ?LoopScope $loop = null)
|
||||
{
|
||||
$this->map = $map === null ? self::DEFAULT_DYNAMIC_CONTENT : self::checkMap($map);
|
||||
$this->loop = $loop ?? LoopScope::none();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,15 +115,12 @@ final class DynamicContentRules
|
||||
return $this->map[$name] ?? self::META_SETTINGS;
|
||||
}
|
||||
|
||||
if (isset($this->map[$name])) {
|
||||
return $this->map[$name];
|
||||
if (str_starts_with($name, 'loop_')) {
|
||||
return $this->loopFieldsFor($name);
|
||||
}
|
||||
|
||||
if (str_starts_with($name, 'loop_')) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Schleifen-Inhalt "%s" ist hier nicht verfügbar (nur innerhalb von Divi-Schleifen).',
|
||||
$name
|
||||
));
|
||||
if (isset($this->map[$name])) {
|
||||
return $this->map[$name];
|
||||
}
|
||||
|
||||
if (in_array($name, $this->design->contentVariableIds(), true)) {
|
||||
@@ -128,6 +130,40 @@ final class DynamicContentRules
|
||||
throw VariableRef::unknownName($name, 'content');
|
||||
}
|
||||
|
||||
/**
|
||||
* A loop_* option (rulings X2/X5): only inside a Divi loop, only of the loop's kind (LoopScope::fits(),
|
||||
* by the option name – Divi reads the meta of the loop object's ID whatever the object is, so a post
|
||||
* meta option in a term loop would print the post with the term's ID: a security boundary, always an
|
||||
* error), and only when the map names it. Its settings then follow the usual rules (meta keys S29/S30).
|
||||
*
|
||||
* @return array<string, string>
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function loopFieldsFor(string $name): array
|
||||
{
|
||||
if ($this->loop->queryType() === null) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Schleifen-Inhalt "%s" braucht eine Divi-Schleife in diesem Modul oder einem übergeordneten '
|
||||
. 'Element (Abschnitt, Zeile, Spalte, Gruppe).',
|
||||
$name
|
||||
));
|
||||
}
|
||||
|
||||
if (! $this->loop->fits($name)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Schleifen-Inhalt "%s" passt nicht zur Schleife dieses Elements (Typ: %s).',
|
||||
$name,
|
||||
$this->loop->label()
|
||||
));
|
||||
}
|
||||
|
||||
if (! isset($this->map[$name])) {
|
||||
throw VariableRef::unknownName($name, 'content');
|
||||
}
|
||||
|
||||
return $this->map[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans one content setting by its field type (rulings S21/S22). The steps, in this order:
|
||||
* 1. control characters off the RAW value (S32/S32a);
|
||||
|
||||
@@ -27,7 +27,8 @@ defined('ABSPATH') || exit;
|
||||
* - type "content": DynamicContentRules – the dynamic content map (ruling S22), custom_meta_<key>
|
||||
* (S12/S29/S30), non-color global variables and theme fonts (S27); it also cleans each content
|
||||
* setting. "enable_html" is always rejected here. Internal links post_link_url_<type> ({{link:…}},
|
||||
* E13a) only in URL fields.
|
||||
* E13a) only in URL fields. loop_* options only inside a Divi loop of their kind (constructor
|
||||
* argument $loop, rulings X2/X5); without it no loop_* name is allowed.
|
||||
* A dynamic content map of the wrong shape fails in the constructor with InvalidArgumentException.
|
||||
*/
|
||||
final class VariableRef
|
||||
@@ -47,11 +48,12 @@ final class VariableRef
|
||||
|
||||
/**
|
||||
* @param array<array-key, mixed>|null $dynamicContent See DynamicContentRules.
|
||||
* @param LoopScope|null $loop The Divi loop of the values; null = none.
|
||||
* @throws InvalidArgumentException When the map does not have that shape.
|
||||
*/
|
||||
public function __construct(private DesignSystem $design, ?array $dynamicContent = null)
|
||||
public function __construct(private DesignSystem $design, ?array $dynamicContent = null, ?LoopScope $loop = null)
|
||||
{
|
||||
$this->content = new DynamicContentRules($design, $dynamicContent);
|
||||
$this->content = new DynamicContentRules($design, $dynamicContent, $loop);
|
||||
}
|
||||
|
||||
/** Whether $value contains a reference anywhere (such a value may only be a whole reference). */
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Compose;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use LIT\MCP\Integrations\Divi\Compose\DynamicContentRules;
|
||||
use LIT\MCP\Integrations\Divi\Compose\LoopScope;
|
||||
use LIT\MCP\Tests\Support\DiviFixtures;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/** Rulings X2/X5 and the security boundary "Typabgleich": loop_* names only in a loop of their kind. */
|
||||
final class DynamicContentRulesTest extends TestCase
|
||||
{
|
||||
/** The map of a composition inside a loop: forPost() plus loopFields() of every kind (worst case). */
|
||||
private const MAP = [
|
||||
'post_title' => ['before' => 'richtext', 'after' => 'richtext'],
|
||||
'loop_post_title' => ['before' => 'richtext', 'after' => 'richtext', 'loop_position' => 'text'],
|
||||
'loop_post_meta_key_manual_custom_field' => ['select_loop_meta_key' => 'select', 'loop_meta_key' => 'text'],
|
||||
'loop_term_name' => ['before' => 'richtext', 'after' => 'richtext', 'loop_position' => 'text'],
|
||||
'loop_term_meta_key_manual_custom_field' => ['select_loop_meta_key' => 'select', 'loop_meta_key' => 'text'],
|
||||
'loop_user_meta_key_manual_custom_field' => ['select_loop_meta_key' => 'select', 'loop_meta_key' => 'text'],
|
||||
'loop_menu_text' => ['before' => 'richtext', 'after' => 'richtext', 'loop_position' => 'text'],
|
||||
'loop_acf_team|||name' => ['before' => 'richtext', 'after' => 'richtext'],
|
||||
];
|
||||
|
||||
private static function rules(?LoopScope $loop = null): DynamicContentRules
|
||||
{
|
||||
return new DynamicContentRules(DiviFixtures::designSystem(), self::MAP, $loop);
|
||||
}
|
||||
|
||||
private static function assertRefused(DynamicContentRules $rules, string $name, string $message): void
|
||||
{
|
||||
try {
|
||||
$rules->fieldsFor($name);
|
||||
self::fail('Accepted: ' . $name);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
self::assertSame($message, $e->getMessage(), $name);
|
||||
}
|
||||
}
|
||||
|
||||
/** Spec §9 / X5: without a loop every loop_* name is refused – also one the map names. */
|
||||
public function testWithoutALoopEveryLoopOptionIsRefused(): void
|
||||
{
|
||||
foreach ([self::rules(), self::rules(LoopScope::none())] as $rules) {
|
||||
foreach (array_keys(self::MAP) as $name) {
|
||||
if (! str_starts_with($name, 'loop_')) {
|
||||
continue;
|
||||
}
|
||||
self::assertRefused(
|
||||
$rules,
|
||||
$name,
|
||||
sprintf(
|
||||
'Schleifen-Inhalt "%s" braucht eine Divi-Schleife in diesem Modul oder einem übergeordneten '
|
||||
. 'Element (Abschnitt, Zeile, Spalte, Gruppe).',
|
||||
$name
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testALoopOptionOfTheLoopsKindHasItsMapSettings(): void
|
||||
{
|
||||
self::assertSame(self::MAP['loop_post_title'], self::rules(LoopScope::ofQueryType('post_types'))
|
||||
->fieldsFor('loop_post_title'));
|
||||
self::assertSame(self::MAP['loop_post_title'], self::rules(LoopScope::ofQueryType('current_page'))
|
||||
->fieldsFor('loop_post_title'));
|
||||
self::assertSame(self::MAP['loop_term_name'], self::rules(LoopScope::ofQueryType('post_taxonomies'))
|
||||
->fieldsFor('loop_term_name'));
|
||||
self::assertSame(
|
||||
self::MAP['loop_user_meta_key_manual_custom_field'],
|
||||
self::rules(LoopScope::ofQueryType('user_roles'))->fieldsFor('loop_user_meta_key_manual_custom_field')
|
||||
);
|
||||
self::assertSame(self::MAP['loop_menu_text'], self::rules(LoopScope::ofQueryType('menus'))
|
||||
->fieldsFor('loop_menu_text'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Security boundary: Divi reads the meta of the loop object's ID whatever the object is – post meta
|
||||
* of the post with the term's ID (DynamicContentLoopOptions.php:145-158). A hard error, not a hint.
|
||||
*
|
||||
* @return array<string, array{string, string, string}>
|
||||
*/
|
||||
public static function mismatches(): array
|
||||
{
|
||||
return [
|
||||
'post meta in a term loop' => ['post_taxonomies', 'loop_post_meta_key_manual_custom_field', 'Terme'],
|
||||
'post meta in a user loop' => ['user_roles', 'loop_post_meta_key_manual_custom_field', 'Benutzer'],
|
||||
'post meta in a menu loop' => ['menus', 'loop_post_meta_key_manual_custom_field', 'Menü'],
|
||||
'user meta in a post loop' => ['post_types', 'loop_user_meta_key_manual_custom_field', 'Beiträge'],
|
||||
'term meta in a post loop' => ['current_page', 'loop_term_meta_key_manual_custom_field', 'Beiträge'],
|
||||
'term meta in a user loop' => ['user_roles', 'loop_term_meta_key_manual_custom_field', 'Benutzer'],
|
||||
'term name in a post loop' => ['post_types', 'loop_term_name', 'Beiträge'],
|
||||
'post title in a menu loop' => ['menus', 'loop_post_title', 'Menü'],
|
||||
'menu text in a term loop' => ['post_taxonomies', 'loop_menu_text', 'Terme'],
|
||||
'acf row in a post loop' => ['post_types', 'loop_acf_team|||name', 'Beiträge'],
|
||||
'post title in an acf repeater loop' => ['repeater_team', 'loop_post_title', 'repeater_team'],
|
||||
'acf row in an acf repeater loop' => ['repeater_team', 'loop_acf_team|||name', 'repeater_team'],
|
||||
];
|
||||
}
|
||||
|
||||
/** @dataProvider mismatches */
|
||||
public function testAnOptionOfAnotherKindIsRefused(string $queryType, string $name, string $label): void
|
||||
{
|
||||
self::assertRefused(
|
||||
self::rules(LoopScope::ofQueryType($queryType)),
|
||||
$name,
|
||||
sprintf('Schleifen-Inhalt "%s" passt nicht zur Schleife dieses Elements (Typ: %s).', $name, $label)
|
||||
);
|
||||
}
|
||||
|
||||
public function testALoopOptionTheMapDoesNotNameIsUnknown(): void
|
||||
{
|
||||
$rules = self::rules(LoopScope::ofQueryType('post_types'));
|
||||
|
||||
foreach (['loop_post_date', 'loop_post_meta_key_telefon', 'loop_'] as $name) {
|
||||
try {
|
||||
$rules->fieldsFor($name);
|
||||
self::fail('Accepted: ' . $name);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
self::assertStringContainsString('Unbekannter Name "' . $name . '"', $e->getMessage());
|
||||
}
|
||||
}
|
||||
// The default map (registry unavailable) names no loop option.
|
||||
$this->expectExceptionMessage('Unbekannter Name "loop_post_title"');
|
||||
(new DynamicContentRules(DiviFixtures::designSystem(), null, LoopScope::ofQueryType('post_types')))
|
||||
->fieldsFor('loop_post_title');
|
||||
}
|
||||
|
||||
public function testOtherNamesAreTheSameInsideALoop(): void
|
||||
{
|
||||
foreach ([null, LoopScope::ofQueryType('post_taxonomies')] as $loop) {
|
||||
$rules = self::rules($loop);
|
||||
|
||||
self::assertSame(self::MAP['post_title'], $rules->fieldsFor('post_title'));
|
||||
self::assertSame(DynamicContentRules::META_SETTINGS, $rules->fieldsFor('custom_meta_telefon'));
|
||||
self::assertSame(DynamicContentRules::DESIGN_SETTINGS, $rules->fieldsFor('gvid-s1'));
|
||||
foreach (['custom_meta__edit_lock', 'site_title'] as $name) {
|
||||
try {
|
||||
$rules->fieldsFor($name);
|
||||
self::fail('Accepted: ' . $name);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
self::assertStringContainsString('Unbekannter Name "' . $name . '"', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Compose;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use LIT\MCP\Integrations\Divi\Compose\LoopScope;
|
||||
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
|
||||
use LIT\MCP\Tests\Support\ComposeFixture;
|
||||
use LIT\MCP\Tests\Support\DiviFixtures;
|
||||
@@ -14,9 +15,14 @@ use PHPUnit\Framework\TestCase;
|
||||
final class VariableRefTest extends TestCase
|
||||
{
|
||||
/** @param array<string, array<string, string>>|null $dynamicContent */
|
||||
private static function ref(?array $dynamicContent = null): VariableRef
|
||||
private static function ref(?array $dynamicContent = null, ?LoopScope $loop = null): VariableRef
|
||||
{
|
||||
return new VariableRef(DiviFixtures::designSystem(), $dynamicContent);
|
||||
return new VariableRef(DiviFixtures::designSystem(), $dynamicContent, $loop);
|
||||
}
|
||||
|
||||
private static function postLoop(): LoopScope
|
||||
{
|
||||
return LoopScope::ofQueryType('post_types');
|
||||
}
|
||||
|
||||
private static function content(string $name, string $settings): string
|
||||
@@ -24,6 +30,12 @@ final class VariableRefTest extends TestCase
|
||||
return '$variable({"type":"content","value":{"name":"' . $name . '","settings":' . $settings . '}})$';
|
||||
}
|
||||
|
||||
/** Divi's one post meta loop option (DynamicContentOptionLoopPostMetaKey.php:218-227). */
|
||||
private static function loopMeta(string $settings): string
|
||||
{
|
||||
return self::content('loop_post_meta_key_manual_custom_field', $settings);
|
||||
}
|
||||
|
||||
/** A JSON "\uXXXX" escape, built from bytes so no tool turns it into the character itself. */
|
||||
private static function escape(string $hex): string
|
||||
{
|
||||
@@ -219,7 +231,18 @@ final class VariableRefTest extends TestCase
|
||||
self::content('post_date', '{"date_format":"d.m.Y","offset":3}'),
|
||||
$ref->canonical(self::content('post_date', '{"date_format":" d.m.Y","offset":3}'), $sanitizer)
|
||||
);
|
||||
self::assertSame(self::content('loop_post_title', '{}'), $ref->canonical(self::content('loop_post_title', '{}'), $sanitizer));
|
||||
// Ruling X5: a loop_* name the map names still needs a loop of its kind.
|
||||
try {
|
||||
$ref->canonical(self::content('loop_post_title', '{}'), $sanitizer);
|
||||
self::fail('loop_post_title accepted without a loop');
|
||||
} catch (InvalidArgumentException $e) {
|
||||
self::assertStringContainsString('braucht eine Divi-Schleife', $e->getMessage());
|
||||
}
|
||||
$inLoop = self::ref(['loop_post_title' => []], self::postLoop());
|
||||
self::assertSame(
|
||||
self::content('loop_post_title', '{}'),
|
||||
$inLoop->canonical(self::content('loop_post_title', '{}'), $sanitizer)
|
||||
);
|
||||
// Names outside the map: custom_meta_ keeps its rule, the rest is unknown.
|
||||
self::assertSame(
|
||||
self::content('custom_meta_telefon', '{"before":"<b>T</b>"}'),
|
||||
@@ -241,6 +264,57 @@ final class VariableRefTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
/** Ruling X2/X5: loop content in a loop of its kind is cleaned like any other content reference. */
|
||||
public function testLoopContentInALoopOfItsKind(): void
|
||||
{
|
||||
$meta = [
|
||||
'before' => 'richtext', 'after' => 'richtext', 'loop_position' => 'text',
|
||||
'select_loop_meta_key' => 'select', 'loop_meta_key' => 'text', 'enable_html' => 'yes_no_button',
|
||||
];
|
||||
$map = [
|
||||
'loop_post_date' => [
|
||||
'before' => 'richtext', 'after' => 'richtext', 'loop_position' => 'text',
|
||||
'date_format' => 'select', 'custom_date_format' => 'text',
|
||||
],
|
||||
'loop_post_meta_key_manual_custom_field' => $meta,
|
||||
'loop_term_meta_key_manual_custom_field' => $meta,
|
||||
];
|
||||
$sanitizer = ComposeFixture::sanitizer();
|
||||
$date = self::content(
|
||||
'loop_post_date',
|
||||
'{"before":"Am ","after":"","loop_position":"","date_format":"custom","custom_date_format":"d.m.Y"}'
|
||||
);
|
||||
|
||||
self::assertSame($date, self::ref($map, self::postLoop())->canonical($date, $sanitizer));
|
||||
self::assertSame(
|
||||
self::content('loop_post_date', '{"before":"Am ","after":""}'),
|
||||
self::ref($map, self::postLoop())->canonical(
|
||||
self::content('loop_post_date', '{"before":"<script>x()</script>Am ","after":""}'),
|
||||
$sanitizer
|
||||
)
|
||||
);
|
||||
|
||||
$terms = self::ref($map, LoopScope::ofQueryType('post_taxonomies'));
|
||||
$termMeta = self::content('loop_term_meta_key_manual_custom_field', '{"select_loop_meta_key":"%s"}');
|
||||
$farbe = sprintf($termMeta, 'loop_term_meta_key_farbe');
|
||||
self::assertSame($farbe, $terms->canonical($farbe, $sanitizer));
|
||||
|
||||
$rejected = [
|
||||
'Typ: Terme' => [$terms, self::loopMeta('{"select_loop_meta_key":"loop_post_meta_key_telefon"}')],
|
||||
'Meta-Schlüssel' => [$terms, sprintf($termMeta, 'loop_term_meta_key__x')],
|
||||
'HTML-Ausgabe' => [self::ref($map, self::postLoop()), self::loopMeta('{"enable_html":"on"}')],
|
||||
'braucht eine Divi-Schleife' => [self::ref($map), $date],
|
||||
];
|
||||
foreach ($rejected as $fragment => [$ref, $value]) {
|
||||
try {
|
||||
$ref->canonical($value, $sanitizer);
|
||||
self::fail('Accepted: ' . $value);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
self::assertStringContainsString($fragment, $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** S28: the split-marker rows really carry no marker before sanitizing. */
|
||||
public function testSplitMarkerRowsHaveNoRawMarker(): void
|
||||
{
|
||||
@@ -258,8 +332,8 @@ final class VariableRefTest extends TestCase
|
||||
$ref = self::ref([
|
||||
'post_meta_key' => ['before' => 'text', 'meta_key' => 'text', 'select_meta_key' => 'select'],
|
||||
'custom_meta__edit_lock' => ['before' => 'text'],
|
||||
'loop_post_meta_key' => ['loop_meta_key' => 'text'],
|
||||
]);
|
||||
'loop_post_meta_key_manual_custom_field' => ['loop_meta_key' => 'text'],
|
||||
], self::postLoop());
|
||||
$sanitizer = ComposeFixture::sanitizer();
|
||||
|
||||
self::assertSame(
|
||||
@@ -275,7 +349,7 @@ final class VariableRefTest extends TestCase
|
||||
self::content('post_meta_key', '{"meta_key":"_wp_page_template"}') => 'meta_key',
|
||||
self::content('post_meta_key', '{"select_meta_key":"custom_meta__edit_lock"}') => 'select_meta_key',
|
||||
self::content('post_meta_key', '{"meta_key":"a b"}') => 'meta_key',
|
||||
self::content('loop_post_meta_key', '{"loop_meta_key":"_secret"}') => 'loop_meta_key',
|
||||
self::loopMeta('{"loop_meta_key":"_secret"}') => 'loop_meta_key',
|
||||
self::content('custom_meta__edit_lock', '{}') => 'custom_meta__edit_lock',
|
||||
];
|
||||
foreach ($rejected as $value => $fragment) {
|
||||
@@ -300,20 +374,20 @@ final class VariableRefTest extends TestCase
|
||||
self::content('post_meta_key', '{"select_meta_key":"custom_meta_custom_meta__wp_page_template"}'),
|
||||
],
|
||||
'doubled prefix in meta_key' => [self::content('post_meta_key', '{"meta_key":"custom_meta_custom_meta__x"}')],
|
||||
'loop post prefix' => [self::content('loop_post_meta_key', '{"select_loop_meta_key":"loop_post_meta_key__edit_lock"}')],
|
||||
'loop user prefix' => [self::content('loop_post_meta_key', '{"select_loop_meta_key":"loop_user_meta_key__session_tokens"}')],
|
||||
'loop term prefix' => [self::content('loop_post_meta_key', '{"select_loop_meta_key":"loop_term_meta_key__secret"}')],
|
||||
'loop post prefix' => [self::loopMeta('{"select_loop_meta_key":"loop_post_meta_key__edit_lock"}')],
|
||||
'loop user prefix' => [self::loopMeta('{"select_loop_meta_key":"loop_user_meta_key__session_tokens"}')],
|
||||
'loop term prefix' => [self::loopMeta('{"select_loop_meta_key":"loop_term_meta_key__secret"}')],
|
||||
'doubled loop prefix' => [
|
||||
self::content('loop_post_meta_key', '{"select_loop_meta_key":"loop_post_meta_key_loop_post_meta_key__edit_lock"}'),
|
||||
self::loopMeta('{"select_loop_meta_key":"loop_post_meta_key_loop_post_meta_key__edit_lock"}'),
|
||||
],
|
||||
'mixed loop and meta prefix' => [
|
||||
self::content('loop_post_meta_key', '{"select_loop_meta_key":"loop_post_meta_key_custom_meta__edit_lock"}'),
|
||||
self::loopMeta('{"select_loop_meta_key":"loop_post_meta_key_custom_meta__edit_lock"}'),
|
||||
],
|
||||
'mixed meta and loop prefix' => [
|
||||
self::content('post_meta_key', '{"select_meta_key":"custom_meta_loop_term_meta_key__secret"}'),
|
||||
],
|
||||
'two loop prefixes' => [
|
||||
self::content('loop_post_meta_key', '{"select_loop_meta_key":"loop_user_meta_key_loop_post_meta_key_telefon"}'),
|
||||
self::loopMeta('{"select_loop_meta_key":"loop_user_meta_key_loop_post_meta_key_telefon"}'),
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -328,10 +402,12 @@ final class VariableRefTest extends TestCase
|
||||
{
|
||||
$ref = self::ref([
|
||||
'post_meta_key' => ['meta_key' => 'text', 'select_meta_key' => 'select'],
|
||||
'loop_post_meta_key' => ['select_loop_meta_key' => 'select', 'loop_meta_key' => 'text'],
|
||||
]);
|
||||
'loop_post_meta_key_manual_custom_field' => ['select_loop_meta_key' => 'select', 'loop_meta_key' => 'text'],
|
||||
], self::postLoop());
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
// The meta key rule refuses it – not the loop check (the post loop fits the post meta option).
|
||||
$this->expectExceptionMessageMatches('/Meta-Schlüssel|Unbekannter Name/');
|
||||
|
||||
$ref->canonical($value, ComposeFixture::sanitizer());
|
||||
}
|
||||
@@ -340,18 +416,17 @@ final class VariableRefTest extends TestCase
|
||||
{
|
||||
$ref = self::ref([
|
||||
'post_meta_key' => ['meta_key' => 'text', 'select_meta_key' => 'select'],
|
||||
'loop_post_meta_key' => ['select_loop_meta_key' => 'select', 'loop_meta_key' => 'text'],
|
||||
]);
|
||||
'loop_post_meta_key_manual_custom_field' => ['select_loop_meta_key' => 'select', 'loop_meta_key' => 'text'],
|
||||
], self::postLoop());
|
||||
$sanitizer = ComposeFixture::sanitizer();
|
||||
|
||||
foreach ([
|
||||
self::content('custom_meta_telefon', '{}'),
|
||||
self::content('post_meta_key', '{"select_meta_key":"custom_meta_fax"}'),
|
||||
self::content('loop_post_meta_key', '{"select_loop_meta_key":"loop_post_meta_key_telefon"}'),
|
||||
self::content('loop_post_meta_key', '{"select_loop_meta_key":"loop_user_meta_key_first_name"}'),
|
||||
self::content('loop_post_meta_key', '{"select_loop_meta_key":"loop_term_meta_key_farbe"}'),
|
||||
self::content(
|
||||
'loop_post_meta_key',
|
||||
self::loopMeta('{"select_loop_meta_key":"loop_post_meta_key_telefon"}'),
|
||||
self::loopMeta('{"select_loop_meta_key":"loop_user_meta_key_first_name"}'),
|
||||
self::loopMeta('{"select_loop_meta_key":"loop_term_meta_key_farbe"}'),
|
||||
self::loopMeta(
|
||||
'{"select_loop_meta_key":"loop_post_meta_key_manual_custom_field_value","loop_meta_key":"telefon"}'
|
||||
),
|
||||
] as $value) {
|
||||
|
||||
Reference in New Issue
Block a user