Create New Item
×
Item Type
File
Folder
Item Name
×
Search file in folder and subfolders...
File Manager
/
wp-includes
/
rest-api
/
endpoints
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { 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())); } protected function prepare_items_query($prepared_args = array(), $request = null) { $query_args = parent::prepare_items_query($prepared_args, $request); if (empty($query_args['post_status'])) { $query_args['post_status'] = 'inherit'; } $media_types = $this->get_media_types(); if (!empty($request['media_type']) && isset($media_types[$request['media_type']])) { $query_args['post_mime_type'] = $media_types[$request['media_type']]; } if (!empty($request['mime_type'])) { $parts = explode('/', $request['mime_type']); if (isset($media_types[$parts[0]]) && in_array($request['mime_type'], $media_types[$parts[0]], true)) { $query_args['post_mime_type'] = $request['mime_type']; } } if (isset($query_args['s'])) { add_filter('posts_clauses', '_filter_query_attachment_filenames'); } return $query_args; } public function create_item_permissions_check($request) { $ret = parent::create_item_permissions_check($request); if (!$ret || is_wp_error($ret)) { return $ret; } 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)); } 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; } 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)); } $insert = $this->insert_attachment($request); if (is_wp_error($insert)) { return $insert; } $schema = $this->get_item_schema(); $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'); do_action('rest_after_insert_attachment', $attachment, $request, true); wp_after_insert_post($attachment, false, null); if (defined('REST_REQUEST') && REST_REQUEST) { header('X-WP-Upload-Attachment-ID: ' . $attachment_id); } require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; 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; } protected function insert_attachment($request) { $files = $request->get_file_params(); $headers = $request->get_headers(); if (!empty($files)) { $file = $this->upload_from_file($files, $headers); } else { $file = $this->upload_from_data($request->get_body(), $headers); } if (is_wp_error($file)) { return $file; } $name = wp_basename($file['file']); $name_parts = pathinfo($name); $name = trim(substr($name, 0, -(1 + strlen($name_parts['extension'])))); $url = $file['url']; $type = $file['type']; $file = $file['file']; require_once ABSPATH . 'wp-admin/includes/image.php'; $image_meta = wp_read_image_metadata($file); if (!empty($image_meta)) { if (empty($request['title']) && trim($image_meta['title']) && !is_numeric(sanitize_title($image_meta['title']))) { $request['title'] = $image_meta['title']; } if (empty($request['caption']) && trim($image_meta['caption'])) { $request['caption'] = $image_meta['caption']; } } $attachment = $this->prepare_item_for_database($request); $attachment->post_mime_type = $type; $attachment->guid = $url; if (empty($attachment->post_title)) { $attachment->post_title = preg_replace('/\\.[^.]+$/', '', wp_basename($file)); } $id = wp_insert_attachment(wp_slash((array) $attachment), $file, 0, true, false); if (is_wp_error($id)) { if ('db_update_error' === $id->get_error_code()) { $id->add_data(array('status' => 500)); } else { $id->add_data(array('status' => 400)); } return $id; } $attachment = get_post($id); do_action('rest_insert_attachment', $attachment, $request, true); return array('attachment_id' => $id, 'file' => $file); } 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)); } $attachment_before = get_post($request['id']); $response = parent::update_item($request); if (is_wp_error($response)) { return $response; } $response = rest_ensure_response($response); $data = $response->get_data(); if (isset($request['alt_text'])) { update_post_meta($data['id'], '_wp_attachment_image_alt', $request['alt_text']); } $attachment = get_post($request['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'); do_action('rest_after_insert_attachment', $attachment, $request, false); wp_after_insert_post($attachment, true, $attachment_before); $response = $this->prepare_item_for_response($attachment, $request); $response = rest_ensure_response($response); return $response; } 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); } public function post_process_item_permissions_check($request) { return $this->update_item_permissions_check($request); } 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); } public function edit_media_item($request) { require_once ABSPATH . 'wp-admin/includes/image.php'; $attachment_id = $request['id']; $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)); } if (isset($request['modifiers'])) { $modifiers = $request['modifiers']; } else { $modifiers = array(); if (!empty($request['rotation'])) { $modifiers[] = array('type' => 'rotate', 'args' => array('angle' => $request['rotation'])); } if (isset($request['x'], $request['y'], $request['width'], $request['height'])) { $modifiers[] = array('type' => 'crop', 'args' => array('left' => $request['x'], 'top' => $request['y'], 'width' => $request['width'], 'height' => $request['height'])); } if (0 === count($modifiers)) { return new WP_Error('rest_image_not_edited', __('The image was not edited. Edit the image before applying the changes.'), array('status' => 400)); } } $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)); } foreach ($modifiers as $modifier) { $args = $modifier['args']; switch ($modifier['type']) { case 'rotate': $rotate = 0 - $args['angle']; 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)); } } break; case 'crop': $size = $image_editor->get_size(); $crop_x = round($size['width'] * $args['left'] / 100.0); $crop_y = round($size['height'] * $args['top'] / 100.0); $width = round($size['width'] * $args['width'] / 100.0); $height = round($size['height'] * $args['height'] / 100.0); if ($size['width'] !== $width && $size['height'] !== $height) { $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)); } } break; } } $image_ext = pathinfo($image_file, PATHINFO_EXTENSION); $image_name = wp_basename($image_file, ".{$image_ext}"); if (preg_match('/-edited(-\\d+)?$/', $image_name)) { $image_name = preg_replace('/-edited(-\\d+)?$/', '-edited', $image_name); } else { $image_name .= '-edited'; } $filename = "{$image_name}.{$image_ext}"; $uploads = wp_upload_dir(); $filename = wp_unique_filename($uploads['path'], $filename); $saved = $image_editor->save($uploads['path'] . "/{$filename}"); if (is_wp_error($saved)) { return $saved; } $new_attachment_post = array('post_mime_type' => $saved['mime-type'], 'guid' => $uploads['url'] . "/{$filename}", 'post_title' => $image_name, 'post_content' => ''); $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; } $image_alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true); if (!empty($image_alt)) { update_post_meta($new_attachment_id, '_wp_attachment_image_alt', wp_slash($image_alt)); } if (defined('REST_REQUEST') && REST_REQUEST) { header('X-WP-Upload-Attachment-ID: ' . $new_attachment_id); } $new_image_meta = wp_generate_attachment_metadata($new_attachment_id, $saved['path']); if (isset($image_meta['image_meta']) && isset($new_image_meta['image_meta']) && is_array($new_image_meta['image_meta'])) { 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; } } } if (!empty($new_image_meta['image_meta']['orientation'])) { $new_image_meta['image_meta']['orientation'] = 1; } $new_image_meta['parent_image'] = array('attachment_id' => $attachment_id, 'file' => _wp_relative_upload_path($image_file)); $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; } protected function prepare_item_for_database($request) { $prepared_attachment = parent::prepare_item_for_database($request); if (isset($request['caption'])) { if (is_string($request['caption'])) { $prepared_attachment->post_excerpt = $request['caption']; } elseif (isset($request['caption']['raw'])) { $prepared_attachment->post_excerpt = $request['caption']['raw']; } } if (isset($request['description'])) { if (is_string($request['description'])) { $prepared_attachment->post_content = $request['description']; } elseif (isset($request['description']['raw'])) { $prepared_attachment->post_content = $request['description']['raw']; } } if (isset($request['post'])) { $prepared_attachment->post_parent = (int) $request['post']; } return $prepared_attachment; } public function prepare_item_for_response($post, $request) { $response = parent::prepare_item_for_response($post, $request); $fields = $this->get_fields_for_response($request); $data = $response->get_data(); if (in_array('description', $fields, true)) { $data['description'] = array('raw' => $post->post_content, 'rendered' => apply_filters('the_content', $post->post_content)); } if (in_array('caption', $fields, true)) { $caption = apply_filters('get_the_excerpt', $post->post_excerpt, $post); $caption = apply_filters('the_excerpt', $caption); $data['caption'] = array('raw' => $post->post_excerpt, 'rendered' => $caption); } if (in_array('alt_text', $fields, true)) { $data['alt_text'] = get_post_meta($post->ID, '_wp_attachment_image_alt', true); } if (in_array('media_type', $fields, true)) { $data['media_type'] = wp_attachment_is_image($post->ID) ? 'image' : 'file'; } if (in_array('mime_type', $fields, true)) { $data['mime_type'] = $post->post_mime_type; } if (in_array('media_details', $fields, true)) { $data['media_details'] = wp_get_attachment_metadata($post->ID); if (empty($data['media_details'])) { $data['media_details'] = new stdClass(); } elseif (!empty($data['media_details']['sizes'])) { foreach ($data['media_details']['sizes'] as $size => &$size_data) { if (isset($size_data['mime-type'])) { $size_data['mime_type'] = $size_data['mime-type']; unset($size_data['mime-type']); } $image_src = wp_get_attachment_image_src($post->ID, $size); if (!$image_src) { continue; } $size_data['source_url'] = $image_src[0]; } $full_src = wp_get_attachment_image_src($post->ID, 'full'); if (!empty($full_src)) { $data['media_details']['sizes']['full'] = array('file' => wp_basename($full_src[0]), 'width' => $full_src[1], 'height' => $full_src[2], 'mime_type' => $post->post_mime_type, 'source_url' => $full_src[0]); } } else { $data['media_details']['sizes'] = new stdClass(); } } if (in_array('post', $fields, true)) { $data['post'] = !empty($post->post_parent) ? (int) $post->post_parent : null; } if (in_array('source_url', $fields, true)) { $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); $links = $response->get_links(); $response = rest_ensure_response($data); foreach ($links as $rel => $rel_links) { foreach ($rel_links as $link) { $response->add_link($rel, $link['href'], $link['attributes']); } } return apply_filters('rest_prepare_attachment', $response, $post, $request); } public function get_item_schema() { if ($this->schema) { return $this->add_additional_fields_schema($this->schema); } $schema = parent::get_item_schema(); $schema['properties']['alt_text'] = array('description' => __('Alternative text to display when attachment is not displayed.'), 'type' => 'string', 'context' => array('view', 'edit', 'embed'), 'arg_options' => array('sanitize_callback' => 'sanitize_text_field')); $schema['properties']['caption'] = array('description' => __('The attachment caption.'), 'type' => 'object', 'context' => array('view', 'edit', 'embed'), 'arg_options' => array('sanitize_callback' => null, 'validate_callback' => null), 'properties' => array('raw' => array('description' => __('Caption for the attachment, as it exists in the database.'), 'type' => 'string', 'context' => array('edit')), 'rendered' => array('description' => __('HTML caption for the attachment, transformed for display.'), 'type' => 'string', 'context' => array('view', 'edit', 'embed'), 'readonly' => true))); $schema['properties']['description'] = array('description' => __('The attachment description.'), 'type' => 'object', 'context' => array('view', 'edit'), 'arg_options' => array('sanitize_callback' => null, 'validate_callback' => null), 'properties' => array('raw' => array('description' => __('Description for the object, as it exists in the database.'), 'type' => 'string', 'context' => array('edit')), 'rendered' => array('description' => __('HTML description for the object, transformed for display.'), 'type' => 'string', 'context' => array('view', 'edit'), 'readonly' => true))); $schema['properties']['media_type'] = array('description' => __('Attachment type.'), 'type' => 'string', 'enum' => array('image', 'file'), 'context' => array('view', 'edit', 'embed'), 'readonly' => true); $schema['properties']['mime_type'] = array('description' => __('The attachment MIME type.'), 'type' => 'string', 'context' => array('view', 'edit', 'embed'), 'readonly' => true); $schema['properties']['media_details'] = array('description' => __('Details about the media file, specific to its type.'), 'type' => 'object', 'context' => array('view', 'edit', 'embed'), 'readonly' => true); $schema['properties']['post'] = array('description' => __('The ID for the associated post of the attachment.'), 'type' => 'integer', 'context' => array('view', 'edit')); $schema['properties']['source_url'] = array('description' => __('URL to the original attachment file.'), 'type' => 'string', 'format' => 'uri', 'context' => array('view', 'edit', 'embed'), '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']); $this->schema = $schema; return $this->add_additional_fields_schema($this->schema); } protected function upload_from_data($data, $headers) { if (empty($data)) { 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)); } if (empty($headers['content_disposition'])) { 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)); } if (!empty($headers['content_md5'])) { $content_md5 = array_shift($headers['content_md5']); $expected = trim($content_md5); $actual = md5($data); if ($expected !== $actual) { return new WP_Error('rest_upload_hash_mismatch', __('Content hash did not match expected.'), array('status' => 412)); } } $type = array_shift($headers['content_type']); require_once ABSPATH . 'wp-admin/includes/file.php'; $tmpfname = wp_tempnam($filename); $fp = fopen($tmpfname, 'w+'); if (!$fp) { return new WP_Error('rest_upload_file_error', __('Could not open file handle.'), array('status' => 500)); } fwrite($fp, $data); fclose($fp); $file_data = array('error' => null, 'tmp_name' => $tmpfname, 'name' => $filename, 'type' => $type); $size_check = self::check_upload_size($file_data); if (is_wp_error($size_check)) { return $size_check; } $overrides = array('test_form' => false); $sideloaded = wp_handle_sideload($file_data, $overrides); if (isset($sideloaded['error'])) { @unlink($tmpfname); return new WP_Error('rest_upload_sideload_error', $sideloaded['error'], array('status' => 500)); } return $sideloaded; } public static function get_filename_from_disposition($disposition_header) { $filename = null; foreach ($disposition_header as $value) { $value = trim($value); if (strpos($value, ';') === false) { continue; } list($type, $attr_parts) = explode(';', $value, 2); $attr_parts = explode(';', $attr_parts); $attributes = array(); foreach ($attr_parts as $part) { if (strpos($part, '=') === false) { continue; } list($key, $value) = explode('=', $part, 2); $attributes[trim($key)] = trim($value); } if (empty($attributes['filename'])) { continue; } $filename = trim($attributes['filename']); if (substr($filename, 0, 1) === '"' && substr($filename, -1, 1) === '"') { $filename = substr($filename, 1, -1); } } return $filename; } public function get_collection_params() { $params = parent::get_collection_params(); $params['status']['default'] = 'inherit'; $params['status']['items']['enum'] = array('inherit', 'private', 'trash'); $media_types = $this->get_media_types(); $params['media_type'] = array('default' => null, 'description' => __('Limit result set to attachments of a particular media type.'), 'type' => 'string', 'enum' => array_keys($media_types)); $params['mime_type'] = array('default' => null, 'description' => __('Limit result set to attachments of a particular MIME type.'), 'type' => 'string'); return $params; } protected function upload_from_file($files, $headers) { if (empty($files)) { return new WP_Error('rest_upload_no_data', __('No data supplied.'), array('status' => 400)); } if (!empty($headers['content_md5'])) { $content_md5 = array_shift($headers['content_md5']); $expected = trim($content_md5); $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)); } } $overrides = array('test_form' => false); if (defined('DIR_TESTDATA') && DIR_TESTDATA) { $overrides['action'] = 'wp_handle_mock_upload'; } $size_check = self::check_upload_size($files['file']); if (is_wp_error($size_check)) { return $size_check; } 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 $file; } protected function get_media_types() { $media_types = array(); foreach (get_allowed_mime_types() as $mime_type) { $parts = explode('/', $mime_type); if (!isset($media_types[$parts[0]])) { $media_types[$parts[0]] = array(); } $media_types[$parts[0]][] = $mime_type; } return $media_types; } protected function check_upload_size($file) { if (!is_multisite()) { return true; } if (get_site_option('upload_space_check_disabled')) { return true; } $space_left = get_upload_space_available(); $file_size = filesize($file['tmp_name']); if ($space_left < $file_size) { 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)); } if ($file_size > KB_IN_BYTES * get_site_option('fileupload_maxk', 1500)) { 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)); } 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 true; } protected function get_edit_media_item_args() { return array('src' => array('description' => __('URL to the edited image file.'), 'type' => 'string', 'format' => 'uri', 'required' => true), 'modifiers' => array('description' => __('Array of image edits.'), 'type' => 'array', 'minItems' => 1, 'items' => array('description' => __('Image edit.'), 'type' => 'object', 'required' => array('type', 'args'), 'oneOf' => array(array('title' => __('Rotation'), 'properties' => array('type' => array('description' => __('Rotation type.'), 'type' => 'string', 'enum' => array('rotate')), 'args' => array('description' => __('Rotation arguments.'), 'type' => 'object', 'required' => array('angle'), 'properties' => array('angle' => array('description' => __('Angle to rotate clockwise in degrees.'), 'type' => 'number'))))), array('title' => __('Crop'), 'properties' => array('type' => array('description' => __('Crop type.'), 'type' => 'string', 'enum' => array('crop')), 'args' => array('description' => __('Crop arguments.'), 'type' => 'object', 'required' => array('left', 'top', 'width', 'height'), 'properties' => array('left' => array('description' => __('Horizontal position from the left to begin the crop as a percentage of the image width.'), 'type' => 'number'), 'top' => array('description' => __('Vertical position from the top to begin the crop as a percentage of the image height.'), 'type' => 'number'), 'width' => array('description' => __('Width of the crop as a percentage of the image width.'), 'type' => 'number'), 'height' => array('description' => __('Height of the crop as a percentage of the image height.'), 'type' => 'number')))))))), 'rotation' => array('description' => __('The amount to rotate the image clockwise in degrees. DEPRECATED: Use `modifiers` instead.'), '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. DEPRECATED: Use `modifiers` instead.'), 'type' => 'number', 'minimum' => 0, 'maximum' => 100), 'y' => array('description' => __('As a percentage of the image, the y position to start the crop from. DEPRECATED: Use `modifiers` instead.'), 'type' => 'number', 'minimum' => 0, 'maximum' => 100), 'width' => array('description' => __('As a percentage of the image, the width to crop the image to. DEPRECATED: Use `modifiers` instead.'), 'type' => 'number', 'minimum' => 0, 'maximum' => 100), 'height' => array('description' => __('As a percentage of the image, the height to crop the image to. DEPRECATED: Use `modifiers` instead.'), 'type' => 'number', 'minimum' => 0, 'maximum' => 100)); } }