diff options
3 files changed, 104 insertions, 7 deletions
diff --git a/src/wp-admin/includes/media.php b/src/wp-admin/includes/media.php index 73a2585f84..5c0b5d0b38 100644 --- a/src/wp-admin/includes/media.php +++ b/src/wp-admin/includes/media.php @@ -2194,14 +2194,19 @@ function media_upload_form( $errors = null ) { $plupload_init['multi_selection'] = false; } - // Check if WebP images can be edited. - if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) { - $plupload_init['webp_upload_error'] = true; - } + /** This filter is documented in wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php */ + $prevent_unsupported_uploads = apply_filters( 'wp_prevent_unsupported_mime_type_uploads', true, null ); + + if ( $prevent_unsupported_uploads ) { + // Check if WebP images can be edited. + if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) { + $plupload_init['webp_upload_error'] = true; + } - // Check if AVIF images can be edited. - if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/avif' ) ) ) { - $plupload_init['avif_upload_error'] = true; + // Check if AVIF images can be edited. + if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/avif' ) ) ) { + $plupload_init['avif_upload_error'] = true; + } } /** diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index 0c98a729e4..4c49a1f335 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php @@ -134,6 +134,36 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { array( 'status' => rest_authorization_required_code() ) ); } + $files = $request->get_file_params(); + + /** + * Filter whether the server should prevent uploads for image types it doesn't support. Default true. + * + * Developers can use this filter to enable uploads of certain image types. By default image types that are not + * supported by the server are prevented from being uploaded. + * + * @since 6.8.0 + * + * @param bool $check_mime Whether to prevent uploads of unsupported image types. + * @param string|null $mime_type The mime type of the file being uploaded (if available). + */ + $prevent_unsupported_uploads = apply_filters( 'wp_prevent_unsupported_mime_type_uploads', true, isset( $files['file']['type'] ) ? $files['file']['type'] : null ); + + // If the upload is an image, check if the server can handle the mime type. + if ( + $prevent_unsupported_uploads && + isset( $files['file']['type'] ) && + str_starts_with( $files['file']['type'], 'image/' ) + ) { + // Check if the image editor supports the type. + if ( ! wp_image_editor_supports( array( 'mime_type' => $files['file']['type'] ) ) ) { + return new WP_Error( + 'rest_upload_image_type_not_supported', + __( 'The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading.' ), + array( 'status' => 400 ) + ); + } + } return true; } diff --git a/tests/phpunit/tests/rest-api/rest-attachments-controller.php b/tests/phpunit/tests/rest-api/rest-attachments-controller.php index 5794bf944d..1fdaaa26e1 100644 --- a/tests/phpunit/tests/rest-api/rest-attachments-controller.php +++ b/tests/phpunit/tests/rest-api/rest-attachments-controller.php @@ -28,6 +28,11 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control private static $test_file2; /** + * @var string The path to the AVIF test image. + */ + private static $test_avif_file; + + /** * @var array The recorded posts query clauses. */ protected $posts_clauses; @@ -72,6 +77,9 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control if ( file_exists( self::$test_file2 ) ) { unlink( self::$test_file2 ); } + if ( file_exists( self::$test_avif_file ) ) { + unlink( self::$test_avif_file ); + } self::delete_user( self::$editor_id ); self::delete_user( self::$author_id ); @@ -101,6 +109,12 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control copy( $orig_file2, self::$test_file2 ); } + $orig_avif_file = DIR_TESTDATA . '/images/avif-lossy.avif'; + self::$test_avif_file = get_temp_dir() . 'avif-lossy.avif'; + if ( ! file_exists( self::$test_avif_file ) ) { + copy( $orig_avif_file, self::$test_avif_file ); + } + add_filter( 'rest_pre_dispatch', array( $this, 'wpSetUpBeforeRequest' ), 10, 3 ); add_filter( 'posts_clauses', array( $this, 'save_posts_clauses' ), 10, 2 ); } @@ -2541,4 +2555,52 @@ class WP_Test_REST_Attachments_Controller extends WP_Test_REST_Post_Type_Control } ); } + + /** + * Test that uploading unsupported image types throws a `rest_upload_image_type_not_supported` error. + * + * @ticket 61167 + */ + public function test_upload_unsupported_image_type() { + + // Only run this test when the editor doesn't support AVIF. + if ( wp_image_editor_supports( array( 'AVIF' ) ) ) { + $this->markTestSkipped( 'The image editor suppports AVIF.' ); + } + + $request = new WP_REST_Request( 'POST', '/wp/v2/media' ); + + wp_set_current_user( self::$author_id ); + $request->set_header( 'Content-Type', 'image/avif' ); + $request->set_header( 'Content-Disposition', 'attachment; filename=avif-lossy.avif' ); + $request->set_body( file_get_contents( self::$test_avif_file ) ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertErrorResponse( 'rest_upload_image_type_not_supported', $response, 400 ); + } + + /** + * Test that the `wp_prevent_unsupported_image_uploads` filter enables uploading of unsupported image types. + * + * @ticket 61167 + */ + public function test_upload_unsupported_image_type_with_filter() { + + // Only run this test when the editor doesn't support AVIF. + if ( wp_image_editor_supports( array( 'AVIF' ) ) ) { + $this->markTestSkipped( 'The image editor suppports AVIF.' ); + } + + add_filter( 'wp_prevent_unsupported_image_uploads', '__return_false' ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/media' ); + + wp_set_current_user( self::$author_id ); + $request->set_header( 'Content-Type', 'image/avif' ); + $request->set_header( 'Content-Disposition', 'attachment; filename=avif-lossy.avif' ); + $request->set_body( file_get_contents( self::$test_avif_file ) ); + $response = rest_get_server()->dispatch( $request ); + + $this->assertSame( 201, $response->get_status() ); + } } |