new WP_Post(['ID' => 839, 'post_type' => 'page', 'post_status' => 'draft']), 841 => new WP_Post(['ID' => 841, 'post_type' => 'page', 'post_status' => 'trash']), 842 => new WP_Post(['ID' => 842, 'post_type' => 'page', 'post_status' => 'private']), 843 => new WP_Post(['ID' => 843, 'post_type' => 'revision', 'post_status' => 'inherit']), 844 => new WP_Post(['ID' => 844, 'post_type' => 'page', 'post_status' => 'auto-draft']), 845 => new WP_Post(['ID' => 845, 'post_type' => 'attachment', 'post_status' => 'inherit', 'post_parent' => 0]), 846 => new WP_Post(['ID' => 846, 'post_type' => 'attachment', 'post_status' => 'trash']), 847 => new WP_Post(['ID' => 847, 'post_type' => 'attachment', 'post_status' => 'private']), ]; Functions\when('get_post')->alias(static fn (int $id): ?WP_Post => $posts[$id] ?? null); Functions\when('current_user_can')->alias( static fn (string $cap, int $id): bool => $cap === 'read_post' && $id !== 842 ); $links = new WpLinkTargets(self::linkable()); self::assertSame('page', $links->postTypeOf(839)); self::assertNull($links->postTypeOf(841), 'trashed'); self::assertNull($links->postTypeOf(842), 'not readable'); self::assertNull($links->postTypeOf(843), 'revision'); self::assertNull($links->postTypeOf(844), 'auto-draft'); self::assertNull($links->postTypeOf(900), 'missing'); self::assertNull($links->postTypeOf(845), 'unattached attachment'); self::assertNull($links->postTypeOf(846), 'trashed attachment'); self::assertNull($links->postTypeOf(847), 'attachment with an own status (ruling P10: no attachment)'); } /** Post types Divi offers post_link_url_ for in these tests (et_builder_get_public_post_types). */ private static function linkable(): \Closure { return static fn (string $type): bool => in_array($type, ['page', 'post', 'attachment', 'lit_person'], true); } /** * Ruling Q14: a type without a Divi link option (the library, internal "_" types, any other) is no * link target – answered like a missing ID, so {{link:}} tells nobody which IDs are library entries. */ public function testTypesWithoutADiviLinkOptionAreNotFound(): void { $posts = [ 1058 => new WP_Post(['ID' => 1058, 'post_type' => 'et_pb_layout', 'post_status' => 'publish']), 1070 => new WP_Post(['ID' => 1070, 'post_type' => '_internal', 'post_status' => 'publish']), 1071 => new WP_Post(['ID' => 1071, 'post_type' => 'wp_template', 'post_status' => 'publish']), 1072 => new WP_Post(['ID' => 1072, 'post_type' => 'lit_person', 'post_status' => 'publish']), ]; Functions\when('get_post')->alias(static fn (int $id): ?WP_Post => $posts[$id] ?? null); Functions\when('current_user_can')->justReturn(true); $everything = new WpLinkTargets(static fn (string $type): bool => true); $links = new WpLinkTargets(self::linkable()); self::assertNull($links->postTypeOf(1058), 'library entry'); self::assertNull($links->postTypeOf(1070), 'internal type'); self::assertNull($links->postTypeOf(1071), 'type without link option'); self::assertSame('lit_person', $links->postTypeOf(1072)); self::assertNull($everything->postTypeOf(1058), 'the library never, whatever Divi lists'); self::assertNull($everything->postTypeOf(1070), 'internal types never, whatever Divi lists'); self::assertNull((new WpLinkTargets())->postTypeOf(1072), 'without Divi no type has a link option'); } /** * Ruling Q14: {{link:}} and the full form answer a library ID exactly like a missing ID – with and * without the library right (read_post on the entry), so no guessed ID reveals a library entry. */ public function testALibraryIdIsAnsweredLikeAMissingId(): void { Functions\when('get_post')->alias(static fn (int $id): ?WP_Post => $id === 1058 ? new WP_Post(['ID' => 1058, 'post_type' => 'et_pb_layout', 'post_status' => 'publish']) : null); $links = new WpLinkTargets(self::linkable()); $placeholders = new Placeholders(DiviFixtures::designSystem(), $links); $ref = new VariableRef(DiviFixtures::designSystem(), ['post_link_url_page' => ['post_id' => 'select_post']]); $sanitizer = new WpValueSanitizer($links); $answer = static function (callable $call, string $id): string { try { $call($id); return 'accepted'; } catch (InvalidArgumentException $e) { return str_replace($id, '', $e->getMessage()); } }; $short = static fn (string $id): string => $placeholders->resolve('{{link:' . $id . '}}'); $full = static fn (string $id): string => $ref->canonical( '$variable({"type":"content","value":{"name":"post_link_url_page","settings":{"post_id":"' . $id . '"}}})$', $sanitizer, true ); foreach (['with the library right' => true, 'without it' => false] as $case => $right) { Functions\when('current_user_can')->justReturn($right); foreach (['{{link:}}' => $short, 'post_link_url_page' => $full] as $form => $call) { $library = $answer($call, '1058'); self::assertNotSame('accepted', $library, $case . ' ' . $form); self::assertSame($answer($call, '1066'), $library, $case . ' ' . $form); self::assertStringNotContainsString('et_pb_layout', $library, $case . ' ' . $form); } } } }