--- a/wp/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php Tue Oct 22 16:11:46 2019 +0200
+++ b/wp/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php Tue Dec 15 13:49:49 2020 +0100
@@ -17,6 +17,47 @@
class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller {
/**
+ * Registers the routes for attachments.
+ *
+ * @since 5.3.0
+ *
+ * @see register_rest_route()
+ */
+ public function register_routes() {
+ parent::register_routes();
+ register_rest_route(
+ $this->namespace,
+ '/' . $this->rest_base . '/(?P<id>[\d]+)/post-process',
+ array(
+ 'methods' => WP_REST_Server::CREATABLE,
+ 'callback' => array( $this, 'post_process_item' ),
+ 'permission_callback' => array( $this, 'post_process_item_permissions_check' ),
+ 'args' => array(
+ 'id' => array(
+ 'description' => __( 'Unique identifier for the object.' ),
+ 'type' => 'integer',
+ ),
+ 'action' => array(
+ 'type' => 'string',
+ 'enum' => array( 'create-image-subsizes' ),
+ 'required' => true,
+ ),
+ ),
+ )
+ );
+ register_rest_route(
+ $this->namespace,
+ '/' . $this->rest_base . '/(?P<id>[\d]+)/edit',
+ array(
+ 'methods' => WP_REST_Server::CREATABLE,
+ 'callback' => array( $this, 'edit_media_item' ),
+ 'permission_callback' => array( $this, 'edit_media_item_permissions_check' ),
+ 'args' => $this->get_edit_media_item_args(),
+ )
+ );
+ }
+
+ /**
* Determines the allowed query_vars for a get_items() response and
* prepares for WP_Query.
*
@@ -60,7 +101,7 @@
* @since 4.7.0
*
* @param WP_REST_Request $request Full details about the request.
- * @return WP_Error|true Boolean true if the attachment may be created, or a WP_Error if not.
+ * @return true|WP_Error Boolean true if the attachment may be created, or a WP_Error if not.
*/
public function create_item_permissions_check( $request ) {
$ret = parent::create_item_permissions_check( $request );
@@ -70,17 +111,20 @@
}
if ( ! current_user_can( 'upload_files' ) ) {
- return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to upload media on this site.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_cannot_create',
+ __( 'Sorry, you are not allowed to upload media on this site.' ),
+ array( 'status' => 400 )
+ );
}
// Attaching media to a post requires ability to edit said post.
- if ( ! empty( $request['post'] ) ) {
- $parent = get_post( (int) $request['post'] );
- $post_parent_type = get_post_type_object( $parent->post_type );
-
- if ( ! current_user_can( $post_parent_type->cap->edit_post, $request['post'] ) ) {
- return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to upload media to this post.' ), array( 'status' => rest_authorization_required_code() ) );
- }
+ if ( ! empty( $request['post'] ) && ! current_user_can( 'edit_post', (int) $request['post'] ) ) {
+ return new WP_Error(
+ 'rest_cannot_edit',
+ __( 'Sorry, you are not allowed to upload media to this post.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
}
return true;
@@ -92,14 +136,92 @@
* @since 4.7.0
*
* @param WP_REST_Request $request Full details about the request.
- * @return WP_Error|WP_REST_Response Response object on success, WP_Error object on failure.
+ * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure.
*/
public function create_item( $request ) {
+ if ( ! empty( $request['post'] ) && in_array( get_post_type( $request['post'] ), array( 'revision', 'attachment' ), true ) ) {
+ return new WP_Error(
+ 'rest_invalid_param',
+ __( 'Invalid parent type.' ),
+ array( 'status' => 400 )
+ );
+ }
- if ( ! empty( $request['post'] ) && in_array( get_post_type( $request['post'] ), array( 'revision', 'attachment' ), true ) ) {
- return new WP_Error( 'rest_invalid_param', __( 'Invalid parent type.' ), array( 'status' => 400 ) );
+ $insert = $this->insert_attachment( $request );
+
+ if ( is_wp_error( $insert ) ) {
+ return $insert;
+ }
+
+ $schema = $this->get_item_schema();
+
+ // Extract by name.
+ $attachment_id = $insert['attachment_id'];
+ $file = $insert['file'];
+
+ if ( isset( $request['alt_text'] ) ) {
+ update_post_meta( $attachment_id, '_wp_attachment_image_alt', sanitize_text_field( $request['alt_text'] ) );
+ }
+
+ if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
+ $meta_update = $this->meta->update_value( $request['meta'], $attachment_id );
+
+ if ( is_wp_error( $meta_update ) ) {
+ return $meta_update;
+ }
+ }
+
+ $attachment = get_post( $attachment_id );
+ $fields_update = $this->update_additional_fields_for_object( $attachment, $request );
+
+ if ( is_wp_error( $fields_update ) ) {
+ return $fields_update;
}
+ $request->set_param( 'context', 'edit' );
+
+ /**
+ * Fires after a single attachment is completely created or updated via the REST API.
+ *
+ * @since 5.0.0
+ *
+ * @param WP_Post $attachment Inserted or updated attachment object.
+ * @param WP_REST_Request $request Request object.
+ * @param bool $creating True when creating an attachment, false when updating.
+ */
+ do_action( 'rest_after_insert_attachment', $attachment, $request, true );
+
+ if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
+ // Set a custom header with the attachment_id.
+ // Used by the browser/client to resume creating image sub-sizes after a PHP fatal error.
+ header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id );
+ }
+
+ // Include media and image functions to get access to wp_generate_attachment_metadata().
+ require_once ABSPATH . 'wp-admin/includes/media.php';
+ require_once ABSPATH . 'wp-admin/includes/image.php';
+
+ // Post-process the upload (create image sub-sizes, make PDF thumbnails, etc.) and insert attachment meta.
+ // At this point the server may run out of resources and post-processing of uploaded images may fail.
+ wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file ) );
+
+ $response = $this->prepare_item_for_response( $attachment, $request );
+ $response = rest_ensure_response( $response );
+ $response->set_status( 201 );
+ $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $attachment_id ) ) );
+
+ return $response;
+ }
+
+ /**
+ * Inserts the attachment post in the database. Does not update the attachment meta.
+ *
+ * @since 5.3.0
+ *
+ * @param WP_REST_Request $request
+ * @return array|WP_Error
+ */
+ protected function insert_attachment( $request ) {
// Get the file via $_FILES or raw data.
$files = $request->get_file_params();
$headers = $request->get_headers();
@@ -125,7 +247,7 @@
// Include image functions to get access to wp_read_image_metadata().
require_once ABSPATH . 'wp-admin/includes/image.php';
- // use image exif/iptc data for title and caption defaults if possible
+ // Use image exif/iptc data for title and caption defaults if possible.
$image_meta = wp_read_image_metadata( $file );
if ( ! empty( $image_meta ) ) {
@@ -138,7 +260,8 @@
}
}
- $attachment = $this->prepare_item_for_database( $request );
+ $attachment = $this->prepare_item_for_database( $request );
+
$attachment->post_mime_type = $type;
$attachment->guid = $url;
@@ -155,6 +278,7 @@
} else {
$id->add_data( array( 'status' => 400 ) );
}
+
return $id;
}
@@ -172,40 +296,10 @@
*/
do_action( 'rest_insert_attachment', $attachment, $request, true );
- // Include admin function to get access to wp_generate_attachment_metadata().
- require_once ABSPATH . 'wp-admin/includes/media.php';
-
- wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) );
-
- if ( isset( $request['alt_text'] ) ) {
- update_post_meta( $id, '_wp_attachment_image_alt', sanitize_text_field( $request['alt_text'] ) );
- }
-
- $fields_update = $this->update_additional_fields_for_object( $attachment, $request );
-
- if ( is_wp_error( $fields_update ) ) {
- return $fields_update;
- }
-
- $request->set_param( 'context', 'edit' );
-
- /**
- * Fires after a single attachment is completely created or updated via the REST API.
- *
- * @since 5.0.0
- *
- * @param WP_Post $attachment Inserted or updated attachment object.
- * @param WP_REST_Request $request Request object.
- * @param bool $creating True when creating an attachment, false when updating.
- */
- do_action( 'rest_after_insert_attachment', $attachment, $request, true );
-
- $response = $this->prepare_item_for_response( $attachment, $request );
- $response = rest_ensure_response( $response );
- $response->set_status( 201 );
- $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $id ) ) );
-
- return $response;
+ return array(
+ 'attachment_id' => $id,
+ 'file' => $file,
+ );
}
/**
@@ -214,11 +308,15 @@
* @since 4.7.0
*
* @param WP_REST_Request $request Full details about the request.
- * @return WP_Error|WP_REST_Response Response object on success, WP_Error object on failure.
+ * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure.
*/
public function update_item( $request ) {
if ( ! empty( $request['post'] ) && in_array( get_post_type( $request['post'] ), array( 'revision', 'attachment' ), true ) ) {
- return new WP_Error( 'rest_invalid_param', __( 'Invalid parent type.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_invalid_param',
+ __( 'Invalid parent type.' ),
+ array( 'status' => 400 )
+ );
}
$response = parent::update_item( $request );
@@ -254,17 +352,298 @@
}
/**
+ * Performs post processing on an attachment.
+ *
+ * @since 5.3.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure.
+ */
+ public function post_process_item( $request ) {
+ switch ( $request['action'] ) {
+ case 'create-image-subsizes':
+ require_once ABSPATH . 'wp-admin/includes/image.php';
+ wp_update_image_subsizes( $request['id'] );
+ break;
+ }
+
+ $request['context'] = 'edit';
+
+ return $this->prepare_item_for_response( get_post( $request['id'] ), $request );
+ }
+
+ /**
+ * Checks if a given request can perform post processing on an attachment.
+ *
+ * @since 5.3.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
+ */
+ public function post_process_item_permissions_check( $request ) {
+ return $this->update_item_permissions_check( $request );
+ }
+
+ /**
+ * Checks if a given request has access to editing media.
+ *
+ * @since 5.5.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
+ */
+ public function edit_media_item_permissions_check( $request ) {
+ if ( ! current_user_can( 'upload_files' ) ) {
+ return new WP_Error(
+ 'rest_cannot_edit_image',
+ __( 'Sorry, you are not allowed to upload media on this site.' ),
+ array( 'status' => rest_authorization_required_code() )
+ );
+ }
+
+ return $this->update_item_permissions_check( $request );
+ }
+
+ /**
+ * Applies edits to a media item and creates a new attachment record.
+ *
+ * @since 5.5.0
+ *
+ * @param WP_REST_Request $request Full details about the request.
+ * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure.
+ */
+ public function edit_media_item( $request ) {
+ require_once ABSPATH . 'wp-admin/includes/image.php';
+
+ $attachment_id = $request['id'];
+
+ // This also confirms the attachment is an image.
+ $image_file = wp_get_original_image_path( $attachment_id );
+ $image_meta = wp_get_attachment_metadata( $attachment_id );
+
+ if (
+ ! $image_meta ||
+ ! $image_file ||
+ ! wp_image_file_matches_image_meta( $request['src'], $image_meta, $attachment_id )
+ ) {
+ return new WP_Error(
+ 'rest_unknown_attachment',
+ __( 'Unable to get meta information for file.' ),
+ array( 'status' => 404 )
+ );
+ }
+
+ $supported_types = array( 'image/jpeg', 'image/png', 'image/gif' );
+ $mime_type = get_post_mime_type( $attachment_id );
+ if ( ! in_array( $mime_type, $supported_types, true ) ) {
+ return new WP_Error(
+ 'rest_cannot_edit_file_type',
+ __( 'This type of file cannot be edited.' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ // Check if we need to do anything.
+ $rotate = 0;
+ $crop = false;
+
+ if ( ! empty( $request['rotation'] ) ) {
+ // Rotation direction: clockwise vs. counter clockwise.
+ $rotate = 0 - (int) $request['rotation'];
+ }
+
+ if ( isset( $request['x'], $request['y'], $request['width'], $request['height'] ) ) {
+ $crop = true;
+ }
+
+ if ( ! $rotate && ! $crop ) {
+ return new WP_Error(
+ 'rest_image_not_edited',
+ __( 'The image was not edited. Edit the image before applying the changes.' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ /*
+ * If the file doesn't exist, attempt a URL fopen on the src link.
+ * This can occur with certain file replication plugins.
+ * Keep the original file path to get a modified name later.
+ */
+ $image_file_to_edit = $image_file;
+ if ( ! file_exists( $image_file_to_edit ) ) {
+ $image_file_to_edit = _load_image_to_edit_path( $attachment_id );
+ }
+
+ $image_editor = wp_get_image_editor( $image_file_to_edit );
+
+ if ( is_wp_error( $image_editor ) ) {
+ return new WP_Error(
+ 'rest_unknown_image_file_type',
+ __( 'Unable to edit this image.' ),
+ array( 'status' => 500 )
+ );
+ }
+
+ if ( 0 !== $rotate ) {
+ $result = $image_editor->rotate( $rotate );
+
+ if ( is_wp_error( $result ) ) {
+ return new WP_Error(
+ 'rest_image_rotation_failed',
+ __( 'Unable to rotate this image.' ),
+ array( 'status' => 500 )
+ );
+ }
+ }
+
+ if ( $crop ) {
+ $size = $image_editor->get_size();
+
+ $crop_x = round( ( $size['width'] * floatval( $request['x'] ) ) / 100.0 );
+ $crop_y = round( ( $size['height'] * floatval( $request['y'] ) ) / 100.0 );
+ $width = round( ( $size['width'] * floatval( $request['width'] ) ) / 100.0 );
+ $height = round( ( $size['height'] * floatval( $request['height'] ) ) / 100.0 );
+
+ $result = $image_editor->crop( $crop_x, $crop_y, $width, $height );
+
+ if ( is_wp_error( $result ) ) {
+ return new WP_Error(
+ 'rest_image_crop_failed',
+ __( 'Unable to crop this image.' ),
+ array( 'status' => 500 )
+ );
+ }
+ }
+
+ // Calculate the file name.
+ $image_ext = pathinfo( $image_file, PATHINFO_EXTENSION );
+ $image_name = wp_basename( $image_file, ".{$image_ext}" );
+
+ // Do not append multiple `-edited` to the file name.
+ // The user may be editing a previously edited image.
+ if ( preg_match( '/-edited(-\d+)?$/', $image_name ) ) {
+ // Remove any `-1`, `-2`, etc. `wp_unique_filename()` will add the proper number.
+ $image_name = preg_replace( '/-edited(-\d+)?$/', '-edited', $image_name );
+ } else {
+ // Append `-edited` before the extension.
+ $image_name .= '-edited';
+ }
+
+ $filename = "{$image_name}.{$image_ext}";
+
+ // Create the uploads sub-directory if needed.
+ $uploads = wp_upload_dir();
+
+ // Make the file name unique in the (new) upload directory.
+ $filename = wp_unique_filename( $uploads['path'], $filename );
+
+ // Save to disk.
+ $saved = $image_editor->save( $uploads['path'] . "/$filename" );
+
+ if ( is_wp_error( $saved ) ) {
+ return $saved;
+ }
+
+ // Create new attachment post.
+ $new_attachment_post = array(
+ 'post_mime_type' => $saved['mime-type'],
+ 'guid' => $uploads['url'] . "/$filename",
+ 'post_title' => $image_name,
+ 'post_content' => '',
+ );
+
+ // Copy post_content, post_excerpt, and post_title from the edited image's attachment post.
+ $attachment_post = get_post( $attachment_id );
+
+ if ( $attachment_post ) {
+ $new_attachment_post['post_content'] = $attachment_post->post_content;
+ $new_attachment_post['post_excerpt'] = $attachment_post->post_excerpt;
+ $new_attachment_post['post_title'] = $attachment_post->post_title;
+ }
+
+ $new_attachment_id = wp_insert_attachment( wp_slash( $new_attachment_post ), $saved['path'], 0, true );
+
+ if ( is_wp_error( $new_attachment_id ) ) {
+ if ( 'db_update_error' === $new_attachment_id->get_error_code() ) {
+ $new_attachment_id->add_data( array( 'status' => 500 ) );
+ } else {
+ $new_attachment_id->add_data( array( 'status' => 400 ) );
+ }
+
+ return $new_attachment_id;
+ }
+
+ // Copy the image alt text from the edited image.
+ $image_alt = get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
+
+ if ( ! empty( $image_alt ) ) {
+ // update_post_meta() expects slashed.
+ update_post_meta( $new_attachment_id, '_wp_attachment_image_alt', wp_slash( $image_alt ) );
+ }
+
+ if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
+ // Set a custom header with the attachment_id.
+ // Used by the browser/client to resume creating image sub-sizes after a PHP fatal error.
+ header( 'X-WP-Upload-Attachment-ID: ' . $new_attachment_id );
+ }
+
+ // Generate image sub-sizes and meta.
+ $new_image_meta = wp_generate_attachment_metadata( $new_attachment_id, $saved['path'] );
+
+ // Copy the EXIF metadata from the original attachment if not generated for the edited image.
+ if ( isset( $image_meta['image_meta'] ) && isset( $new_image_meta['image_meta'] ) && is_array( $new_image_meta['image_meta'] ) ) {
+ // Merge but skip empty values.
+ foreach ( (array) $image_meta['image_meta'] as $key => $value ) {
+ if ( empty( $new_image_meta['image_meta'][ $key ] ) && ! empty( $value ) ) {
+ $new_image_meta['image_meta'][ $key ] = $value;
+ }
+ }
+ }
+
+ // Reset orientation. At this point the image is edited and orientation is correct.
+ if ( ! empty( $new_image_meta['image_meta']['orientation'] ) ) {
+ $new_image_meta['image_meta']['orientation'] = 1;
+ }
+
+ // The attachment_id may change if the site is exported and imported.
+ $new_image_meta['parent_image'] = array(
+ 'attachment_id' => $attachment_id,
+ // Path to the originally uploaded image file relative to the uploads directory.
+ 'file' => _wp_relative_upload_path( $image_file ),
+ );
+
+ /**
+ * Filters the meta data for the new image created by editing an existing image.
+ *
+ * @since 5.5.0
+ *
+ * @param array $new_image_meta Meta data for the new image.
+ * @param int $new_attachment_id Attachment post ID for the new image.
+ * @param int $attachment_id Attachment post ID for the edited (parent) image.
+ */
+ $new_image_meta = apply_filters( 'wp_edited_image_metadata', $new_image_meta, $new_attachment_id, $attachment_id );
+
+ wp_update_attachment_metadata( $new_attachment_id, $new_image_meta );
+
+ $response = $this->prepare_item_for_response( get_post( $new_attachment_id ), $request );
+ $response->set_status( 201 );
+ $response->header( 'Location', rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $new_attachment_id ) ) );
+
+ return $response;
+ }
+
+ /**
* Prepares a single attachment for create or update.
*
* @since 4.7.0
*
* @param WP_REST_Request $request Request object.
- * @return WP_Error|stdClass $prepared_attachment Post object.
+ * @return stdClass|WP_Error Post object.
*/
protected function prepare_item_for_database( $request ) {
$prepared_attachment = parent::prepare_item_for_database( $request );
- // Attachment caption (post_excerpt internally)
+ // Attachment caption (post_excerpt internally).
if ( isset( $request['caption'] ) ) {
if ( is_string( $request['caption'] ) ) {
$prepared_attachment->post_excerpt = $request['caption'];
@@ -273,7 +652,7 @@
}
}
- // Attachment description (post_content internally)
+ // Attachment description (post_content internally).
if ( isset( $request['description'] ) ) {
if ( is_string( $request['description'] ) ) {
$prepared_attachment->post_content = $request['description'];
@@ -313,7 +692,11 @@
if ( in_array( 'caption', $fields, true ) ) {
/** This filter is documented in wp-includes/post-template.php */
- $caption = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $post->post_excerpt, $post ) );
+ $caption = apply_filters( 'get_the_excerpt', $post->post_excerpt, $post );
+
+ /** This filter is documented in wp-includes/post-template.php */
+ $caption = apply_filters( 'the_excerpt', $caption );
+
$data['caption'] = array(
'raw' => $post->post_excerpt,
'rendered' => $caption,
@@ -380,6 +763,11 @@
$data['source_url'] = wp_get_attachment_url( $post->ID );
}
+ if ( in_array( 'missing_image_sizes', $fields, true ) ) {
+ require_once ABSPATH . 'wp-admin/includes/image.php';
+ $data['missing_image_sizes'] = array_keys( wp_get_missing_image_subsizes( $post->ID ) );
+ }
+
$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
$data = $this->filter_response_by_context( $data, $context );
@@ -417,6 +805,9 @@
* @return array Item schema as an array.
*/
public function get_item_schema() {
+ if ( $this->schema ) {
+ return $this->add_additional_fields_schema( $this->schema );
+ }
$schema = parent::get_item_schema();
@@ -434,8 +825,8 @@
'type' => 'object',
'context' => array( 'view', 'edit', 'embed' ),
'arg_options' => array(
- 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
- 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
+ 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database().
+ 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database().
),
'properties' => array(
'raw' => array(
@@ -457,8 +848,8 @@
'type' => 'object',
'context' => array( 'view', 'edit' ),
'arg_options' => array(
- 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database()
- 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database()
+ 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database().
+ 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database().
),
'properties' => array(
'raw' => array(
@@ -511,9 +902,19 @@
'readonly' => true,
);
+ $schema['properties']['missing_image_sizes'] = array(
+ 'description' => __( 'List of the missing image sizes of the attachment.' ),
+ 'type' => 'array',
+ 'items' => array( 'type' => 'string' ),
+ 'context' => array( 'edit' ),
+ 'readonly' => true,
+ );
+
unset( $schema['properties']['password'] );
- return $schema;
+ $this->schema = $schema;
+
+ return $this->add_additional_fields_schema( $this->schema );
}
/**
@@ -527,21 +928,37 @@
*/
protected function upload_from_data( $data, $headers ) {
if ( empty( $data ) ) {
- return new WP_Error( 'rest_upload_no_data', __( 'No data supplied.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_upload_no_data',
+ __( 'No data supplied.' ),
+ array( 'status' => 400 )
+ );
}
if ( empty( $headers['content_type'] ) ) {
- return new WP_Error( 'rest_upload_no_content_type', __( 'No Content-Type supplied.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_upload_no_content_type',
+ __( 'No Content-Type supplied.' ),
+ array( 'status' => 400 )
+ );
}
if ( empty( $headers['content_disposition'] ) ) {
- return new WP_Error( 'rest_upload_no_content_disposition', __( 'No Content-Disposition supplied.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_upload_no_content_disposition',
+ __( 'No Content-Disposition supplied.' ),
+ array( 'status' => 400 )
+ );
}
$filename = self::get_filename_from_disposition( $headers['content_disposition'] );
if ( empty( $filename ) ) {
- return new WP_Error( 'rest_upload_invalid_disposition', __( 'Invalid Content-Disposition supplied. Content-Disposition needs to be formatted as `attachment; filename="image.png"` or similar.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_upload_invalid_disposition',
+ __( 'Invalid Content-Disposition supplied. Content-Disposition needs to be formatted as `attachment; filename="image.png"` or similar.' ),
+ array( 'status' => 400 )
+ );
}
if ( ! empty( $headers['content_md5'] ) ) {
@@ -550,14 +967,18 @@
$actual = md5( $data );
if ( $expected !== $actual ) {
- return new WP_Error( 'rest_upload_hash_mismatch', __( 'Content hash did not match expected.' ), array( 'status' => 412 ) );
+ return new WP_Error(
+ 'rest_upload_hash_mismatch',
+ __( 'Content hash did not match expected.' ),
+ array( 'status' => 412 )
+ );
}
}
// Get the content-type.
$type = array_shift( $headers['content_type'] );
- /** Include admin functions to get access to wp_tempnam() and wp_handle_sideload(). */
+ // Include filesystem functions to get access to wp_tempnam() and wp_handle_sideload().
require_once ABSPATH . 'wp-admin/includes/file.php';
// Save the file.
@@ -566,7 +987,11 @@
$fp = fopen( $tmpfname, 'w+' );
if ( ! $fp ) {
- return new WP_Error( 'rest_upload_file_error', __( 'Could not open file handle.' ), array( 'status' => 500 ) );
+ return new WP_Error(
+ 'rest_upload_file_error',
+ __( 'Could not open file handle.' ),
+ array( 'status' => 500 )
+ );
}
fwrite( $fp, $data );
@@ -594,7 +1019,11 @@
if ( isset( $sideloaded['error'] ) ) {
@unlink( $tmpfname );
- return new WP_Error( 'rest_upload_sideload_error', $sideloaded['error'], array( 'status' => 500 ) );
+ return new WP_Error(
+ 'rest_upload_sideload_error',
+ $sideloaded['error'],
+ array( 'status' => 500 )
+ );
}
return $sideloaded;
@@ -623,8 +1052,8 @@
*
* @since 4.7.0
*
- * @link http://tools.ietf.org/html/rfc2388
- * @link http://tools.ietf.org/html/rfc6266
+ * @link https://tools.ietf.org/html/rfc2388
+ * @link https://tools.ietf.org/html/rfc6266
*
* @param string[] $disposition_header List of Content-Disposition header values.
* @return string|null Filename if available, or null if not found.
@@ -710,7 +1139,11 @@
*/
protected function upload_from_file( $files, $headers ) {
if ( empty( $files ) ) {
- return new WP_Error( 'rest_upload_no_data', __( 'No data supplied.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_upload_no_data',
+ __( 'No data supplied.' ),
+ array( 'status' => 400 )
+ );
}
// Verify hash, if given.
@@ -720,7 +1153,11 @@
$actual = md5_file( $files['file']['tmp_name'] );
if ( $expected !== $actual ) {
- return new WP_Error( 'rest_upload_hash_mismatch', __( 'Content hash did not match expected.' ), array( 'status' => 412 ) );
+ return new WP_Error(
+ 'rest_upload_hash_mismatch',
+ __( 'Content hash did not match expected.' ),
+ array( 'status' => 412 )
+ );
}
}
@@ -739,13 +1176,17 @@
return $size_check;
}
- /** Include admin function to get access to wp_handle_upload(). */
+ // Include filesystem functions to get access to wp_handle_upload().
require_once ABSPATH . 'wp-admin/includes/file.php';
$file = wp_handle_upload( $files['file'], $overrides );
if ( isset( $file['error'] ) ) {
- return new WP_Error( 'rest_upload_unknown_error', $file['error'], array( 'status' => 500 ) );
+ return new WP_Error(
+ 'rest_upload_unknown_error',
+ $file['error'],
+ array( 'status' => 500 )
+ );
}
return $file;
@@ -798,23 +1239,87 @@
$space_left = get_upload_space_available();
$file_size = filesize( $file['tmp_name'] );
+
if ( $space_left < $file_size ) {
- /* translators: %s: required disk space in kilobytes */
- return new WP_Error( 'rest_upload_limited_space', sprintf( __( 'Not enough space to upload. %s KB needed.' ), number_format( ( $file_size - $space_left ) / KB_IN_BYTES ) ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_upload_limited_space',
+ /* translators: %s: Required disk space in kilobytes. */
+ sprintf( __( 'Not enough space to upload. %s KB needed.' ), number_format( ( $file_size - $space_left ) / KB_IN_BYTES ) ),
+ array( 'status' => 400 )
+ );
}
if ( $file_size > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) {
- /* translators: %s: maximum allowed file size in kilobytes */
- return new WP_Error( 'rest_upload_file_too_big', sprintf( __( 'This file is too big. Files must be less than %s KB in size.' ), get_site_option( 'fileupload_maxk', 1500 ) ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_upload_file_too_big',
+ /* translators: %s: Maximum allowed file size in kilobytes. */
+ sprintf( __( 'This file is too big. Files must be less than %s KB in size.' ), get_site_option( 'fileupload_maxk', 1500 ) ),
+ array( 'status' => 400 )
+ );
}
- // Include admin function to get access to upload_is_user_over_quota().
+ // Include multisite admin functions to get access to upload_is_user_over_quota().
require_once ABSPATH . 'wp-admin/includes/ms.php';
if ( upload_is_user_over_quota( false ) ) {
- return new WP_Error( 'rest_upload_user_quota_exceeded', __( 'You have used your space quota. Please delete files before uploading.' ), array( 'status' => 400 ) );
+ return new WP_Error(
+ 'rest_upload_user_quota_exceeded',
+ __( 'You have used your space quota. Please delete files before uploading.' ),
+ array( 'status' => 400 )
+ );
}
+
return true;
}
+ /**
+ * Gets the request args for the edit item route.
+ *
+ * @since 5.5.0
+ *
+ * @return array
+ */
+ protected function get_edit_media_item_args() {
+ return array(
+ 'rotation' => array(
+ 'description' => __( 'The amount to rotate the image clockwise in degrees.' ),
+ 'type' => 'integer',
+ 'minimum' => 0,
+ 'exclusiveMinimum' => true,
+ 'maximum' => 360,
+ 'exclusiveMaximum' => true,
+ ),
+ 'x' => array(
+ 'description' => __( 'As a percentage of the image, the x position to start the crop from.' ),
+ 'type' => 'number',
+ 'minimum' => 0,
+ 'maximum' => 100,
+ ),
+ 'y' => array(
+ 'description' => __( 'As a percentage of the image, the y position to start the crop from.' ),
+ 'type' => 'number',
+ 'minimum' => 0,
+ 'maximum' => 100,
+ ),
+ 'width' => array(
+ 'description' => __( 'As a percentage of the image, the width to crop the image to.' ),
+ 'type' => 'number',
+ 'minimum' => 0,
+ 'maximum' => 100,
+ ),
+ 'height' => array(
+ 'description' => __( 'As a percentage of the image, the height to crop the image to.' ),
+ 'type' => 'number',
+ 'minimum' => 0,
+ 'maximum' => 100,
+ ),
+ 'src' => array(
+ 'description' => __( 'URL to the edited image file.' ),
+ 'type' => 'string',
+ 'format' => 'uri',
+ 'required' => true,
+ ),
+ );
+ }
+
}