@@ -3330,6 +3330,111 @@ public function test_create_update_post_with_featured_media() {
33303330 $ this ->assertSame ( 0 , (int ) get_post_thumbnail_id ( $ new_post ->ID ) );
33313331 }
33323332
3333+ /**
3334+ * Data provider for featured media link permission tests.
3335+ *
3336+ * @return array
3337+ */
3338+ public function data_featured_media_link_permissions () {
3339+ return array (
3340+ 'unauthenticated user with draft parent attachment ' => array (
3341+ 'attachment_parent_status ' => 'draft ' ,
3342+ 'attachment_status ' => 'inherit ' ,
3343+ 'user_id ' => 0 ,
3344+ 'expect_link ' => false ,
3345+ ),
3346+ 'authenticated editor with draft parent attachment ' => array (
3347+ 'attachment_parent_status ' => 'draft ' ,
3348+ 'attachment_status ' => 'inherit ' ,
3349+ 'user_id ' => 'editor ' ,
3350+ 'expect_link ' => true ,
3351+ ),
3352+ 'unauthenticated user with published attachment ' => array (
3353+ 'attachment_parent_status ' => null ,
3354+ 'attachment_status ' => 'publish ' ,
3355+ 'user_id ' => 0 ,
3356+ 'expect_link ' => true ,
3357+ ),
3358+ );
3359+ }
3360+
3361+ /**
3362+ * Tests that featured media links respect attachment permissions.
3363+ *
3364+ * @ticket 64183
3365+ * @dataProvider data_featured_media_link_permissions
3366+ *
3367+ * @param string|null $attachment_parent_status Status of the attachment's parent post, or null for no parent.
3368+ * @param string $attachment_status Status to set on the attachment.
3369+ * @param int|string $user_id User ID (0 for unauthenticated) or 'editor' for editor role.
3370+ * @param bool $expect_link Whether the featured media link should be included.
3371+ */
3372+ public function test_get_item_featured_media_link_permissions ( $ attachment_parent_status , $ attachment_status , $ user_id , $ expect_link ) {
3373+ $ file = DIR_TESTDATA . '/images/canola.jpg ' ;
3374+
3375+ // Create attachment parent if needed.
3376+ $ parent_post_id = 0 ;
3377+ if ( null !== $ attachment_parent_status ) {
3378+ $ parent_post_id = self ::factory ()->post ->create (
3379+ array (
3380+ 'post_title ' => 'Parent Post ' ,
3381+ 'post_status ' => $ attachment_parent_status ,
3382+ )
3383+ );
3384+ }
3385+
3386+ // Create attachment.
3387+ $ attachment_id = self ::factory ()->attachment ->create_object (
3388+ $ file ,
3389+ $ parent_post_id ,
3390+ array (
3391+ 'post_mime_type ' => 'image/jpeg ' ,
3392+ )
3393+ );
3394+
3395+ // Set attachment status if different from default.
3396+ if ( 'publish ' === $ attachment_status ) {
3397+ wp_update_post (
3398+ array (
3399+ 'ID ' => $ attachment_id ,
3400+ 'post_status ' => 'publish ' ,
3401+ )
3402+ );
3403+ }
3404+
3405+ // Create published post with featured media.
3406+ $ published_post_id = self ::factory ()->post ->create (
3407+ array (
3408+ 'post_title ' => 'Published Post ' ,
3409+ 'post_status ' => 'publish ' ,
3410+ )
3411+ );
3412+ set_post_thumbnail ( $ published_post_id , $ attachment_id );
3413+
3414+ // Set current user.
3415+ if ( 'editor ' === $ user_id ) {
3416+ wp_set_current_user ( self ::$ editor_id );
3417+ } else {
3418+ wp_set_current_user ( $ user_id );
3419+ }
3420+
3421+ // Make request.
3422+ $ request = new WP_REST_Request ( 'GET ' , sprintf ( '/wp/v2/posts/%d ' , $ published_post_id ) );
3423+ $ response = rest_get_server ()->dispatch ( $ request );
3424+ $ links = $ response ->get_links ();
3425+
3426+ // Assert link presence based on expectation.
3427+ if ( $ expect_link ) {
3428+ $ this ->assertArrayHasKey ( 'https://api.w.org/featuredmedia ' , $ links );
3429+ $ this ->assertSame (
3430+ rest_url ( '/wp/v2/media/ ' . $ attachment_id ),
3431+ $ links ['https://api.w.org/featuredmedia ' ][0 ]['href ' ]
3432+ );
3433+ } else {
3434+ $ this ->assertArrayNotHasKey ( 'https://api.w.org/featuredmedia ' , $ links );
3435+ }
3436+ }
3437+
33333438 public function test_create_post_invalid_author () {
33343439 wp_set_current_user ( self ::$ editor_id );
33353440
0 commit comments