: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Handles cropping an image via AJAX.
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 ) ) {
$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.' ) ) );
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' ) );
/** 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' ) );
* Fires before a cropped image is saved.
* Allows to add filters to modify the way a cropped image is saved.
* @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.
* @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.
* @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 ) );
* Handles generating a password via AJAX.
function wp_ajax_generate_password() {
wp_send_json_success( wp_generate_password( 24 ) );
* Handles generating a password in the no-privilege context via AJAX.
function wp_ajax_nopriv_generate_password() {
wp_send_json_success( wp_generate_password( 24 ) );
* Handles saving the user's WordPress.org username via AJAX.
function wp_ajax_save_wporg_username() {
if ( ! current_user_can( 'install_themes' ) && ! current_user_can( 'install_plugins' ) ) {
check_ajax_referer( 'save_wporg_username_' . get_current_user_id() );
$username = isset( $_REQUEST['username'] ) ? wp_unslash( $_REQUEST['username'] ) : false;
wp_send_json_success( update_user_meta( get_current_user_id(), 'wporg_favorites', $username ) );
* Handles installing a theme via AJAX.
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
function wp_ajax_install_theme() {
check_ajax_referer( 'updates' );
if ( empty( $_POST['slug'] ) ) {
'errorCode' => 'no_theme_specified',
'errorMessage' => __( 'No theme specified.' ),
$slug = sanitize_key( wp_unslash( $_POST['slug'] ) );
if ( ! current_user_can( 'install_themes' ) ) {
$status['errorMessage'] = __( 'Sorry, you are not allowed to install themes on this site.' );
wp_send_json_error( $status );
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
require_once ABSPATH . 'wp-admin/includes/theme.php';
'fields' => array( 'sections' => false ),
if ( is_wp_error( $api ) ) {
$status['errorMessage'] = $api->get_error_message();
wp_send_json_error( $status );
$skin = new WP_Ajax_Upgrader_Skin();
$upgrader = new Theme_Upgrader( $skin );
$result = $upgrader->install( $api->download_link );
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
$status['debug'] = $skin->get_upgrade_messages();
if ( is_wp_error( $result ) ) {
$status['errorCode'] = $result->get_error_code();
$status['errorMessage'] = $result->get_error_message();
wp_send_json_error( $status );
} elseif ( is_wp_error( $skin->result ) ) {
$status['errorCode'] = $skin->result->get_error_code();
$status['errorMessage'] = $skin->result->get_error_message();
wp_send_json_error( $status );
} elseif ( $skin->get_errors()->has_errors() ) {
$status['errorMessage'] = $skin->get_error_messages();
wp_send_json_error( $status );
} elseif ( is_null( $result ) ) {
$status['errorCode'] = 'unable_to_connect_to_filesystem';
$status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
// Pass through the error from WP_Filesystem if one was raised.
if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) {
$status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
wp_send_json_error( $status );
$status['themeName'] = wp_get_theme( $slug )->get( 'Name' );
if ( current_user_can( 'switch_themes' ) ) {
$status['activateUrl'] = add_query_arg(
'_wpnonce' => wp_create_nonce( 'enable-theme_' . $slug ),
network_admin_url( 'themes.php' )
$status['activateUrl'] = add_query_arg(
'_wpnonce' => wp_create_nonce( 'switch-theme_' . $slug ),
admin_url( 'themes.php' )
$theme = wp_get_theme( $slug );
$status['blockTheme'] = $theme->is_block_theme();
if ( ! is_multisite() && current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
$status['customizeUrl'] = add_query_arg(
'return' => urlencode( network_admin_url( 'theme-install.php', 'relative' ) ),
wp_customize_url( $slug )
* See WP_Theme_Install_List_Table::_get_theme_status() if we wanted to check
* on post-installation status.
wp_send_json_success( $status );
* Handles updating a theme via AJAX.
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
function wp_ajax_update_theme() {
check_ajax_referer( 'updates' );
if ( empty( $_POST['slug'] ) ) {
'errorCode' => 'no_theme_specified',
'errorMessage' => __( 'No theme specified.' ),
$stylesheet = preg_replace( '/[^A-z0-9_\-]/', '', wp_unslash( $_POST['slug'] ) );
if ( ! current_user_can( 'update_themes' ) ) {
$status['errorMessage'] = __( 'Sorry, you are not allowed to update themes for this site.' );
wp_send_json_error( $status );
$theme = wp_get_theme( $stylesheet );
if ( $theme->exists() ) {
$status['oldVersion'] = $theme->get( 'Version' );
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
$current = get_site_transient( 'update_themes' );
if ( empty( $current ) ) {
$skin = new WP_Ajax_Upgrader_Skin();
$upgrader = new Theme_Upgrader( $skin );
$result = $upgrader->bulk_upgrade( array( $stylesheet ) );
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
$status['debug'] = $skin->get_upgrade_messages();
if ( is_wp_error( $skin->result ) ) {
$status['errorCode'] = $skin->result->get_error_code();
$status['errorMessage'] = $skin->result->get_error_message();
wp_send_json_error( $status );
} elseif ( $skin->get_errors()->has_errors() ) {
$status['errorMessage'] = $skin->get_error_messages();
wp_send_json_error( $status );
} elseif ( is_array( $result ) && ! empty( $result[ $stylesheet ] ) ) {
// Theme is already at the latest version.
if ( true === $result[ $stylesheet ] ) {
$status['errorMessage'] = $upgrader->strings['up_to_date'];
wp_send_json_error( $status );
$theme = wp_get_theme( $stylesheet );
if ( $theme->exists() ) {
$status['newVersion'] = $theme->get( 'Version' );
wp_send_json_success( $status );
} elseif ( false === $result ) {
$status['errorCode'] = 'unable_to_connect_to_filesystem';
$status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
// Pass through the error from WP_Filesystem if one was raised.
if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) {
$status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
wp_send_json_error( $status );
// An unhandled error occurred.
$status['errorMessage'] = __( 'Theme update failed.' );
wp_send_json_error( $status );
* Handles deleting a theme via AJAX.
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
function wp_ajax_delete_theme() {
check_ajax_referer( 'updates' );
if ( empty( $_POST['slug'] ) ) {
'errorCode' => 'no_theme_specified',
'errorMessage' => __( 'No theme specified.' ),
$stylesheet = preg_replace( '/[^A-z0-9_\-]/', '', wp_unslash( $_POST['slug'] ) );
if ( ! current_user_can( 'delete_themes' ) ) {
$status['errorMessage'] = __( 'Sorry, you are not allowed to delete themes on this site.' );
wp_send_json_error( $status );
if ( ! wp_get_theme( $stylesheet )->exists() ) {
$status['errorMessage'] = __( 'The requested theme does not exist.' );
wp_send_json_error( $status );
// Check filesystem credentials. `delete_theme()` will bail otherwise.
$url = wp_nonce_url( 'themes.php?action=delete&stylesheet=' . urlencode( $stylesheet ), 'delete-theme_' . $stylesheet );
$credentials = request_filesystem_credentials( $url );
if ( false === $credentials || ! WP_Filesystem( $credentials ) ) {
$status['errorCode'] = 'unable_to_connect_to_filesystem';
$status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
// Pass through the error from WP_Filesystem if one was raised.
if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) {
$status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
wp_send_json_error( $status );
require_once ABSPATH . 'wp-admin/includes/theme.php';
$result = delete_theme( $stylesheet );
if ( is_wp_error( $result ) ) {
$status['errorMessage'] = $result->get_error_message();
wp_send_json_error( $status );
} elseif ( false === $result ) {
$status['errorMessage'] = __( 'Theme could not be deleted.' );
wp_send_json_error( $status );
wp_send_json_success( $status );
* Handles installing a plugin via AJAX.
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
function wp_ajax_install_plugin() {
check_ajax_referer( 'updates' );
if ( empty( $_POST['slug'] ) ) {
'errorCode' => 'no_plugin_specified',
'errorMessage' => __( 'No plugin specified.' ),
'slug' => sanitize_key( wp_unslash( $_POST['slug'] ) ),
if ( ! current_user_can( 'install_plugins' ) ) {
$status['errorMessage'] = __( 'Sorry, you are not allowed to install plugins on this site.' );
wp_send_json_error( $status );
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
'slug' => sanitize_key( wp_unslash( $_POST['slug'] ) ),
if ( is_wp_error( $api ) ) {
$status['errorMessage'] = $api->get_error_message();
wp_send_json_error( $status );
$status['pluginName'] = $api->name;
$skin = new WP_Ajax_Upgrader_Skin();
$upgrader = new Plugin_Upgrader( $skin );
$result = $upgrader->install( $api->download_link );
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
$status['debug'] = $skin->get_upgrade_messages();
if ( is_wp_error( $result ) ) {
$status['errorCode'] = $result->get_error_code();