diff --git a/src/wp-includes/theme.php b/src/wp-includes/theme.php index 37db1db2d5115..66efdd5226775 100644 --- a/src/wp-includes/theme.php +++ b/src/wp-includes/theme.php @@ -1547,11 +1547,33 @@ function get_uploaded_header_images() { } } - set_transient( $transient_key, $header_images, HOUR_IN_SECONDS ); + set_transient( $transient_key, $header_images, DAY_IN_SECONDS ); return $header_images; } +/** + * Invalidates the cache for header images when a custom header image is changed or deleted. + * + * @since 6.7 + * + * @param int $meta_id The meta ID of the attachment meta data. + * @param int $post_id The post ID of the attachment. + * @param string $meta_key The meta key of the attachment meta data. + * @param mixed $meta_value The new value of the attachment meta data. + * + * @return void + */ +function invalidate_header_images_cache( $meta_id, $post_id, $meta_key, $meta_value ) { + if ( '_wp_attachment_is_custom_header' === $meta_key ) { + $stylesheet = get_option( 'stylesheet' ); + $transient_key = 'uploaded_header_images' . $stylesheet; + delete_transient( $transient_key ); + } +} +add_action( 'updated_post_meta', 'invalidate_header_images_cache', 10, 4 ); +add_action( 'deleted_post_meta', 'invalidate_header_images_cache', 10, 4 ); + /** * Gets the header image data. * diff --git a/tests/phpunit/tests/theme/getUploadedHeaderImages.php b/tests/phpunit/tests/theme/getUploadedHeaderImages.php index d4f20875dd3f7..a6d69b25932c7 100644 --- a/tests/phpunit/tests/theme/getUploadedHeaderImages.php +++ b/tests/phpunit/tests/theme/getUploadedHeaderImages.php @@ -1,6 +1,8 @@ custom_image_header = new Custom_Image_Header( '__return_null' ); + } + + /** + * @ticket 49446 + */ + public function test_invalidate_header_images_cache_meta_updated() { + $id = wp_insert_attachment( + array( + 'post_status' => 'publish', + 'post_title' => 'foo.png', + 'post_type' => 'post', + 'guid' => 'http://localhost/foo.png', + ) + ); + + // Create initial crop object. + $cropped_1 = 'foo-cropped-1.png'; + $object = wp_copy_parent_attachment_properties( $cropped_1, $id, 'custom-header' ); + + // Ensure no previous crop exists. + $previous = $this->custom_image_header->get_previous_crop( $object ); + $this->assertFalse( $previous ); + + // Create the initial crop attachment and set it as the header. + $cropped_1_id = $this->custom_image_header->insert_attachment( $object, $cropped_1 ); + $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet(); + update_post_meta( $cropped_1_id, $key, time() ); + update_post_meta( $cropped_1_id, '_wp_attachment_is_custom_header', get_stylesheet() ); + + $expected = array( + $cropped_1_id => array( + 'attachment_id' => $cropped_1_id, + 'url' => 'http://example.org/wp-content/uploads/foo-cropped-1.png', + 'thumbnail_url' => 'http://example.org/wp-content/uploads/foo-cropped-1.png', + 'alt_text' => '', + 'attachment_parent' => $id, + ), + ); + + $num_queries = get_num_queries() + 4; + + $this->assertSame( $expected, get_uploaded_header_images() ); + + $this->assertSame( $num_queries, get_num_queries() ); + update_post_meta( $cropped_1_id, '_wp_attachment_is_custom_header', 'updated' ); + + $stylesheet = get_option( 'stylesheet' ); + $transient_key = 'uploaded_header_images' . $stylesheet; + $this->assertFalse( get_transient( $transient_key ), 'cache cleared' ); + } + + /** + * @ticket 49446 + */ + public function test_invalidate_header_images_cache_meta_deleted() { + $id = wp_insert_attachment( + array( + 'post_status' => 'publish', + 'post_title' => 'foo.png', + 'post_type' => 'post', + 'guid' => 'http://localhost/foo.png', + ) + ); + + // Create initial crop object. + $cropped_1 = 'foo-cropped-1.png'; + $object = wp_copy_parent_attachment_properties( $cropped_1, $id, 'custom-header' ); + + // Ensure no previous crop exists. + $previous = $this->custom_image_header->get_previous_crop( $object ); + $this->assertFalse( $previous ); + + // Create the initial crop attachment and set it as the header. + $cropped_1_id = $this->custom_image_header->insert_attachment( $object, $cropped_1 ); + $key = '_wp_attachment_custom_header_last_used_' . get_stylesheet(); + update_post_meta( $cropped_1_id, $key, time() ); + update_post_meta( $cropped_1_id, '_wp_attachment_is_custom_header', get_stylesheet() ); + + $expected = array( + $cropped_1_id => array( + 'attachment_id' => $cropped_1_id, + 'url' => 'http://example.org/wp-content/uploads/foo-cropped-1.png', + 'thumbnail_url' => 'http://example.org/wp-content/uploads/foo-cropped-1.png', + 'alt_text' => '', + 'attachment_parent' => $id, + ), + ); + + $num_queries = get_num_queries() + 4; + + $this->assertSame( $expected, get_uploaded_header_images() ); + + $this->assertSame( $num_queries, get_num_queries() ); + delete_post_meta( $cropped_1_id, '_wp_attachment_is_custom_header' ); + + $stylesheet = get_option( 'stylesheet' ); + $transient_key = 'uploaded_header_images' . $stylesheet; + $this->assertFalse( get_transient( $transient_key ), 'cache cleared' ); + } +}