diff --git a/src/wp-admin/admin.php b/src/wp-admin/admin.php
index 1186f9bedce21..82ab6b93ac99e 100644
--- a/src/wp-admin/admin.php
+++ b/src/wp-admin/admin.php
@@ -395,17 +395,22 @@
*/
if ( 'page' === $typenow ) {
if ( 'post-new.php' === $pagenow ) {
+ /** This action is documented in wp-admin/admin.php */
do_action( 'load-page-new.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
} elseif ( 'post.php' === $pagenow ) {
+ /** This action is documented in wp-admin/admin.php */
do_action( 'load-page.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
}
} elseif ( 'edit-tags.php' === $pagenow ) {
if ( 'category' === $taxnow ) {
+ /** This action is documented in wp-admin/admin.php */
do_action( 'load-categories.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
} elseif ( 'link_category' === $taxnow ) {
+ /** This action is documented in wp-admin/admin.php */
do_action( 'load-edit-link-categories.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
}
} elseif ( 'term.php' === $pagenow ) {
+ /** This action is documented in wp-admin/admin.php */
do_action( 'load-edit-tags.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
}
}
diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php
index 18152756d5b73..c21e8d9e9feec 100644
--- a/src/wp-includes/block-editor.php
+++ b/src/wp-includes/block-editor.php
@@ -338,6 +338,7 @@ function _wp_get_iframed_editor_assets() {
* front-end assets for the content.
*/
add_filter( 'should_load_block_editor_scripts_and_styles', '__return_false' );
+ /** This action is documented in wp-includes/script-loader.php */
do_action( 'enqueue_block_assets' );
remove_filter( 'should_load_block_editor_scripts_and_styles', '__return_false' );
diff --git a/src/wp-includes/category-template.php b/src/wp-includes/category-template.php
index 790cd3e0d0486..5c304072ed5d8 100644
--- a/src/wp-includes/category-template.php
+++ b/src/wp-includes/category-template.php
@@ -1258,8 +1258,8 @@ function tag_description( $tag = 0 ) {
* @since 2.8.0
* @since 4.9.2 The `$taxonomy` parameter was deprecated.
*
- * @param int $term Optional. Term ID. Defaults to the current term ID.
- * @param null $deprecated Deprecated. Not used.
+ * @param int $term Optional. Term ID. Defaults to the current term ID.
+ * @param mixed $deprecated Not used.
* @return string Term description, if available.
*/
function term_description( $term = 0, $deprecated = null ) {
diff --git a/src/wp-includes/class-wp-admin-bar.php b/src/wp-includes/class-wp-admin-bar.php
index 0c6ab15553bb2..e1f7282f82ab9 100644
--- a/src/wp-includes/class-wp-admin-bar.php
+++ b/src/wp-includes/class-wp-admin-bar.php
@@ -169,7 +169,7 @@ public function add_node( $args ) {
'my-blogs' => array( 'my-sites', '3.3' ),
);
- if ( isset( $back_compat_parents[ $args['parent'] ] ) ) {
+ if ( is_string( $args['parent'] ) && isset( $back_compat_parents[ $args['parent'] ] ) ) {
list( $new_parent, $version ) = $back_compat_parents[ $args['parent'] ];
_deprecated_argument( __METHOD__, $version, sprintf( 'Use %s as the parent for the %s admin bar node instead of %s.', $new_parent, $args['id'], $args['parent'] ) );
$args['parent'] = $new_parent;
diff --git a/src/wp-includes/class-wp-block-parser.php b/src/wp-includes/class-wp-block-parser.php
index bf8a59249d99d..8c619a7b47f2c 100644
--- a/src/wp-includes/class-wp-block-parser.php
+++ b/src/wp-includes/class-wp-block-parser.php
@@ -318,7 +318,7 @@ public function freeform( $inner_html ) {
*
* @internal
* @since 5.0.0
- * @param null $length how many bytes of document text to output.
+ * @param null|int $length How many bytes of document text to output.
*/
public function add_freeform( $length = null ) {
$length = $length ? $length : strlen( $this->document ) - $this->offset;
diff --git a/src/wp-includes/class-wp-widget.php b/src/wp-includes/class-wp-widget.php
index e72b2798cbab9..5ad32f49378a8 100644
--- a/src/wp-includes/class-wp-widget.php
+++ b/src/wp-includes/class-wp-widget.php
@@ -546,9 +546,9 @@ public function form_callback( $widget_args = 1 ) {
*
* @since 2.8.0
*
- * @param WP_Widget $widget The widget instance (passed by reference).
- * @param null $return Return null if new fields are added.
- * @param array $instance An array of the widget's settings.
+ * @param WP_Widget $widget The widget instance (passed by reference).
+ * @param null|string $return Default 'noform'. Return null if new fields are added.
+ * @param array $instance An array of the widget's settings.
*/
do_action_ref_array( 'in_widget_form', array( &$this, &$return, $instance ) );
}
diff --git a/src/wp-includes/deprecated.php b/src/wp-includes/deprecated.php
index b4add654f1fe3..14f5c24aec914 100644
--- a/src/wp-includes/deprecated.php
+++ b/src/wp-includes/deprecated.php
@@ -150,6 +150,7 @@ function previous_post($format='%', $previous='previous post: ', $title='yes', $
$string = ''.$previous;
if ( 'yes' == $title )
+ /** This filter is documented in wp-includes/post-template.php */
$string .= apply_filters('the_title', $post->post_title, $post->ID);
$string .= '';
$format = str_replace('%', $string, $format);
@@ -185,6 +186,7 @@ function next_post($format='%', $next='next post: ', $title='yes', $in_same_cat=
$string = ''.$next;
if ( 'yes' == $title )
+ /** This filter is documented in wp-includes/post-template.php */
$string .= apply_filters('the_title', $post->post_title, $post->ID);
$string .= '';
$format = str_replace('%', $string, $format);
@@ -1060,6 +1062,7 @@ function get_links_list($order = 'name') {
// Handle each category.
// Display the category name.
+ /** This filter is documented in wp-includes/bookmark-template.php */
echo '
' . apply_filters('link_category', $cat->name ) . "
\n\t\n";
// Call get_links() with all the appropriate params.
get_links($cat->term_id, '- ', "
", "\n", true, 'name', false);
@@ -2702,6 +2705,7 @@ function get_boundary_post_rel_link($title = '%title', $in_same_cat = false, $ex
$title = str_replace('%title', $post->post_title, $title);
$title = str_replace('%date', $date, $title);
+ /** This filter is documented in wp-includes/post-template.php */
$title = apply_filters('the_title', $title, $post->ID);
$link = $start ? "post_title, $title);
$title = str_replace('%date', $date, $title);
+ /** This filter is documented in wp-includes/post-template.php */
$title = apply_filters('the_title', $title, $post->ID);
$link = "ID );
- if ( $featured_media ) {
+ if ( $featured_media && ( 'publish' === get_post_status( $featured_media ) || current_user_can( 'read_post', $featured_media ) ) ) {
$image_url = rest_url( rest_get_route_for_post( $featured_media ) );
$links['https://api.w.org/featuredmedia'] = array(
diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
index bf94550d7905c..73a888d6eac48 100644
--- a/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
+++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php
@@ -226,6 +226,8 @@ protected function get_revision( $id ) {
*
* @since 4.7.0
*
+ * @see WP_REST_Posts_Controller::get_items()
+ *
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
@@ -297,7 +299,17 @@ public function get_items( $request ) {
$args['update_post_meta_cache'] = false;
}
- /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php */
+ /**
+ * Filters WP_Query arguments when querying revisions via the REST API.
+ *
+ * Serves the same purpose as the {@see 'rest_{$this->post_type}_query'} filter in
+ * WP_REST_Posts_Controller, but for the standalone WP_REST_Revisions_Controller.
+ *
+ * @since 5.0.0
+ *
+ * @param array $args Array of arguments for WP_Query.
+ * @param WP_REST_Request $request The REST API request.
+ */
$args = apply_filters( 'rest_revision_query', $args, $request );
if ( ! is_array( $args ) ) {
$args = array();
diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php
index d701d12f9dd68..212ddde70dd83 100644
--- a/tests/phpunit/tests/rest-api/rest-posts-controller.php
+++ b/tests/phpunit/tests/rest-api/rest-posts-controller.php
@@ -3330,6 +3330,111 @@ public function test_create_update_post_with_featured_media() {
$this->assertSame( 0, (int) get_post_thumbnail_id( $new_post->ID ) );
}
+ /**
+ * Data provider for featured media link permission tests.
+ *
+ * @return array
+ */
+ public function data_featured_media_link_permissions() {
+ return array(
+ 'unauthenticated user with draft parent attachment' => array(
+ 'attachment_parent_status' => 'draft',
+ 'attachment_status' => 'inherit',
+ 'user_id' => 0,
+ 'expect_link' => false,
+ ),
+ 'authenticated editor with draft parent attachment' => array(
+ 'attachment_parent_status' => 'draft',
+ 'attachment_status' => 'inherit',
+ 'user_id' => 'editor',
+ 'expect_link' => true,
+ ),
+ 'unauthenticated user with published attachment' => array(
+ 'attachment_parent_status' => null,
+ 'attachment_status' => 'publish',
+ 'user_id' => 0,
+ 'expect_link' => true,
+ ),
+ );
+ }
+
+ /**
+ * Tests that featured media links respect attachment permissions.
+ *
+ * @ticket 64183
+ * @dataProvider data_featured_media_link_permissions
+ *
+ * @param string|null $attachment_parent_status Status of the attachment's parent post, or null for no parent.
+ * @param string $attachment_status Status to set on the attachment.
+ * @param int|string $user_id User ID (0 for unauthenticated) or 'editor' for editor role.
+ * @param bool $expect_link Whether the featured media link should be included.
+ */
+ public function test_get_item_featured_media_link_permissions( $attachment_parent_status, $attachment_status, $user_id, $expect_link ) {
+ $file = DIR_TESTDATA . '/images/canola.jpg';
+
+ // Create attachment parent if needed.
+ $parent_post_id = 0;
+ if ( null !== $attachment_parent_status ) {
+ $parent_post_id = self::factory()->post->create(
+ array(
+ 'post_title' => 'Parent Post',
+ 'post_status' => $attachment_parent_status,
+ )
+ );
+ }
+
+ // Create attachment.
+ $attachment_id = self::factory()->attachment->create_object(
+ $file,
+ $parent_post_id,
+ array(
+ 'post_mime_type' => 'image/jpeg',
+ )
+ );
+
+ // Set attachment status if different from default.
+ if ( 'publish' === $attachment_status ) {
+ wp_update_post(
+ array(
+ 'ID' => $attachment_id,
+ 'post_status' => 'publish',
+ )
+ );
+ }
+
+ // Create published post with featured media.
+ $published_post_id = self::factory()->post->create(
+ array(
+ 'post_title' => 'Published Post',
+ 'post_status' => 'publish',
+ )
+ );
+ set_post_thumbnail( $published_post_id, $attachment_id );
+
+ // Set current user.
+ if ( 'editor' === $user_id ) {
+ wp_set_current_user( self::$editor_id );
+ } else {
+ wp_set_current_user( $user_id );
+ }
+
+ // Make request.
+ $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $published_post_id ) );
+ $response = rest_get_server()->dispatch( $request );
+ $links = $response->get_links();
+
+ // Assert link presence based on expectation.
+ if ( $expect_link ) {
+ $this->assertArrayHasKey( 'https://api.w.org/featuredmedia', $links );
+ $this->assertSame(
+ rest_url( '/wp/v2/media/' . $attachment_id ),
+ $links['https://api.w.org/featuredmedia'][0]['href']
+ );
+ } else {
+ $this->assertArrayNotHasKey( 'https://api.w.org/featuredmedia', $links );
+ }
+ }
+
public function test_create_post_invalid_author() {
wp_set_current_user( self::$editor_id );
diff --git a/tests/phpunit/tests/rest-api/wpRestAbilitiesV1CategoriesController.php b/tests/phpunit/tests/rest-api/wpRestAbilitiesV1CategoriesController.php
index 8a93c7a64047d..43525263ac5ba 100644
--- a/tests/phpunit/tests/rest-api/wpRestAbilitiesV1CategoriesController.php
+++ b/tests/phpunit/tests/rest-api/wpRestAbilitiesV1CategoriesController.php
@@ -6,7 +6,7 @@
* @covers WP_REST_Abilities_V1_Categories_Controller
*
* @group abilities-api
- * @group rest-api
+ * @group restapi
*/
class Tests_REST_API_WpRestAbilitiesV1CategoriesController extends WP_UnitTestCase {
diff --git a/tests/phpunit/tests/rest-api/wpRestAbilitiesV1ListController.php b/tests/phpunit/tests/rest-api/wpRestAbilitiesV1ListController.php
index e64965242dc98..9ee564ef00069 100644
--- a/tests/phpunit/tests/rest-api/wpRestAbilitiesV1ListController.php
+++ b/tests/phpunit/tests/rest-api/wpRestAbilitiesV1ListController.php
@@ -6,7 +6,7 @@
* @covers WP_REST_Abilities_V1_List_Controller
*
* @group abilities-api
- * @group rest-api
+ * @group restapi
*/
class Tests_REST_API_WpRestAbilitiesV1ListController extends WP_UnitTestCase {
diff --git a/tests/phpunit/tests/rest-api/wpRestAbilitiesV1RunController.php b/tests/phpunit/tests/rest-api/wpRestAbilitiesV1RunController.php
index 0c03d72dab8a5..609b7677d7b58 100644
--- a/tests/phpunit/tests/rest-api/wpRestAbilitiesV1RunController.php
+++ b/tests/phpunit/tests/rest-api/wpRestAbilitiesV1RunController.php
@@ -6,7 +6,7 @@
* @covers WP_REST_Abilities_V1_Run_Controller
*
* @group abilities-api
- * @group rest-api
+ * @group restapi
*/
class Tests_REST_API_WpRestAbilitiesV1RunController extends WP_UnitTestCase {