From 97298804f610ea672aa0ea39dd7c717d06e5fcd0 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 24 Jan 2025 13:30:27 +0000 Subject: [PATCH] Posts, Post Types: Embeds: Add new `embeddable` argument to post types. This new argument, which defaults to the value of `public`, can be used to determine whether a post can be embedded using oEmbed. A new `is_post_embeddable()` function is added to easily check this. Props pampfelimetten, swissspidy, bradleyt, DrewAPicture, gadelhas, mukesh27. Fixes #35567. git-svn-id: https://develop.svn.wordpress.org/trunk@59700 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-post-type.php | 16 +++++++ src/wp-includes/embed.php | 12 +++-- src/wp-includes/post.php | 34 ++++++++++++++ tests/phpunit/tests/oembed/discovery.php | 15 +++++++ tests/phpunit/tests/oembed/template.php | 56 ++++++++++++++++++++++++ tests/phpunit/tests/post/types.php | 30 +++++++++++++ 6 files changed, 160 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/class-wp-post-type.php b/src/wp-includes/class-wp-post-type.php index 5336d3e1cf8f3..400fd0b698c1e 100644 --- a/src/wp-includes/class-wp-post-type.php +++ b/src/wp-includes/class-wp-post-type.php @@ -113,6 +113,16 @@ final class WP_Post_Type { */ public $publicly_queryable = null; + /** + * Whether this post type is embeddable. + * + * Default is the value of $public. + * + * @since 6.8.0 + * @var bool $embeddable + */ + public $embeddable = null; + /** * Whether to generate and allow a UI for managing this post type in the admin. * @@ -521,6 +531,7 @@ public function set_props( $args ) { 'hierarchical' => false, 'exclude_from_search' => null, 'publicly_queryable' => null, + 'embeddable' => null, 'show_ui' => null, 'show_in_menu' => null, 'show_in_nav_menus' => null, @@ -565,6 +576,11 @@ public function set_props( $args ) { $args['show_ui'] = $args['public']; } + // If not set, default to the setting for 'public'. + if ( null === $args['embeddable'] ) { + $args['embeddable'] = $args['public']; + } + // If not set, default rest_namespace to wp/v2 if show_in_rest is true. if ( false === $args['rest_namespace'] && ! empty( $args['show_in_rest'] ) ) { $args['rest_namespace'] = 'wp/v2'; diff --git a/src/wp-includes/embed.php b/src/wp-includes/embed.php index f7fe10e48239d..b5b30acead880 100644 --- a/src/wp-includes/embed.php +++ b/src/wp-includes/embed.php @@ -331,11 +331,12 @@ function wp_oembed_register_route() { * Adds oEmbed discovery links in the head element of the website. * * @since 4.4.0 + * @since 6.8.0 Output was adjusted to only embed if the post supports it. */ function wp_oembed_add_discovery_links() { $output = ''; - if ( is_singular() ) { + if ( is_singular() && is_post_embeddable() ) { $output .= '' . "\n"; if ( class_exists( 'SimpleXMLElement' ) ) { @@ -538,11 +539,12 @@ function get_post_embed_html( $width, $height, $post = null ) { * Retrieves the oEmbed response data for a given post. * * @since 4.4.0 + * @since 6.8.0 Output was adjusted to only embed if the post type supports it. * * @param WP_Post|int $post Post ID or post object. * @param int $width The requested width. - * @return array|false Response data on success, false if post doesn't exist - * or is not publicly viewable. + * @return array|false Response data on success, false if post doesn't exist, + * is not publicly viewable or post type is not embeddable. */ function get_oembed_response_data( $post, $width ) { $post = get_post( $post ); @@ -556,6 +558,10 @@ function get_oembed_response_data( $post, $width ) { return false; } + if ( ! is_post_embeddable( $post ) ) { + return false; + } + /** * Filters the allowed minimum and maximum widths for the oEmbed response. * diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 009287916d7bb..a78e97b6c1dd9 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -2475,6 +2475,40 @@ function is_post_publicly_viewable( $post = null ) { return is_post_type_viewable( $post_type ) && is_post_status_viewable( $post_status ); } +/** + * Determines whether a post is embeddable. + * + * @since 6.8.0 + * + * @param int|WP_Post|null $post Optional. Post ID or `WP_Post` object. Defaults to global $post. + * @return bool Whether the post should be considered embeddable. + */ +function is_post_embeddable( $post = null ) { + $post = get_post( $post ); + + if ( ! $post ) { + return false; + } + + $post_type = get_post_type_object( $post->post_type ); + + if ( ! $post_type ) { + return false; + } + + $is_embeddable = $post_type->embeddable; + + /** + * Filter whether a post is embeddable. + * + * @since 6.8.0 + * + * @param bool $is_embeddable Whether the post is embeddable. + * @param WP_Post $post Post object. + */ + return apply_filters( 'is_post_embeddable', $is_embeddable, $post ); +} + /** * Retrieves an array of the latest posts, or posts matching the given criteria. * diff --git a/tests/phpunit/tests/oembed/discovery.php b/tests/phpunit/tests/oembed/discovery.php index 675d3264fcf71..41efe1cc2a378 100644 --- a/tests/phpunit/tests/oembed/discovery.php +++ b/tests/phpunit/tests/oembed/discovery.php @@ -85,4 +85,19 @@ public function test_add_oembed_discovery_links_to_attachment() { $this->assertSame( $expected, get_echo( 'wp_oembed_add_discovery_links' ) ); } + + /** + * @ticket 35567 + */ + public function test_wp_oembed_add_discovery_links_non_embeddable_post_type_output_should_be_empty() { + register_post_type( 'not_embeddable', array( 'embeddable' => false ) ); + + $post = self::factory()->post->create_and_get( + array( + 'post_type' => 'not_embeddable', + ) + ); + + $this->assertFalse( get_oembed_response_data( $post, 100 ) ); + } } diff --git a/tests/phpunit/tests/oembed/template.php b/tests/phpunit/tests/oembed/template.php index 8416b50c1b0a5..bce26bbcd8ddd 100644 --- a/tests/phpunit/tests/oembed/template.php +++ b/tests/phpunit/tests/oembed/template.php @@ -343,4 +343,60 @@ public function test_wp_maybe_enqueue_oembed_host_js_without_wp_head_action() { wp_maybe_enqueue_oembed_host_js( $post_embed ); $this->assertFalse( $scripts->query( 'wp-embed', 'enqueued' ) ); } + + /** + * @ticket 35567 + */ + public function test_is_embeddable_post_non_existent_post() { + $this->assertFalse( is_post_embeddable( 99999 ) ); + } + + /** + * @ticket 35567 + */ + public function test_is_embeddable_post_should_return_false_for_non_embeddable_post_type() { + register_post_type( 'not_embeddable', array( 'embeddable' => false ) ); + + $post = self::factory()->post->create_and_get( + array( + 'post_type' => 'not_embeddable', + ) + ); + + $this->assertFalse( is_post_embeddable( $post ) ); + } + + /** + * @ticket 35567 + */ + public function test_is_embeddable_post_should_return_true_for_embeddable_post_type() { + register_post_type( 'embeddable', array( 'embeddable' => true ) ); + + $post = self::factory()->post->create_and_get( + array( + 'post_type' => 'embeddable', + ) + ); + + $this->assertTrue( is_post_embeddable( $post ) ); + } + + /** + * @ticket 35567 + */ + public function test_is_embeddable_post_filtered() { + register_post_type( 'not_embeddable', array( 'embeddable' => false ) ); + + $post = self::factory()->post->create_and_get( + array( + 'post_type' => 'not_embeddable', + ) + ); + + add_filter( 'is_post_embeddable', '__return_true' ); + $embeddable = is_post_embeddable( $post ); + remove_filter( 'is_post_embeddable', '__return_true' ); + + $this->assertTrue( $embeddable ); + } } diff --git a/tests/phpunit/tests/post/types.php b/tests/phpunit/tests/post/types.php index 5aa1ca505a22f..96519586d6cff 100644 --- a/tests/phpunit/tests/post/types.php +++ b/tests/phpunit/tests/post/types.php @@ -646,4 +646,34 @@ public function test_removing_editor_support_does_not_remove_autosave_support() $this->assertFalse( post_type_supports( 'foo', 'editor' ), 'Post type should not support editor.' ); $this->assertTrue( post_type_supports( 'foo', 'autosave' ), 'Post type should still support autosaves.' ); } + + /** + * @group oembed + * @ticket 35567 + */ + public function test_register_post_type_is_embeddable_should_default_to_value_of_public() { + $post_type = register_post_type( $this->post_type ); + $this->assertFalse( $post_type->embeddable, 'Non-public post type should not be embeddable by default' ); + + $post_type = register_post_type( $this->post_type, array( 'public' => true ) ); + $this->assertTrue( $post_type->embeddable, 'Public post type should be embeddable by default' ); + } + + /** + * @group oembed + * @ticket 35567 + */ + public function test_register_post_type_override_is_embeddable() { + $post_type = register_post_type( $this->post_type, array( 'embeddable' => true ) ); + $this->assertTrue( $post_type->embeddable, 'Post type should be embeddable even though it is not public' ); + + $post_type = register_post_type( + $this->post_type, + array( + 'public' => true, + 'embeddable' => false, + ) + ); + $this->assertFalse( $post_type->embeddable, 'Post type should not be embeddable even though it is public' ); + } }