wp_ajax_crop_image

The timeline below displays how wordpress function wp_ajax_crop_image has changed across different WordPress versions. If a version is not listed, refer to the next available version below.

WordPress Version: 6.5

/**
 * Handles cropping an image via AJAX.
 *
 * @since 4.3.0
 */
function wp_ajax_crop_image()
{
    $attachment_id = absint($_POST['id']);
    check_ajax_referer('image_editor-' . $attachment_id, 'nonce');
    if (empty($attachment_id) || !current_user_can('edit_post', $attachment_id)) {
        wp_send_json_error();
    }
    $context = str_replace('_', '-', $_POST['context']);
    $data = array_map('absint', $_POST['cropDetails']);
    $cropped = wp_crop_image($attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height']);
    if (!$cropped || is_wp_error($cropped)) {
        wp_send_json_error(array('message' => __('Image could not be processed.')));
    }
    switch ($context) {
        case 'site-icon':
            require_once ABSPATH . 'wp-admin/includes/class-wp-site-icon.php';
            $wp_site_icon = new WP_Site_Icon();
            // Skip creating a new attachment if the attachment is a Site Icon.
            if (get_post_meta($attachment_id, '_wp_attachment_context', true) == $context) {
                // Delete the temporary cropped file, we don't need it.
                wp_delete_file($cropped);
                // Additional sizes in wp_prepare_attachment_for_js().
                add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
                break;
            }
            /** This filter is documented in wp-admin/includes/class-custom-image-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            // Copy attachment properties.
            $attachment = wp_copy_parent_attachment_properties($cropped, $attachment_id, $context);
            // Update the attachment.
            add_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            $attachment_id = $wp_site_icon->insert_attachment($attachment, $cropped);
            remove_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            // Additional sizes in wp_prepare_attachment_for_js().
            add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
            break;
        default:
            /**
             * Fires before a cropped image is saved.
             *
             * Allows to add filters to modify the way a cropped image is saved.
             *
             * @since 4.3.0
             *
             * @param string $context       The Customizer control requesting the cropped image.
             * @param int    $attachment_id The attachment ID of the original image.
             * @param string $cropped       Path to the cropped image file.
             */
            do_action('wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped);
            /** This filter is documented in wp-admin/includes/class-custom-image-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            // Copy attachment properties.
            $attachment = wp_copy_parent_attachment_properties($cropped, $attachment_id, $context);
            $attachment_id = wp_insert_attachment($attachment, $cropped);
            $metadata = wp_generate_attachment_metadata($attachment_id, $cropped);
            /**
             * Filters the cropped image attachment metadata.
             *
             * @since 4.3.0
             *
             * @see wp_generate_attachment_metadata()
             *
             * @param array $metadata Attachment metadata.
             */
            $metadata = apply_filters('wp_ajax_cropped_attachment_metadata', $metadata);
            wp_update_attachment_metadata($attachment_id, $metadata);
            /**
             * Filters the attachment ID for a cropped image.
             *
             * @since 4.3.0
             *
             * @param int    $attachment_id The attachment ID of the cropped image.
             * @param string $context       The Customizer control requesting the cropped image.
             */
            $attachment_id = apply_filters('wp_ajax_cropped_attachment_id', $attachment_id, $context);
    }
    wp_send_json_success(wp_prepare_attachment_for_js($attachment_id));
}

WordPress Version: 6.3

/**
 * Handles cropping an image via AJAX.
 *
 * @since 4.3.0
 */
function wp_ajax_crop_image()
{
    $attachment_id = absint($_POST['id']);
    check_ajax_referer('image_editor-' . $attachment_id, 'nonce');
    if (empty($attachment_id) || !current_user_can('edit_post', $attachment_id)) {
        wp_send_json_error();
    }
    $context = str_replace('_', '-', $_POST['context']);
    $data = array_map('absint', $_POST['cropDetails']);
    $cropped = wp_crop_image($attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height']);
    if (!$cropped || is_wp_error($cropped)) {
        wp_send_json_error(array('message' => __('Image could not be processed.')));
    }
    switch ($context) {
        case 'site-icon':
            require_once ABSPATH . 'wp-admin/includes/class-wp-site-icon.php';
            $wp_site_icon = new WP_Site_Icon();
            // Skip creating a new attachment if the attachment is a Site Icon.
            if (get_post_meta($attachment_id, '_wp_attachment_context', true) == $context) {
                // Delete the temporary cropped file, we don't need it.
                wp_delete_file($cropped);
                // Additional sizes in wp_prepare_attachment_for_js().
                add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
                break;
            }
            /** This filter is documented in wp-admin/includes/class-custom-image-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $attachment = $wp_site_icon->create_attachment_object($cropped, $attachment_id);
            unset($attachment['ID']);
            // Update the attachment.
            add_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            $attachment_id = $wp_site_icon->insert_attachment($attachment, $cropped);
            remove_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            // Additional sizes in wp_prepare_attachment_for_js().
            add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
            break;
        default:
            /**
             * Fires before a cropped image is saved.
             *
             * Allows to add filters to modify the way a cropped image is saved.
             *
             * @since 4.3.0
             *
             * @param string $context       The Customizer control requesting the cropped image.
             * @param int    $attachment_id The attachment ID of the original image.
             * @param string $cropped       Path to the cropped image file.
             */
            do_action('wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped);
            /** This filter is documented in wp-admin/includes/class-custom-image-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $parent_url = wp_get_attachment_url($attachment_id);
            $parent_basename = wp_basename($parent_url);
            $url = str_replace($parent_basename, wp_basename($cropped), $parent_url);
            $size = wp_getimagesize($cropped);
            $image_type = $size ? $size['mime'] : 'image/jpeg';
            // Get the original image's post to pre-populate the cropped image.
            $original_attachment = get_post($attachment_id);
            $sanitized_post_title = sanitize_file_name($original_attachment->post_title);
            $use_original_title = '' !== trim($original_attachment->post_title) && $parent_basename !== $sanitized_post_title && pathinfo($parent_basename, PATHINFO_FILENAME) !== $sanitized_post_title;
            $use_original_description = '' !== trim($original_attachment->post_content);
            $attachment = array('post_title' => $use_original_title ? $original_attachment->post_title : wp_basename($cropped), 'post_content' => $use_original_description ? $original_attachment->post_content : $url, 'post_mime_type' => $image_type, 'guid' => $url, 'context' => $context);
            // Copy the image caption attribute (post_excerpt field) from the original image.
            if ('' !== trim($original_attachment->post_excerpt)) {
                $attachment['post_excerpt'] = $original_attachment->post_excerpt;
            }
            // Copy the image alt text attribute from the original image.
            if ('' !== trim($original_attachment->_wp_attachment_image_alt)) {
                $attachment['meta_input'] = array('_wp_attachment_image_alt' => wp_slash($original_attachment->_wp_attachment_image_alt));
            }
            $attachment_id = wp_insert_attachment($attachment, $cropped);
            $metadata = wp_generate_attachment_metadata($attachment_id, $cropped);
            /**
             * Filters the cropped image attachment metadata.
             *
             * @since 4.3.0
             *
             * @see wp_generate_attachment_metadata()
             *
             * @param array $metadata Attachment metadata.
             */
            $metadata = apply_filters('wp_ajax_cropped_attachment_metadata', $metadata);
            wp_update_attachment_metadata($attachment_id, $metadata);
            /**
             * Filters the attachment ID for a cropped image.
             *
             * @since 4.3.0
             *
             * @param int    $attachment_id The attachment ID of the cropped image.
             * @param string $context       The Customizer control requesting the cropped image.
             */
            $attachment_id = apply_filters('wp_ajax_cropped_attachment_id', $attachment_id, $context);
    }
    wp_send_json_success(wp_prepare_attachment_for_js($attachment_id));
}

WordPress Version: 6.1

/**
 * Ajax handler for cropping an image.
 *
 * @since 4.3.0
 */
function wp_ajax_crop_image()
{
    $attachment_id = absint($_POST['id']);
    check_ajax_referer('image_editor-' . $attachment_id, 'nonce');
    if (empty($attachment_id) || !current_user_can('edit_post', $attachment_id)) {
        wp_send_json_error();
    }
    $context = str_replace('_', '-', $_POST['context']);
    $data = array_map('absint', $_POST['cropDetails']);
    $cropped = wp_crop_image($attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height']);
    if (!$cropped || is_wp_error($cropped)) {
        wp_send_json_error(array('message' => __('Image could not be processed.')));
    }
    switch ($context) {
        case 'site-icon':
            require_once ABSPATH . 'wp-admin/includes/class-wp-site-icon.php';
            $wp_site_icon = new WP_Site_Icon();
            // Skip creating a new attachment if the attachment is a Site Icon.
            if (get_post_meta($attachment_id, '_wp_attachment_context', true) == $context) {
                // Delete the temporary cropped file, we don't need it.
                wp_delete_file($cropped);
                // Additional sizes in wp_prepare_attachment_for_js().
                add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
                break;
            }
            /** This filter is documented in wp-admin/includes/class-custom-image-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $attachment = $wp_site_icon->create_attachment_object($cropped, $attachment_id);
            unset($attachment['ID']);
            // Update the attachment.
            add_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            $attachment_id = $wp_site_icon->insert_attachment($attachment, $cropped);
            remove_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            // Additional sizes in wp_prepare_attachment_for_js().
            add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
            break;
        default:
            /**
             * Fires before a cropped image is saved.
             *
             * Allows to add filters to modify the way a cropped image is saved.
             *
             * @since 4.3.0
             *
             * @param string $context       The Customizer control requesting the cropped image.
             * @param int    $attachment_id The attachment ID of the original image.
             * @param string $cropped       Path to the cropped image file.
             */
            do_action('wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped);
            /** This filter is documented in wp-admin/includes/class-custom-image-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $parent_url = wp_get_attachment_url($attachment_id);
            $parent_basename = wp_basename($parent_url);
            $url = str_replace($parent_basename, wp_basename($cropped), $parent_url);
            $size = wp_getimagesize($cropped);
            $image_type = $size ? $size['mime'] : 'image/jpeg';
            // Get the original image's post to pre-populate the cropped image.
            $original_attachment = get_post($attachment_id);
            $sanitized_post_title = sanitize_file_name($original_attachment->post_title);
            $use_original_title = '' !== trim($original_attachment->post_title) && $parent_basename !== $sanitized_post_title && pathinfo($parent_basename, PATHINFO_FILENAME) !== $sanitized_post_title;
            $use_original_description = '' !== trim($original_attachment->post_content);
            $attachment = array('post_title' => $use_original_title ? $original_attachment->post_title : wp_basename($cropped), 'post_content' => $use_original_description ? $original_attachment->post_content : $url, 'post_mime_type' => $image_type, 'guid' => $url, 'context' => $context);
            // Copy the image caption attribute (post_excerpt field) from the original image.
            if ('' !== trim($original_attachment->post_excerpt)) {
                $attachment['post_excerpt'] = $original_attachment->post_excerpt;
            }
            // Copy the image alt text attribute from the original image.
            if ('' !== trim($original_attachment->_wp_attachment_image_alt)) {
                $attachment['meta_input'] = array('_wp_attachment_image_alt' => wp_slash($original_attachment->_wp_attachment_image_alt));
            }
            $attachment_id = wp_insert_attachment($attachment, $cropped);
            $metadata = wp_generate_attachment_metadata($attachment_id, $cropped);
            /**
             * Filters the cropped image attachment metadata.
             *
             * @since 4.3.0
             *
             * @see wp_generate_attachment_metadata()
             *
             * @param array $metadata Attachment metadata.
             */
            $metadata = apply_filters('wp_ajax_cropped_attachment_metadata', $metadata);
            wp_update_attachment_metadata($attachment_id, $metadata);
            /**
             * Filters the attachment ID for a cropped image.
             *
             * @since 4.3.0
             *
             * @param int    $attachment_id The attachment ID of the cropped image.
             * @param string $context       The Customizer control requesting the cropped image.
             */
            $attachment_id = apply_filters('wp_ajax_cropped_attachment_id', $attachment_id, $context);
    }
    wp_send_json_success(wp_prepare_attachment_for_js($attachment_id));
}

WordPress Version: 5.7

/**
 * Ajax handler for cropping an image.
 *
 * @since 4.3.0
 */
function wp_ajax_crop_image()
{
    $attachment_id = absint($_POST['id']);
    check_ajax_referer('image_editor-' . $attachment_id, 'nonce');
    if (empty($attachment_id) || !current_user_can('edit_post', $attachment_id)) {
        wp_send_json_error();
    }
    $context = str_replace('_', '-', $_POST['context']);
    $data = array_map('absint', $_POST['cropDetails']);
    $cropped = wp_crop_image($attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height']);
    if (!$cropped || is_wp_error($cropped)) {
        wp_send_json_error(array('message' => __('Image could not be processed.')));
    }
    switch ($context) {
        case 'site-icon':
            require_once ABSPATH . 'wp-admin/includes/class-wp-site-icon.php';
            $wp_site_icon = new WP_Site_Icon();
            // Skip creating a new attachment if the attachment is a Site Icon.
            if (get_post_meta($attachment_id, '_wp_attachment_context', true) == $context) {
                // Delete the temporary cropped file, we don't need it.
                wp_delete_file($cropped);
                // Additional sizes in wp_prepare_attachment_for_js().
                add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
                break;
            }
            /** This filter is documented in wp-admin/includes/class-custom-image-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $object = $wp_site_icon->create_attachment_object($cropped, $attachment_id);
            unset($object['ID']);
            // Update the attachment.
            add_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            $attachment_id = $wp_site_icon->insert_attachment($object, $cropped);
            remove_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            // Additional sizes in wp_prepare_attachment_for_js().
            add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
            break;
        default:
            /**
             * Fires before a cropped image is saved.
             *
             * Allows to add filters to modify the way a cropped image is saved.
             *
             * @since 4.3.0
             *
             * @param string $context       The Customizer control requesting the cropped image.
             * @param int    $attachment_id The attachment ID of the original image.
             * @param string $cropped       Path to the cropped image file.
             */
            do_action('wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped);
            /** This filter is documented in wp-admin/includes/class-custom-image-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $parent_url = wp_get_attachment_url($attachment_id);
            $url = str_replace(wp_basename($parent_url), wp_basename($cropped), $parent_url);
            $size = wp_getimagesize($cropped);
            $image_type = $size ? $size['mime'] : 'image/jpeg';
            $object = array('post_title' => wp_basename($cropped), 'post_content' => $url, 'post_mime_type' => $image_type, 'guid' => $url, 'context' => $context);
            $attachment_id = wp_insert_attachment($object, $cropped);
            $metadata = wp_generate_attachment_metadata($attachment_id, $cropped);
            /**
             * Filters the cropped image attachment metadata.
             *
             * @since 4.3.0
             *
             * @see wp_generate_attachment_metadata()
             *
             * @param array $metadata Attachment metadata.
             */
            $metadata = apply_filters('wp_ajax_cropped_attachment_metadata', $metadata);
            wp_update_attachment_metadata($attachment_id, $metadata);
            /**
             * Filters the attachment ID for a cropped image.
             *
             * @since 4.3.0
             *
             * @param int    $attachment_id The attachment ID of the cropped image.
             * @param string $context       The Customizer control requesting the cropped image.
             */
            $attachment_id = apply_filters('wp_ajax_cropped_attachment_id', $attachment_id, $context);
    }
    wp_send_json_success(wp_prepare_attachment_for_js($attachment_id));
}

WordPress Version: 5.3

/**
 * Ajax handler for cropping an image.
 *
 * @since 4.3.0
 */
function wp_ajax_crop_image()
{
    $attachment_id = absint($_POST['id']);
    check_ajax_referer('image_editor-' . $attachment_id, 'nonce');
    if (empty($attachment_id) || !current_user_can('edit_post', $attachment_id)) {
        wp_send_json_error();
    }
    $context = str_replace('_', '-', $_POST['context']);
    $data = array_map('absint', $_POST['cropDetails']);
    $cropped = wp_crop_image($attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height']);
    if (!$cropped || is_wp_error($cropped)) {
        wp_send_json_error(array('message' => __('Image could not be processed.')));
    }
    switch ($context) {
        case 'site-icon':
            require_once ABSPATH . 'wp-admin/includes/class-wp-site-icon.php';
            $wp_site_icon = new WP_Site_Icon();
            // Skip creating a new attachment if the attachment is a Site Icon.
            if (get_post_meta($attachment_id, '_wp_attachment_context', true) == $context) {
                // Delete the temporary cropped file, we don't need it.
                wp_delete_file($cropped);
                // Additional sizes in wp_prepare_attachment_for_js().
                add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
                break;
            }
            /** This filter is documented in wp-admin/includes/class-custom-image-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $object = $wp_site_icon->create_attachment_object($cropped, $attachment_id);
            unset($object['ID']);
            // Update the attachment.
            add_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            $attachment_id = $wp_site_icon->insert_attachment($object, $cropped);
            remove_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            // Additional sizes in wp_prepare_attachment_for_js().
            add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
            break;
        default:
            /**
             * Fires before a cropped image is saved.
             *
             * Allows to add filters to modify the way a cropped image is saved.
             *
             * @since 4.3.0
             *
             * @param string $context       The Customizer control requesting the cropped image.
             * @param int    $attachment_id The attachment ID of the original image.
             * @param string $cropped       Path to the cropped image file.
             */
            do_action('wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped);
            /** This filter is documented in wp-admin/includes/class-custom-image-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $parent_url = wp_get_attachment_url($attachment_id);
            $url = str_replace(wp_basename($parent_url), wp_basename($cropped), $parent_url);
            $size = @getimagesize($cropped);
            $image_type = $size ? $size['mime'] : 'image/jpeg';
            $object = array('post_title' => wp_basename($cropped), 'post_content' => $url, 'post_mime_type' => $image_type, 'guid' => $url, 'context' => $context);
            $attachment_id = wp_insert_attachment($object, $cropped);
            $metadata = wp_generate_attachment_metadata($attachment_id, $cropped);
            /**
             * Filters the cropped image attachment metadata.
             *
             * @since 4.3.0
             *
             * @see wp_generate_attachment_metadata()
             *
             * @param array $metadata Attachment metadata.
             */
            $metadata = apply_filters('wp_ajax_cropped_attachment_metadata', $metadata);
            wp_update_attachment_metadata($attachment_id, $metadata);
            /**
             * Filters the attachment ID for a cropped image.
             *
             * @since 4.3.0
             *
             * @param int    $attachment_id The attachment ID of the cropped image.
             * @param string $context       The Customizer control requesting the cropped image.
             */
            $attachment_id = apply_filters('wp_ajax_cropped_attachment_id', $attachment_id, $context);
    }
    wp_send_json_success(wp_prepare_attachment_for_js($attachment_id));
}

WordPress Version: 5.2

/**
 * Ajax handler for cropping an image.
 *
 * @since 4.3.0
 */
function wp_ajax_crop_image()
{
    $attachment_id = absint($_POST['id']);
    check_ajax_referer('image_editor-' . $attachment_id, 'nonce');
    if (empty($attachment_id) || !current_user_can('edit_post', $attachment_id)) {
        wp_send_json_error();
    }
    $context = str_replace('_', '-', $_POST['context']);
    $data = array_map('absint', $_POST['cropDetails']);
    $cropped = wp_crop_image($attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height']);
    if (!$cropped || is_wp_error($cropped)) {
        wp_send_json_error(array('message' => __('Image could not be processed.')));
    }
    switch ($context) {
        case 'site-icon':
            require_once ABSPATH . 'wp-admin/includes/class-wp-site-icon.php';
            $wp_site_icon = new WP_Site_Icon();
            // Skip creating a new attachment if the attachment is a Site Icon.
            if (get_post_meta($attachment_id, '_wp_attachment_context', true) == $context) {
                // Delete the temporary cropped file, we don't need it.
                wp_delete_file($cropped);
                // Additional sizes in wp_prepare_attachment_for_js().
                add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
                break;
            }
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $object = $wp_site_icon->create_attachment_object($cropped, $attachment_id);
            unset($object['ID']);
            // Update the attachment.
            add_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            $attachment_id = $wp_site_icon->insert_attachment($object, $cropped);
            remove_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            // Additional sizes in wp_prepare_attachment_for_js().
            add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
            break;
        default:
            /**
             * Fires before a cropped image is saved.
             *
             * Allows to add filters to modify the way a cropped image is saved.
             *
             * @since 4.3.0
             *
             * @param string $context       The Customizer control requesting the cropped image.
             * @param int    $attachment_id The attachment ID of the original image.
             * @param string $cropped       Path to the cropped image file.
             */
            do_action('wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped);
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $parent_url = wp_get_attachment_url($attachment_id);
            $url = str_replace(wp_basename($parent_url), wp_basename($cropped), $parent_url);
            $size = @getimagesize($cropped);
            $image_type = $size ? $size['mime'] : 'image/jpeg';
            $object = array('post_title' => wp_basename($cropped), 'post_content' => $url, 'post_mime_type' => $image_type, 'guid' => $url, 'context' => $context);
            $attachment_id = wp_insert_attachment($object, $cropped);
            $metadata = wp_generate_attachment_metadata($attachment_id, $cropped);
            /**
             * Filters the cropped image attachment metadata.
             *
             * @since 4.3.0
             *
             * @see wp_generate_attachment_metadata()
             *
             * @param array $metadata Attachment metadata.
             */
            $metadata = apply_filters('wp_ajax_cropped_attachment_metadata', $metadata);
            wp_update_attachment_metadata($attachment_id, $metadata);
            /**
             * Filters the attachment ID for a cropped image.
             *
             * @since 4.3.0
             *
             * @param int    $attachment_id The attachment ID of the cropped image.
             * @param string $context       The Customizer control requesting the cropped image.
             */
            $attachment_id = apply_filters('wp_ajax_cropped_attachment_id', $attachment_id, $context);
    }
    wp_send_json_success(wp_prepare_attachment_for_js($attachment_id));
}

WordPress Version: 4.9

/**
 * Ajax handler for cropping an image.
 *
 * @since 4.3.0
 */
function wp_ajax_crop_image()
{
    $attachment_id = absint($_POST['id']);
    check_ajax_referer('image_editor-' . $attachment_id, 'nonce');
    if (empty($attachment_id) || !current_user_can('edit_post', $attachment_id)) {
        wp_send_json_error();
    }
    $context = str_replace('_', '-', $_POST['context']);
    $data = array_map('absint', $_POST['cropDetails']);
    $cropped = wp_crop_image($attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height']);
    if (!$cropped || is_wp_error($cropped)) {
        wp_send_json_error(array('message' => __('Image could not be processed.')));
    }
    switch ($context) {
        case 'site-icon':
            require_once ABSPATH . '/wp-admin/includes/class-wp-site-icon.php';
            $wp_site_icon = new WP_Site_Icon();
            // Skip creating a new attachment if the attachment is a Site Icon.
            if (get_post_meta($attachment_id, '_wp_attachment_context', true) == $context) {
                // Delete the temporary cropped file, we don't need it.
                wp_delete_file($cropped);
                // Additional sizes in wp_prepare_attachment_for_js().
                add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
                break;
            }
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $object = $wp_site_icon->create_attachment_object($cropped, $attachment_id);
            unset($object['ID']);
            // Update the attachment.
            add_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            $attachment_id = $wp_site_icon->insert_attachment($object, $cropped);
            remove_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            // Additional sizes in wp_prepare_attachment_for_js().
            add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
            break;
        default:
            /**
             * Fires before a cropped image is saved.
             *
             * Allows to add filters to modify the way a cropped image is saved.
             *
             * @since 4.3.0
             *
             * @param string $context       The Customizer control requesting the cropped image.
             * @param int    $attachment_id The attachment ID of the original image.
             * @param string $cropped       Path to the cropped image file.
             */
            do_action('wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped);
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $parent_url = wp_get_attachment_url($attachment_id);
            $url = str_replace(basename($parent_url), basename($cropped), $parent_url);
            $size = @getimagesize($cropped);
            $image_type = $size ? $size['mime'] : 'image/jpeg';
            $object = array('post_title' => basename($cropped), 'post_content' => $url, 'post_mime_type' => $image_type, 'guid' => $url, 'context' => $context);
            $attachment_id = wp_insert_attachment($object, $cropped);
            $metadata = wp_generate_attachment_metadata($attachment_id, $cropped);
            /**
             * Filters the cropped image attachment metadata.
             *
             * @since 4.3.0
             *
             * @see wp_generate_attachment_metadata()
             *
             * @param array $metadata Attachment metadata.
             */
            $metadata = apply_filters('wp_ajax_cropped_attachment_metadata', $metadata);
            wp_update_attachment_metadata($attachment_id, $metadata);
            /**
             * Filters the attachment ID for a cropped image.
             *
             * @since 4.3.0
             *
             * @param int    $attachment_id The attachment ID of the cropped image.
             * @param string $context       The Customizer control requesting the cropped image.
             */
            $attachment_id = apply_filters('wp_ajax_cropped_attachment_id', $attachment_id, $context);
    }
    wp_send_json_success(wp_prepare_attachment_for_js($attachment_id));
}

WordPress Version: 4.7

/**
 * Ajax handler for cropping an image.
 *
 * @since 4.3.0
 */
function wp_ajax_crop_image()
{
    $attachment_id = absint($_POST['id']);
    check_ajax_referer('image_editor-' . $attachment_id, 'nonce');
    if (!current_user_can('customize')) {
        wp_send_json_error();
    }
    $context = str_replace('_', '-', $_POST['context']);
    $data = array_map('absint', $_POST['cropDetails']);
    $cropped = wp_crop_image($attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height']);
    if (!$cropped || is_wp_error($cropped)) {
        wp_send_json_error(array('message' => __('Image could not be processed.')));
    }
    switch ($context) {
        case 'site-icon':
            require_once ABSPATH . '/wp-admin/includes/class-wp-site-icon.php';
            $wp_site_icon = new WP_Site_Icon();
            // Skip creating a new attachment if the attachment is a Site Icon.
            if (get_post_meta($attachment_id, '_wp_attachment_context', true) == $context) {
                // Delete the temporary cropped file, we don't need it.
                wp_delete_file($cropped);
                // Additional sizes in wp_prepare_attachment_for_js().
                add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
                break;
            }
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $object = $wp_site_icon->create_attachment_object($cropped, $attachment_id);
            unset($object['ID']);
            // Update the attachment.
            add_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            $attachment_id = $wp_site_icon->insert_attachment($object, $cropped);
            remove_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            // Additional sizes in wp_prepare_attachment_for_js().
            add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
            break;
        default:
            /**
             * Fires before a cropped image is saved.
             *
             * Allows to add filters to modify the way a cropped image is saved.
             *
             * @since 4.3.0
             *
             * @param string $context       The Customizer control requesting the cropped image.
             * @param int    $attachment_id The attachment ID of the original image.
             * @param string $cropped       Path to the cropped image file.
             */
            do_action('wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped);
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $parent_url = wp_get_attachment_url($attachment_id);
            $url = str_replace(basename($parent_url), basename($cropped), $parent_url);
            $size = @getimagesize($cropped);
            $image_type = $size ? $size['mime'] : 'image/jpeg';
            $object = array('post_title' => basename($cropped), 'post_content' => $url, 'post_mime_type' => $image_type, 'guid' => $url, 'context' => $context);
            $attachment_id = wp_insert_attachment($object, $cropped);
            $metadata = wp_generate_attachment_metadata($attachment_id, $cropped);
            /**
             * Filters the cropped image attachment metadata.
             *
             * @since 4.3.0
             *
             * @see wp_generate_attachment_metadata()
             *
             * @param array $metadata Attachment metadata.
             */
            $metadata = apply_filters('wp_ajax_cropped_attachment_metadata', $metadata);
            wp_update_attachment_metadata($attachment_id, $metadata);
            /**
             * Filters the attachment ID for a cropped image.
             *
             * @since 4.3.0
             *
             * @param int    $attachment_id The attachment ID of the cropped image.
             * @param string $context       The Customizer control requesting the cropped image.
             */
            $attachment_id = apply_filters('wp_ajax_cropped_attachment_id', $attachment_id, $context);
    }
    wp_send_json_success(wp_prepare_attachment_for_js($attachment_id));
}

WordPress Version: 4.6

/**
 * Ajax handler for cropping an image.
 *
 * @since 4.3.0
 *
 * @global WP_Site_Icon $wp_site_icon
 */
function wp_ajax_crop_image()
{
    $attachment_id = absint($_POST['id']);
    check_ajax_referer('image_editor-' . $attachment_id, 'nonce');
    if (!current_user_can('customize')) {
        wp_send_json_error();
    }
    $context = str_replace('_', '-', $_POST['context']);
    $data = array_map('absint', $_POST['cropDetails']);
    $cropped = wp_crop_image($attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height']);
    if (!$cropped || is_wp_error($cropped)) {
        wp_send_json_error(array('message' => __('Image could not be processed.')));
    }
    switch ($context) {
        case 'site-icon':
            require_once ABSPATH . '/wp-admin/includes/class-wp-site-icon.php';
            global $wp_site_icon;
            // Skip creating a new attachment if the attachment is a Site Icon.
            if (get_post_meta($attachment_id, '_wp_attachment_context', true) == $context) {
                // Delete the temporary cropped file, we don't need it.
                wp_delete_file($cropped);
                // Additional sizes in wp_prepare_attachment_for_js().
                add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
                break;
            }
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $object = $wp_site_icon->create_attachment_object($cropped, $attachment_id);
            unset($object['ID']);
            // Update the attachment.
            add_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            $attachment_id = $wp_site_icon->insert_attachment($object, $cropped);
            remove_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            // Additional sizes in wp_prepare_attachment_for_js().
            add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
            break;
        default:
            /**
             * Fires before a cropped image is saved.
             *
             * Allows to add filters to modify the way a cropped image is saved.
             *
             * @since 4.3.0
             *
             * @param string $context       The Customizer control requesting the cropped image.
             * @param int    $attachment_id The attachment ID of the original image.
             * @param string $cropped       Path to the cropped image file.
             */
            do_action('wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped);
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $parent_url = wp_get_attachment_url($attachment_id);
            $url = str_replace(basename($parent_url), basename($cropped), $parent_url);
            $size = @getimagesize($cropped);
            $image_type = $size ? $size['mime'] : 'image/jpeg';
            $object = array('post_title' => basename($cropped), 'post_content' => $url, 'post_mime_type' => $image_type, 'guid' => $url, 'context' => $context);
            $attachment_id = wp_insert_attachment($object, $cropped);
            $metadata = wp_generate_attachment_metadata($attachment_id, $cropped);
            /**
             * Filters the cropped image attachment metadata.
             *
             * @since 4.3.0
             *
             * @see wp_generate_attachment_metadata()
             *
             * @param array $metadata Attachment metadata.
             */
            $metadata = apply_filters('wp_ajax_cropped_attachment_metadata', $metadata);
            wp_update_attachment_metadata($attachment_id, $metadata);
            /**
             * Filters the attachment ID for a cropped image.
             *
             * @since 4.3.0
             *
             * @param int    $attachment_id The attachment ID of the cropped image.
             * @param string $context       The Customizer control requesting the cropped image.
             */
            $attachment_id = apply_filters('wp_ajax_cropped_attachment_id', $attachment_id, $context);
    }
    wp_send_json_success(wp_prepare_attachment_for_js($attachment_id));
}

WordPress Version: 4.4

/**
 * AJAX handler for cropping an image.
 *
 * @since 4.3.0
 *
 * @global WP_Site_Icon $wp_site_icon
 */
function wp_ajax_crop_image()
{
    $attachment_id = absint($_POST['id']);
    check_ajax_referer('image_editor-' . $attachment_id, 'nonce');
    if (!current_user_can('customize')) {
        wp_send_json_error();
    }
    $context = str_replace('_', '-', $_POST['context']);
    $data = array_map('absint', $_POST['cropDetails']);
    $cropped = wp_crop_image($attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height']);
    if (!$cropped || is_wp_error($cropped)) {
        wp_send_json_error(array('message' => __('Image could not be processed.')));
    }
    switch ($context) {
        case 'site-icon':
            require_once ABSPATH . '/wp-admin/includes/class-wp-site-icon.php';
            global $wp_site_icon;
            // Skip creating a new attachment if the attachment is a Site Icon.
            if (get_post_meta($attachment_id, '_wp_attachment_context', true) == $context) {
                // Delete the temporary cropped file, we don't need it.
                wp_delete_file($cropped);
                // Additional sizes in wp_prepare_attachment_for_js().
                add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
                break;
            }
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $object = $wp_site_icon->create_attachment_object($cropped, $attachment_id);
            unset($object['ID']);
            // Update the attachment.
            add_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            $attachment_id = $wp_site_icon->insert_attachment($object, $cropped);
            remove_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            // Additional sizes in wp_prepare_attachment_for_js().
            add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
            break;
        default:
            /**
             * Fires before a cropped image is saved.
             *
             * Allows to add filters to modify the way a cropped image is saved.
             *
             * @since 4.3.0
             *
             * @param string $context       The Customizer control requesting the cropped image.
             * @param int    $attachment_id The attachment ID of the original image.
             * @param string $cropped       Path to the cropped image file.
             */
            do_action('wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped);
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $parent_url = wp_get_attachment_url($attachment_id);
            $url = str_replace(basename($parent_url), basename($cropped), $parent_url);
            $size = @getimagesize($cropped);
            $image_type = $size ? $size['mime'] : 'image/jpeg';
            $object = array('post_title' => basename($cropped), 'post_content' => $url, 'post_mime_type' => $image_type, 'guid' => $url, 'context' => $context);
            $attachment_id = wp_insert_attachment($object, $cropped);
            $metadata = wp_generate_attachment_metadata($attachment_id, $cropped);
            /**
             * Filter the cropped image attachment metadata.
             *
             * @since 4.3.0
             *
             * @see wp_generate_attachment_metadata()
             *
             * @param array $metadata Attachment metadata.
             */
            $metadata = apply_filters('wp_ajax_cropped_attachment_metadata', $metadata);
            wp_update_attachment_metadata($attachment_id, $metadata);
            /**
             * Filter the attachment ID for a cropped image.
             *
             * @since 4.3.0
             *
             * @param int    $attachment_id The attachment ID of the cropped image.
             * @param string $context       The Customizer control requesting the cropped image.
             */
            $attachment_id = apply_filters('wp_ajax_cropped_attachment_id', $attachment_id, $context);
    }
    wp_send_json_success(wp_prepare_attachment_for_js($attachment_id));
}

WordPress Version: 4.3

/**
 * AJAX handler for cropping an image.
 *
 * @since 4.3.0
 *
 * @global WP_Site_Icon $wp_site_icon
 */
function wp_ajax_crop_image()
{
    $attachment_id = absint($_POST['id']);
    check_ajax_referer('image_editor-' . $attachment_id, 'nonce');
    if (!current_user_can('customize')) {
        wp_send_json_error();
    }
    $context = str_replace('_', '-', $_POST['context']);
    $data = array_map('absint', $_POST['cropDetails']);
    $cropped = wp_crop_image($attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height']);
    if (!$cropped || is_wp_error($cropped)) {
        wp_send_json_error(array('message' => __('Image could not be processed.')));
    }
    switch ($context) {
        case 'site-icon':
            require_once ABSPATH . '/wp-admin/includes/class-wp-site-icon.php';
            global $wp_site_icon;
            // Skip creating a new attachment if the attachment is a Site Icon.
            if (get_post_meta($attachment_id, '_wp_attachment_context', true) == $context) {
                // Delete the temporary cropped file, we don't need it.
                wp_delete_file($cropped);
                // Additional sizes in wp_prepare_attachment_for_js().
                add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
                break;
            }
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $object = $wp_site_icon->create_attachment_object($cropped, $attachment_id);
            unset($object['ID']);
            // Update the attachment.
            add_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            $attachment_id = $wp_site_icon->insert_attachment($object, $cropped);
            remove_filter('intermediate_image_sizes_advanced', array($wp_site_icon, 'additional_sizes'));
            // Additional sizes in wp_prepare_attachment_for_js().
            add_filter('image_size_names_choose', array($wp_site_icon, 'additional_sizes'));
            break;
        default:
            /**
             * Fires before a cropped image is saved.
             *
             * Allows to add filters to modify the way a cropped image is saved.
             *
             * @since 4.3.0
             *
             * @param string $context       The Customizer control requesting the cropped image.
             * @param int    $attachment_id The attachment ID of the original image.
             * @param string $cropped       Path to the cropped image file.
             */
            do_action('wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped);
            /** This filter is documented in wp-admin/custom-header.php */
            $cropped = apply_filters('wp_create_file_in_uploads', $cropped, $attachment_id);
            // For replication.
            $parent_url = get_post($attachment_id)->guid;
            $url = str_replace(basename($parent_url), basename($cropped), $parent_url);
            $size = @getimagesize($cropped);
            $image_type = $size ? $size['mime'] : 'image/jpeg';
            $object = array('post_title' => basename($cropped), 'post_content' => $url, 'post_mime_type' => $image_type, 'guid' => $url, 'context' => $context);
            $attachment_id = wp_insert_attachment($object, $cropped);
            $metadata = wp_generate_attachment_metadata($attachment_id, $cropped);
            /**
             * Filter the cropped image attachment metadata.
             *
             * @since 4.3.0
             *
             * @see wp_generate_attachment_metadata()
             *
             * @param array $metadata Attachment metadata.
             */
            $metadata = apply_filters('wp_ajax_cropped_attachment_metadata', $metadata);
            wp_update_attachment_metadata($attachment_id, $metadata);
            /**
             * Filter the attachment ID for a cropped image.
             *
             * @since 4.3.0
             *
             * @param int    $attachment_id The attachment ID of the cropped image.
             * @param string $context       The Customizer control requesting the cropped image.
             */
            $attachment_id = apply_filters('wp_ajax_cropped_attachment_id', $attachment_id, $context);
    }
    wp_send_json_success(wp_prepare_attachment_for_js($attachment_id));
}