['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 */ 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()); } } } } }