wp/wp-admin/includes/ajax-actions.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
--- a/wp/wp-admin/includes/ajax-actions.php	Thu Sep 29 08:06:27 2022 +0200
+++ b/wp/wp-admin/includes/ajax-actions.php	Fri Sep 05 18:40:08 2025 +0200
@@ -12,7 +12,7 @@
 //
 
 /**
- * Ajax handler for the Heartbeat API in the no-privilege context.
+ * Handles the Heartbeat API in the no-privilege context via AJAX .
  *
  * Runs when the user is not logged in.
  *
@@ -76,7 +76,7 @@
 //
 
 /**
- * Ajax handler for fetching a list table.
+ * Handles fetching a list table via AJAX.
  *
  * @since 3.1.0
  */
@@ -99,7 +99,7 @@
 }
 
 /**
- * Ajax handler for tag search.
+ * Handles tag search via AJAX.
  *
  * @since 3.1.0
  */
@@ -108,66 +108,77 @@
 		wp_die( 0 );
 	}
 
-	$taxonomy = sanitize_key( $_GET['tax'] );
-	$tax      = get_taxonomy( $taxonomy );
-
-	if ( ! $tax ) {
+	$taxonomy        = sanitize_key( $_GET['tax'] );
+	$taxonomy_object = get_taxonomy( $taxonomy );
+
+	if ( ! $taxonomy_object ) {
 		wp_die( 0 );
 	}
 
-	if ( ! current_user_can( $tax->cap->assign_terms ) ) {
+	if ( ! current_user_can( $taxonomy_object->cap->assign_terms ) ) {
 		wp_die( -1 );
 	}
 
-	$s = wp_unslash( $_GET['q'] );
+	$search = wp_unslash( $_GET['q'] );
 
 	$comma = _x( ',', 'tag delimiter' );
 	if ( ',' !== $comma ) {
-		$s = str_replace( $comma, ',', $s );
-	}
-
-	if ( false !== strpos( $s, ',' ) ) {
-		$s = explode( ',', $s );
-		$s = $s[ count( $s ) - 1 ];
-	}
-
-	$s = trim( $s );
+		$search = str_replace( $comma, ',', $search );
+	}
+
+	if ( str_contains( $search, ',' ) ) {
+		$search = explode( ',', $search );
+		$search = $search[ count( $search ) - 1 ];
+	}
+
+	$search = trim( $search );
 
 	/**
 	 * Filters the minimum number of characters required to fire a tag search via Ajax.
 	 *
 	 * @since 4.0.0
 	 *
-	 * @param int         $characters The minimum number of characters required. Default 2.
-	 * @param WP_Taxonomy $tax        The taxonomy object.
-	 * @param string      $s          The search term.
+	 * @param int         $characters      The minimum number of characters required. Default 2.
+	 * @param WP_Taxonomy $taxonomy_object The taxonomy object.
+	 * @param string      $search          The search term.
 	 */
-	$term_search_min_chars = (int) apply_filters( 'term_search_min_chars', 2, $tax, $s );
+	$term_search_min_chars = (int) apply_filters( 'term_search_min_chars', 2, $taxonomy_object, $search );
 
 	/*
 	 * Require $term_search_min_chars chars for matching (default: 2)
 	 * ensure it's a non-negative, non-zero integer.
 	 */
-	if ( ( 0 == $term_search_min_chars ) || ( strlen( $s ) < $term_search_min_chars ) ) {
+	if ( ( 0 === $term_search_min_chars ) || ( strlen( $search ) < $term_search_min_chars ) ) {
 		wp_die();
 	}
 
 	$results = get_terms(
 		array(
 			'taxonomy'   => $taxonomy,
-			'name__like' => $s,
+			'name__like' => $search,
 			'fields'     => 'names',
 			'hide_empty' => false,
 			'number'     => isset( $_GET['number'] ) ? (int) $_GET['number'] : 0,
 		)
 	);
 
+	/**
+	 * Filters the Ajax term search results.
+	 *
+	 * @since 6.1.0
+	 *
+	 * @param string[]    $results         Array of term names.
+	 * @param WP_Taxonomy $taxonomy_object The taxonomy object.
+	 * @param string      $search          The search term.
+	 */
+	$results = apply_filters( 'ajax_term_search_results', $results, $taxonomy_object, $search );
+
 	echo implode( "\n", $results );
 	wp_die();
 }
 
 /**
- * Ajax handler for compression testing.
+ * Handles compression testing via AJAX.
  *
  * @since 3.1.0
  */
@@ -177,7 +188,12 @@
 	}
 
 	if ( ini_get( 'zlib.output_compression' ) || 'ob_gzhandler' === ini_get( 'output_handler' ) ) {
-		update_site_option( 'can_compress_scripts', 0 );
+		// Use `update_option()` on single site to mark the option for autoloading.
+		if ( is_multisite() ) {
+			update_site_option( 'can_compress_scripts', 0 );
+		} else {
+			update_option( 'can_compress_scripts', 0, true );
+		}
 		wp_die( 0 );
 	}
 
@@ -189,10 +205,10 @@
 		$force_gzip = ( defined( 'ENFORCE_GZIP' ) && ENFORCE_GZIP );
 		$test_str   = '"wpCompressionTest Lorem ipsum dolor sit amet consectetuer mollis sapien urna ut a. Eu nonummy condimentum fringilla tempor pretium platea vel nibh netus Maecenas. Hac molestie amet justo quis pellentesque est ultrices interdum nibh Morbi. Cras mattis pretium Phasellus ante ipsum ipsum ut sociis Suspendisse Lorem. Ante et non molestie. Porta urna Vestibulum egestas id congue nibh eu risus gravida sit. Ac augue auctor Ut et non a elit massa id sodales. Elit eu Nulla at nibh adipiscing mattis lacus mauris at tempus. Netus nibh quis suscipit nec feugiat eget sed lorem et urna. Pellentesque lacus at ut massa consectetuer ligula ut auctor semper Pellentesque. Ut metus massa nibh quam Curabitur molestie nec mauris congue. Volutpat molestie elit justo facilisis neque ac risus Ut nascetur tristique. Vitae sit lorem tellus et quis Phasellus lacus tincidunt nunc Fusce. Pharetra wisi Suspendisse mus sagittis libero lacinia Integer consequat ac Phasellus. Et urna ac cursus tortor aliquam Aliquam amet tellus volutpat Vestibulum. Justo interdum condimentum In augue congue tellus sollicitudin Quisque quis nibh."';
 
-		if ( 1 == $_GET['test'] ) {
+		if ( '1' === $_GET['test'] ) {
 			echo $test_str;
 			wp_die();
-		} elseif ( 2 == $_GET['test'] ) {
+		} elseif ( '2' === $_GET['test'] ) {
 			if ( ! isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
 				wp_die( -1 );
 			}
@@ -211,10 +227,20 @@
 			wp_die();
 		} elseif ( 'no' === $_GET['test'] ) {
 			check_ajax_referer( 'update_can_compress_scripts' );
-			update_site_option( 'can_compress_scripts', 0 );
+			// Use `update_option()` on single site to mark the option for autoloading.
+			if ( is_multisite() ) {
+				update_site_option( 'can_compress_scripts', 0 );
+			} else {
+				update_option( 'can_compress_scripts', 0, true );
+			}
 		} elseif ( 'yes' === $_GET['test'] ) {
 			check_ajax_referer( 'update_can_compress_scripts' );
-			update_site_option( 'can_compress_scripts', 1 );
+			// Use `update_option()` on single site to mark the option for autoloading.
+			if ( is_multisite() ) {
+				update_site_option( 'can_compress_scripts', 1 );
+			} else {
+				update_option( 'can_compress_scripts', 1, true );
+			}
 		}
 	}
 
@@ -222,7 +248,7 @@
 }
 
 /**
- * Ajax handler for image editor previews.
+ * Handles image editor previews via AJAX.
  *
  * @since 3.1.0
  */
@@ -234,7 +260,7 @@
 
 	check_ajax_referer( "image_editor-$post_id" );
 
-	include_once ABSPATH . 'wp-admin/includes/image-edit.php';
+	require_once ABSPATH . 'wp-admin/includes/image-edit.php';
 
 	if ( ! stream_preview_image( $post_id ) ) {
 		wp_die( -1 );
@@ -244,11 +270,11 @@
 }
 
 /**
- * Ajax handler for oEmbed caching.
+ * Handles oEmbed caching via AJAX.
  *
  * @since 3.1.0
  *
- * @global WP_Embed $wp_embed
+ * @global WP_Embed $wp_embed WordPress Embed object.
  */
 function wp_ajax_oembed_cache() {
 	$GLOBALS['wp_embed']->cache_oembed( $_GET['post'] );
@@ -256,7 +282,7 @@
 }
 
 /**
- * Ajax handler for user autocomplete.
+ * Handles user autocomplete via AJAX.
  *
  * @since 3.4.0
  */
@@ -272,16 +298,20 @@
 
 	$return = array();
 
-	// Check the type of request.
-	// Current allowed values are `add` and `search`.
+	/*
+	 * Check the type of request.
+	 * Current allowed values are `add` and `search`.
+	 */
 	if ( isset( $_REQUEST['autocomplete_type'] ) && 'search' === $_REQUEST['autocomplete_type'] ) {
 		$type = $_REQUEST['autocomplete_type'];
 	} else {
 		$type = 'add';
 	}
 
-	// Check the desired field for value.
-	// Current allowed values are `user_email` and `user_login`.
+	/*
+	 * Check the desired field for value.
+	 * Current allowed values are `user_email` and `user_login`.
+	 */
 	if ( isset( $_REQUEST['autocomplete_field'] ) && 'user_email' === $_REQUEST['autocomplete_field'] ) {
 		$field = $_REQUEST['autocomplete_field'];
 	} else {
@@ -383,7 +413,7 @@
 }
 
 /**
- * Ajax handler for dashboard widgets.
+ * Handles dashboard widgets via AJAX.
  *
  * @since 3.4.0
  */
@@ -404,7 +434,7 @@
 }
 
 /**
- * Ajax handler for Customizer preview logged-in status.
+ * Handles Customizer preview logged-in status via AJAX.
  *
  * @since 3.4.0
  */
@@ -431,7 +461,7 @@
 	$total    = isset( $_POST['_total'] ) ? (int) $_POST['_total'] : 0;
 	$per_page = isset( $_POST['_per_page'] ) ? (int) $_POST['_per_page'] : 0;
 	$page     = isset( $_POST['_page'] ) ? (int) $_POST['_page'] : 0;
-	$url      = isset( $_POST['_url'] ) ? esc_url_raw( $_POST['_url'] ) : '';
+	$url      = isset( $_POST['_url'] ) ? sanitize_url( $_POST['_url'] ) : '';
 
 	// JS didn't send us everything we need to know. Just die with success message.
 	if ( ! $total || ! $per_page || ! $page || ! $url ) {
@@ -483,7 +513,7 @@
 	}
 
 	// Only do the expensive stuff on a page-break, and about 1 other time per page.
-	if ( 0 == $total % $per_page || 1 == mt_rand( 1, $per_page ) ) {
+	if ( 0 === $total % $per_page || 1 === mt_rand( 1, $per_page ) ) {
 		$post_id = 0;
 		// What type of comment count are we looking for?
 		$status = 'all';
@@ -531,8 +561,8 @@
 				'postId'               => $comment ? $comment->comment_post_ID : '',
 				/* translators: %s: Number of comments. */
 				'total_items_i18n'     => sprintf( _n( '%s item', '%s items', $total ), number_format_i18n( $total ) ),
-				'total_pages'          => ceil( $total / $per_page ),
-				'total_pages_i18n'     => number_format_i18n( ceil( $total / $per_page ) ),
+				'total_pages'          => (int) ceil( $total / $per_page ),
+				'total_pages_i18n'     => number_format_i18n( (int) ceil( $total / $per_page ) ),
 				'total'                => $total,
 				'time'                 => $time,
 				'in_moderation'        => $counts->moderated,
@@ -552,7 +582,7 @@
 //
 
 /**
- * Ajax handler for adding a hierarchical term.
+ * Handles adding a hierarchical term via AJAX.
  *
  * @since 3.1.0
  * @access private
@@ -682,7 +712,7 @@
 }
 
 /**
- * Ajax handler for deleting a comment.
+ * Handles deleting a comment via AJAX.
  *
  * @since 3.1.0
  */
@@ -703,13 +733,13 @@
 	$status = wp_get_comment_status( $comment );
 	$delta  = -1;
 
-	if ( isset( $_POST['trash'] ) && 1 == $_POST['trash'] ) {
+	if ( isset( $_POST['trash'] ) && '1' === $_POST['trash'] ) {
 		if ( 'trash' === $status ) {
 			wp_die( time() );
 		}
 
 		$r = wp_trash_comment( $comment );
-	} elseif ( isset( $_POST['untrash'] ) && 1 == $_POST['untrash'] ) {
+	} elseif ( isset( $_POST['untrash'] ) && '1' === $_POST['untrash'] ) {
 		if ( 'trash' !== $status ) {
 			wp_die( time() );
 		}
@@ -720,13 +750,13 @@
 		if ( ! isset( $_POST['comment_status'] ) || 'trash' !== $_POST['comment_status'] ) {
 			$delta = 1;
 		}
-	} elseif ( isset( $_POST['spam'] ) && 1 == $_POST['spam'] ) {
+	} elseif ( isset( $_POST['spam'] ) && '1' === $_POST['spam'] ) {
 		if ( 'spam' === $status ) {
 			wp_die( time() );
 		}
 
 		$r = wp_spam_comment( $comment );
-	} elseif ( isset( $_POST['unspam'] ) && 1 == $_POST['unspam'] ) {
+	} elseif ( isset( $_POST['unspam'] ) && '1' === $_POST['unspam'] ) {
 		if ( 'spam' !== $status ) {
 			wp_die( time() );
 		}
@@ -737,7 +767,7 @@
 		if ( ! isset( $_POST['comment_status'] ) || 'spam' !== $_POST['comment_status'] ) {
 			$delta = 1;
 		}
-	} elseif ( isset( $_POST['delete'] ) && 1 == $_POST['delete'] ) {
+	} elseif ( isset( $_POST['delete'] ) && '1' === $_POST['delete'] ) {
 		$r = wp_delete_comment( $comment );
 	} else {
 		wp_die( -1 );
@@ -752,7 +782,7 @@
 }
 
 /**
- * Ajax handler for deleting a tag.
+ * Handles deleting a tag via AJAX.
  *
  * @since 3.1.0
  */
@@ -779,7 +809,7 @@
 }
 
 /**
- * Ajax handler for deleting a link.
+ * Handles deleting a link via AJAX.
  *
  * @since 3.1.0
  */
@@ -805,7 +835,7 @@
 }
 
 /**
- * Ajax handler for deleting meta.
+ * Handles deleting meta via AJAX.
  *
  * @since 3.1.0
  */
@@ -831,7 +861,7 @@
 }
 
 /**
- * Ajax handler for deleting a post.
+ * Handles deleting a post via AJAX.
  *
  * @since 3.1.0
  *
@@ -861,7 +891,7 @@
 }
 
 /**
- * Ajax handler for sending a post to the Trash.
+ * Handles sending a post to the Trash via AJAX.
  *
  * @since 3.1.0
  *
@@ -897,7 +927,7 @@
 }
 
 /**
- * Ajax handler to restore a post from the Trash.
+ * Handles restoring a post from the Trash via AJAX.
  *
  * @since 3.1.0
  *
@@ -912,7 +942,7 @@
 }
 
 /**
- * Ajax handler to delete a page.
+ * Handles deleting a page via AJAX.
  *
  * @since 3.1.0
  *
@@ -942,7 +972,7 @@
 }
 
 /**
- * Ajax handler to dim a comment.
+ * Handles dimming a comment via AJAX.
  *
  * @since 3.1.0
  */
@@ -970,7 +1000,7 @@
 
 	$current = wp_get_comment_status( $comment );
 
-	if ( isset( $_POST['new'] ) && $_POST['new'] == $current ) {
+	if ( isset( $_POST['new'] ) && $_POST['new'] === $current ) {
 		wp_die( time() );
 	}
 
@@ -998,7 +1028,7 @@
 }
 
 /**
- * Ajax handler for adding a link category.
+ * Handles adding a link category via AJAX.
  *
  * @since 3.1.0
  *
@@ -1010,9 +1040,10 @@
 	}
 
 	check_ajax_referer( $action );
-	$tax = get_taxonomy( 'link_category' );
-
-	if ( ! current_user_can( $tax->cap->manage_terms ) ) {
+
+	$taxonomy_object = get_taxonomy( 'link_category' );
+
+	if ( ! current_user_can( $taxonomy_object->cap->manage_terms ) ) {
 		wp_die( -1 );
 	}
 
@@ -1050,16 +1081,17 @@
 }
 
 /**
- * Ajax handler to add a tag.
+ * Handles adding a tag via AJAX.
  *
  * @since 3.1.0
  */
 function wp_ajax_add_tag() {
 	check_ajax_referer( 'add-tag', '_wpnonce_add-tag' );
-	$taxonomy = ! empty( $_POST['taxonomy'] ) ? $_POST['taxonomy'] : 'post_tag';
-	$tax      = get_taxonomy( $taxonomy );
-
-	if ( ! current_user_can( $tax->cap->edit_terms ) ) {
+
+	$taxonomy        = ! empty( $_POST['taxonomy'] ) ? $_POST['taxonomy'] : 'post_tag';
+	$taxonomy_object = get_taxonomy( $taxonomy );
+
+	if ( ! current_user_can( $taxonomy_object->cap->edit_terms ) ) {
 		wp_die( -1 );
 	}
 
@@ -1111,8 +1143,8 @@
 	require ABSPATH . 'wp-admin/includes/edit-tag-messages.php';
 
 	$message = '';
-	if ( isset( $messages[ $tax->name ][1] ) ) {
-		$message = $messages[ $tax->name ][1];
+	if ( isset( $messages[ $taxonomy_object->name ][1] ) ) {
+		$message = $messages[ $taxonomy_object->name ][1];
 	} elseif ( isset( $messages['_item'][1] ) ) {
 		$message = $messages['_item'][1];
 	}
@@ -1141,7 +1173,7 @@
 }
 
 /**
- * Ajax handler for getting a tagcloud.
+ * Handles getting a tagcloud via AJAX.
  *
  * @since 3.1.0
  */
@@ -1150,14 +1182,14 @@
 		wp_die( 0 );
 	}
 
-	$taxonomy = sanitize_key( $_POST['tax'] );
-	$tax      = get_taxonomy( $taxonomy );
-
-	if ( ! $tax ) {
+	$taxonomy        = sanitize_key( $_POST['tax'] );
+	$taxonomy_object = get_taxonomy( $taxonomy );
+
+	if ( ! $taxonomy_object ) {
 		wp_die( 0 );
 	}
 
-	if ( ! current_user_can( $tax->cap->assign_terms ) ) {
+	if ( ! current_user_can( $taxonomy_object->cap->assign_terms ) ) {
 		wp_die( -1 );
 	}
 
@@ -1171,7 +1203,7 @@
 	);
 
 	if ( empty( $tags ) ) {
-		wp_die( $tax->labels->not_found );
+		wp_die( $taxonomy_object->labels->not_found );
 	}
 
 	if ( is_wp_error( $tags ) ) {
@@ -1201,7 +1233,7 @@
 }
 
 /**
- * Ajax handler for getting comments.
+ * Handles getting comments via AJAX.
  *
  * @since 3.1.0
  *
@@ -1264,7 +1296,7 @@
 }
 
 /**
- * Ajax handler for replying to a comment.
+ * Handles replying to a comment via AJAX.
  *
  * @since 3.1.0
  *
@@ -1277,14 +1309,14 @@
 
 	check_ajax_referer( $action, '_ajax_nonce-replyto-comment' );
 
-	$comment_post_ID = (int) $_POST['comment_post_ID'];
-	$post            = get_post( $comment_post_ID );
+	$comment_post_id = (int) $_POST['comment_post_ID'];
+	$post            = get_post( $comment_post_id );
 
 	if ( ! $post ) {
 		wp_die( -1 );
 	}
 
-	if ( ! current_user_can( 'edit_post', $comment_post_ID ) ) {
+	if ( ! current_user_can( 'edit_post', $comment_post_id ) ) {
 		wp_die( -1 );
 	}
 
@@ -1297,19 +1329,17 @@
 	$user = wp_get_current_user();
 
 	if ( $user->exists() ) {
-		$user_ID              = $user->ID;
 		$comment_author       = wp_slash( $user->display_name );
 		$comment_author_email = wp_slash( $user->user_email );
 		$comment_author_url   = wp_slash( $user->user_url );
-		$comment_content      = trim( $_POST['content'] );
-		$comment_type         = isset( $_POST['comment_type'] ) ? trim( $_POST['comment_type'] ) : 'comment';
+		$user_id              = $user->ID;
 
 		if ( current_user_can( 'unfiltered_html' ) ) {
 			if ( ! isset( $_POST['_wp_unfiltered_html_comment'] ) ) {
 				$_POST['_wp_unfiltered_html_comment'] = '';
 			}
 
-			if ( wp_create_nonce( 'unfiltered-html-comment' ) != $_POST['_wp_unfiltered_html_comment'] ) {
+			if ( wp_create_nonce( 'unfiltered-html-comment' ) !== $_POST['_wp_unfiltered_html_comment'] ) {
 				kses_remove_filters(); // Start with a clean slate.
 				kses_init_filters();   // Set up the filters.
 				remove_filter( 'pre_comment_content', 'wp_filter_post_kses' );
@@ -1320,10 +1350,14 @@
 		wp_die( __( 'Sorry, you must be logged in to reply to a comment.' ) );
 	}
 
+	$comment_content = trim( $_POST['content'] );
+
 	if ( '' === $comment_content ) {
 		wp_die( __( 'Please type your comment text.' ) );
 	}
 
+	$comment_type = isset( $_POST['comment_type'] ) ? trim( $_POST['comment_type'] ) : 'comment';
+
 	$comment_parent = 0;
 
 	if ( isset( $_POST['comment_ID'] ) ) {
@@ -1331,13 +1365,26 @@
 	}
 
 	$comment_auto_approved = false;
-	$commentdata           = compact( 'comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID' );
+
+	$commentdata = array(
+		'comment_post_ID' => $comment_post_id,
+	);
+
+	$commentdata += compact(
+		'comment_author',
+		'comment_author_email',
+		'comment_author_url',
+		'comment_content',
+		'comment_type',
+		'comment_parent',
+		'user_id'
+	);
 
 	// Automatically approve parent comment.
 	if ( ! empty( $_POST['approve_parent'] ) ) {
 		$parent = get_comment( $comment_parent );
 
-		if ( $parent && '0' === $parent->comment_approved && $parent->comment_post_ID == $comment_post_ID ) {
+		if ( $parent && '0' === $parent->comment_approved && (int) $parent->comment_post_ID === $comment_post_id ) {
 			if ( ! current_user_can( 'edit_comment', $parent->comment_ID ) ) {
 				wp_die( -1 );
 			}
@@ -1409,7 +1456,7 @@
 }
 
 /**
- * Ajax handler for editing a comment.
+ * Handles editing a comment via AJAX.
  *
  * @since 3.1.0
  */
@@ -1435,8 +1482,12 @@
 		wp_die( $updated->get_error_message() );
 	}
 
-	$position      = ( isset( $_POST['position'] ) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1';
-	$checkbox      = ( isset( $_POST['checkbox'] ) && true == $_POST['checkbox'] ) ? 1 : 0;
+	$position = ( isset( $_POST['position'] ) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1';
+	/*
+	 * Checkbox is used to differentiate between the Edit Comments screen (1)
+	 * and the Comments section on the Edit Post screen (0).
+	 */
+	$checkbox      = ( isset( $_POST['checkbox'] ) && '1' === $_POST['checkbox'] ) ? 1 : 0;
 	$wp_list_table = _get_list_table( $checkbox ? 'WP_Comments_List_Table' : 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
 
 	$comment = get_comment( $comment_id );
@@ -1464,7 +1515,7 @@
 }
 
 /**
- * Ajax handler for adding a menu item.
+ * Handles adding a menu item via AJAX.
  *
  * @since 3.1.0
  */
@@ -1477,8 +1528,10 @@
 
 	require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
 
-	// For performance reasons, we omit some object properties from the checklist.
-	// The following is a hacky way to restore them when adding non-custom items.
+	/*
+	 * For performance reasons, we omit some object properties from the checklist.
+	 * The following is a hacky way to restore them when adding non-custom items.
+	 */
 	$menu_items_data = array();
 
 	foreach ( (array) $_POST['menu-item'] as $menu_item_data ) {
@@ -1542,7 +1595,7 @@
 			'before'      => '',
 			'link_after'  => '',
 			'link_before' => '',
-			'walker'      => new $walker_class_name,
+			'walker'      => new $walker_class_name(),
 		);
 
 		echo walk_nav_menu_tree( $menu_items, 0, (object) $args );
@@ -1552,7 +1605,7 @@
 }
 
 /**
- * Ajax handler for adding meta.
+ * Handles adding meta via AJAX.
  *
  * @since 3.1.0
  */
@@ -1579,8 +1632,13 @@
 			$post_data['post_type']   = $post->post_type;
 			$post_data['post_status'] = 'draft';
 			$now                      = time();
-			/* translators: 1: Post creation date, 2: Post creation time. */
-			$post_data['post_title'] = sprintf( __( 'Draft created on %1$s at %2$s' ), gmdate( __( 'F j, Y' ), $now ), gmdate( __( 'g:i a' ), $now ) );
+
+			$post_data['post_title'] = sprintf(
+				/* translators: 1: Post creation date, 2: Post creation time. */
+				__( 'Draft created on %1$s at %2$s' ),
+				gmdate( __( 'F j, Y' ), $now ),
+				gmdate( __( 'g:i a' ), $now )
+			);
 
 			$pid = edit_post( $post_data );
 
@@ -1645,7 +1703,7 @@
 			wp_die( -1 );
 		}
 
-		if ( $meta->meta_value != $value || $meta->meta_key != $key ) {
+		if ( $meta->meta_value !== $value || $meta->meta_key !== $key ) {
 			$u = update_metadata_by_mid( 'post', $mid, $value, $key );
 			if ( ! $u ) {
 				wp_die( 0 ); // We know meta exists; we also know it's unchanged (or DB error, in which case there are bigger problems).
@@ -1674,7 +1732,7 @@
 }
 
 /**
- * Ajax handler for adding a user.
+ * Handles adding a user via AJAX.
  *
  * @since 3.1.0
  *
@@ -1729,7 +1787,7 @@
 }
 
 /**
- * Ajax handler for closed post boxes.
+ * Handles closed post boxes via AJAX.
  *
  * @since 3.1.0
  */
@@ -1743,7 +1801,7 @@
 
 	$page = isset( $_POST['page'] ) ? $_POST['page'] : '';
 
-	if ( sanitize_key( $page ) != $page ) {
+	if ( sanitize_key( $page ) !== $page ) {
 		wp_die( 0 );
 	}
 
@@ -1766,7 +1824,7 @@
 }
 
 /**
- * Ajax handler for hidden columns.
+ * Handles hidden columns via AJAX.
  *
  * @since 3.1.0
  */
@@ -1774,7 +1832,7 @@
 	check_ajax_referer( 'screen-options-nonce', 'screenoptionnonce' );
 	$page = isset( $_POST['page'] ) ? $_POST['page'] : '';
 
-	if ( sanitize_key( $page ) != $page ) {
+	if ( sanitize_key( $page ) !== $page ) {
 		wp_die( 0 );
 	}
 
@@ -1790,7 +1848,7 @@
 }
 
 /**
- * Ajax handler for updating whether to display the welcome panel.
+ * Handles updating whether to display the welcome panel via AJAX.
  *
  * @since 3.1.0
  */
@@ -1807,7 +1865,7 @@
 }
 
 /**
- * Ajax handler for retrieving menu meta boxes.
+ * Handles for retrieving menu meta boxes via AJAX.
  *
  * @since 3.1.0
  */
@@ -1858,7 +1916,7 @@
 }
 
 /**
- * Ajax handler for internal linking.
+ * Handles internal linking via AJAX.
  *
  * @since 3.1.0
  */
@@ -1894,7 +1952,7 @@
 }
 
 /**
- * Ajax handler for menu locations save.
+ * Handles saving menu locations via AJAX.
  *
  * @since 3.1.0
  */
@@ -1914,7 +1972,7 @@
 }
 
 /**
- * Ajax handler for saving the meta box order.
+ * Handles saving the meta box order via AJAX.
  *
  * @since 3.1.0
  */
@@ -1929,7 +1987,7 @@
 
 	$page = isset( $_POST['page'] ) ? $_POST['page'] : '';
 
-	if ( sanitize_key( $page ) != $page ) {
+	if ( sanitize_key( $page ) !== $page ) {
 		wp_die( 0 );
 	}
 
@@ -1950,7 +2008,7 @@
 }
 
 /**
- * Ajax handler for menu quick searching.
+ * Handles menu quick searching via AJAX.
  *
  * @since 3.1.0
  */
@@ -1967,7 +2025,7 @@
 }
 
 /**
- * Ajax handler to retrieve a permalink.
+ * Handles retrieving a permalink via AJAX.
  *
  * @since 3.1.0
  */
@@ -1978,7 +2036,7 @@
 }
 
 /**
- * Ajax handler to retrieve a sample permalink.
+ * Handles retrieving a sample permalink via AJAX.
  *
  * @since 3.1.0
  */
@@ -1991,7 +2049,7 @@
 }
 
 /**
- * Ajax handler for Quick Edit saving a post from a list table.
+ * Handles Quick Edit saving a post from a list table via AJAX.
  *
  * @since 3.1.0
  *
@@ -2006,19 +2064,19 @@
 		wp_die();
 	}
 
-	$post_ID = (int) $_POST['post_ID'];
+	$post_id = (int) $_POST['post_ID'];
 
 	if ( 'page' === $_POST['post_type'] ) {
-		if ( ! current_user_can( 'edit_page', $post_ID ) ) {
+		if ( ! current_user_can( 'edit_page', $post_id ) ) {
 			wp_die( __( 'Sorry, you are not allowed to edit this page.' ) );
 		}
 	} else {
-		if ( ! current_user_can( 'edit_post', $post_ID ) ) {
+		if ( ! current_user_can( 'edit_post', $post_id ) ) {
 			wp_die( __( 'Sorry, you are not allowed to edit this post.' ) );
 		}
 	}
 
-	$last = wp_check_post_lock( $post_ID );
+	$last = wp_check_post_lock( $post_id );
 	if ( $last ) {
 		$last_user      = get_userdata( $last );
 		$last_user_name = $last_user ? $last_user->display_name : __( 'Someone' );
@@ -2037,7 +2095,7 @@
 
 	$data = &$_POST;
 
-	$post = get_post( $post_ID, ARRAY_A );
+	$post = get_post( $post_id, ARRAY_A );
 
 	// Since it's coming from the database.
 	$post = wp_slash( $post );
@@ -2100,7 +2158,7 @@
 		while ( $parent > 0 ) {
 			$parent_post = get_post( $parent );
 			$parent      = $parent_post->post_parent;
-			$level++;
+			++$level;
 		}
 	}
 
@@ -2110,17 +2168,17 @@
 }
 
 /**
- * Ajax handler for quick edit saving for a term.
+ * Handles Quick Edit saving for a term via AJAX.
  *
  * @since 3.1.0
  */
 function wp_ajax_inline_save_tax() {
 	check_ajax_referer( 'taxinlineeditnonce', '_inline_edit' );
 
-	$taxonomy = sanitize_key( $_POST['taxonomy'] );
-	$tax      = get_taxonomy( $taxonomy );
-
-	if ( ! $tax ) {
+	$taxonomy        = sanitize_key( $_POST['taxonomy'] );
+	$taxonomy_object = get_taxonomy( $taxonomy );
+
+	if ( ! $taxonomy_object ) {
 		wp_die( 0 );
 	}
 
@@ -2162,7 +2220,7 @@
 	while ( $parent > 0 ) {
 		$parent_tag = get_term( $parent, $taxonomy );
 		$parent     = $parent_tag->parent;
-		$level++;
+		++$level;
 	}
 
 	$wp_list_table->single_row( $tag, $level );
@@ -2170,7 +2228,7 @@
 }
 
 /**
- * Ajax handler for querying posts for the Find Posts modal.
+ * Handles querying posts for the Find Posts modal via AJAX.
  *
  * @see window.findPosts
  *
@@ -2182,15 +2240,16 @@
 	$post_types = get_post_types( array( 'public' => true ), 'objects' );
 	unset( $post_types['attachment'] );
 
-	$s    = wp_unslash( $_POST['ps'] );
 	$args = array(
 		'post_type'      => array_keys( $post_types ),
 		'post_status'    => 'any',
 		'posts_per_page' => 50,
 	);
 
-	if ( '' !== $s ) {
-		$args['s'] = $s;
+	$search = wp_unslash( $_POST['ps'] );
+
+	if ( '' !== $search ) {
+		$args['s'] = $search;
 	}
 
 	$posts = get_posts( $args );
@@ -2238,7 +2297,7 @@
 }
 
 /**
- * Ajax handler for saving the widgets order.
+ * Handles saving the widgets order via AJAX.
  *
  * @since 3.1.0
  */
@@ -2262,7 +2321,7 @@
 				$val = explode( ',', $val );
 
 				foreach ( $val as $k => $v ) {
-					if ( strpos( $v, 'widget-' ) === false ) {
+					if ( ! str_contains( $v, 'widget-' ) ) {
 						continue;
 					}
 
@@ -2280,7 +2339,7 @@
 }
 
 /**
- * Ajax handler for saving a widget.
+ * Handles saving a widget via AJAX.
  *
  * @since 3.1.0
  *
@@ -2357,7 +2416,7 @@
 
 	foreach ( (array) $wp_registered_widget_updates as $name => $control ) {
 
-		if ( $name == $id_base ) {
+		if ( $name === $id_base ) {
 			if ( ! is_callable( $control['callback'] ) ) {
 				continue;
 			}
@@ -2389,7 +2448,7 @@
 }
 
 /**
- * Ajax handler for updating a widget.
+ * Handles updating a widget via AJAX.
  *
  * @since 3.9.0
  *
@@ -2401,7 +2460,7 @@
 }
 
 /**
- * Ajax handler for removing inactive widgets.
+ * Handles removing inactive widgets via AJAX.
  *
  * @since 4.4.0
  */
@@ -2438,7 +2497,7 @@
 }
 
 /**
- * Ajax handler for creating missing image sub-sizes for just uploaded images.
+ * Handles creating missing image sub-sizes for just uploaded images via AJAX.
  *
  * @since 5.3.0
  */
@@ -2468,14 +2527,18 @@
 		}
 	}
 
-	// Set a custom header with the attachment_id.
-	// Used by the browser/client to resume creating image sub-sizes after a PHP fatal error.
+	/*
+	 * Set a custom header with the attachment_id.
+	 * Used by the browser/client to resume creating image sub-sizes after a PHP fatal error.
+	 */
 	if ( ! headers_sent() ) {
 		header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id );
 	}
 
-	// This can still be pretty slow and cause timeout or out of memory errors.
-	// The js that handles the response would need to also handle HTTP 500 errors.
+	/*
+	 * This can still be pretty slow and cause timeout or out of memory errors.
+	 * The js that handles the response would need to also handle HTTP 500 errors.
+	 */
 	wp_update_image_subsizes( $attachment_id );
 
 	if ( ! empty( $_POST['_legacy_support'] ) ) {
@@ -2495,7 +2558,7 @@
 }
 
 /**
- * Ajax handler for uploading attachments
+ * Handles uploading attachments via AJAX.
  *
  * @since 3.3.0
  */
@@ -2503,7 +2566,7 @@
 	check_ajax_referer( 'media-form' );
 	/*
 	 * This function does not use wp_send_json_success() / wp_send_json_error()
-	 * as the html4 Plupload handler requires a text/html content-type for older IE.
+	 * as the html4 Plupload handler requires a text/html Content-Type for older IE.
 	 * See https://core.trac.wordpress.org/ticket/31037
 	 */
 
@@ -2608,7 +2671,7 @@
 }
 
 /**
- * Ajax handler for image editing.
+ * Handles image editing via AJAX.
  *
  * @since 3.1.0
  */
@@ -2620,7 +2683,7 @@
 	}
 
 	check_ajax_referer( "image_editor-$attachment_id" );
-	include_once ABSPATH . 'wp-admin/includes/image-edit.php';
+	require_once ABSPATH . 'wp-admin/includes/image-edit.php';
 
 	$msg = false;
 
@@ -2663,37 +2726,37 @@
 }
 
 /**
- * Ajax handler for setting the featured image.
+ * Handles setting the featured image via AJAX.
  *
  * @since 3.1.0
  */
 function wp_ajax_set_post_thumbnail() {
 	$json = ! empty( $_REQUEST['json'] ); // New-style request.
 
-	$post_ID = (int) $_POST['post_id'];
-	if ( ! current_user_can( 'edit_post', $post_ID ) ) {
+	$post_id = (int) $_POST['post_id'];
+	if ( ! current_user_can( 'edit_post', $post_id ) ) {
 		wp_die( -1 );
 	}
 
 	$thumbnail_id = (int) $_POST['thumbnail_id'];
 
 	if ( $json ) {
-		check_ajax_referer( "update-post_$post_ID" );
+		check_ajax_referer( "update-post_$post_id" );
 	} else {
-		check_ajax_referer( "set_post_thumbnail-$post_ID" );
-	}
-
-	if ( '-1' == $thumbnail_id ) {
-		if ( delete_post_thumbnail( $post_ID ) ) {
-			$return = _wp_post_thumbnail_html( null, $post_ID );
+		check_ajax_referer( "set_post_thumbnail-$post_id" );
+	}
+
+	if ( -1 === $thumbnail_id ) {
+		if ( delete_post_thumbnail( $post_id ) ) {
+			$return = _wp_post_thumbnail_html( null, $post_id );
 			$json ? wp_send_json_success( $return ) : wp_die( $return );
 		} else {
 			wp_die( 0 );
 		}
 	}
 
-	if ( set_post_thumbnail( $post_ID, $thumbnail_id ) ) {
-		$return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID );
+	if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) {
+		$return = _wp_post_thumbnail_html( $thumbnail_id, $post_id );
 		$json ? wp_send_json_success( $return ) : wp_die( $return );
 	}
 
@@ -2701,16 +2764,16 @@
 }
 
 /**
- * Ajax handler for retrieving HTML for the featured image.
+ * Handles retrieving HTML for the featured image via AJAX.
  *
  * @since 4.6.0
  */
 function wp_ajax_get_post_thumbnail_html() {
-	$post_ID = (int) $_POST['post_id'];
-
-	check_ajax_referer( "update-post_$post_ID" );
-
-	if ( ! current_user_can( 'edit_post', $post_ID ) ) {
+	$post_id = (int) $_POST['post_id'];
+
+	check_ajax_referer( "update-post_$post_id" );
+
+	if ( ! current_user_can( 'edit_post', $post_id ) ) {
 		wp_die( -1 );
 	}
 
@@ -2721,12 +2784,12 @@
 		$thumbnail_id = null;
 	}
 
-	$return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID );
+	$return = _wp_post_thumbnail_html( $thumbnail_id, $post_id );
 	wp_send_json_success( $return );
 }
 
 /**
- * Ajax handler for setting the featured image for an attachment.
+ * Handles setting the featured image for an attachment via AJAX.
  *
  * @since 4.0.0
  *
@@ -2742,6 +2805,10 @@
 		wp_send_json_error();
 	}
 
+	if ( false === check_ajax_referer( 'set-attachment-thumbnail', '_ajax_nonce', false ) ) {
+		wp_send_json_error();
+	}
+
 	$post_ids = array();
 	// For each URL, try to find its corresponding post ID.
 	foreach ( $_POST['urls'] as $url ) {
@@ -2763,7 +2830,7 @@
 		}
 
 		if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) {
-			$success++;
+			++$success;
 		}
 	}
 
@@ -2777,7 +2844,7 @@
 }
 
 /**
- * Ajax handler for date formatting.
+ * Handles formatting a date via AJAX.
  *
  * @since 3.1.0
  */
@@ -2786,7 +2853,7 @@
 }
 
 /**
- * Ajax handler for time formatting.
+ * Handles formatting a time via AJAX.
  *
  * @since 3.1.0
  */
@@ -2795,7 +2862,7 @@
 }
 
 /**
- * Ajax handler for saving posts from the fullscreen editor.
+ * Handles saving posts from the fullscreen editor via AJAX.
  *
  * @since 3.1.0
  * @deprecated 4.3.0
@@ -2839,7 +2906,7 @@
 }
 
 /**
- * Ajax handler for removing a post lock.
+ * Handles removing a post lock via AJAX.
  *
  * @since 3.1.0
  */
@@ -2863,7 +2930,7 @@
 
 	$active_lock = array_map( 'absint', explode( ':', $_POST['active_post_lock'] ) );
 
-	if ( get_current_user_id() != $active_lock[1] ) {
+	if ( get_current_user_id() !== $active_lock[1] ) {
 		wp_die( 0 );
 	}
 
@@ -2881,14 +2948,14 @@
 }
 
 /**
- * Ajax handler for dismissing a WordPress pointer.
+ * Handles dismissing a WordPress pointer via AJAX.
  *
  * @since 3.1.0
  */
 function wp_ajax_dismiss_wp_pointer() {
 	$pointer = $_POST['pointer'];
 
-	if ( sanitize_key( $pointer ) != $pointer ) {
+	if ( sanitize_key( $pointer ) !== $pointer ) {
 		wp_die( 0 );
 	}
 
@@ -2908,7 +2975,7 @@
 }
 
 /**
- * Ajax handler for getting an attachment.
+ * Handles getting an attachment via AJAX.
  *
  * @since 3.5.0
  */
@@ -2944,7 +3011,7 @@
 }
 
 /**
- * Ajax handler for querying attachments.
+ * Handles querying attachments via AJAX.
  *
  * @since 3.5.0
  */
@@ -2994,7 +3061,7 @@
 
 	// Filter query clauses to include filenames.
 	if ( isset( $query['s'] ) ) {
-		add_filter( 'posts_clauses', '_filter_query_attachment_filenames' );
+		add_filter( 'wp_allow_query_attachment_by_filename', '__return_true' );
 	}
 
 	/**
@@ -3009,6 +3076,7 @@
 	 */
 	$query             = apply_filters( 'ajax_query_attachments_args', $query );
 	$attachments_query = new WP_Query( $query );
+	update_post_parent_caches( $attachments_query->posts );
 
 	$posts       = array_map( 'wp_prepare_attachment_for_js', $attachments_query->posts );
 	$posts       = array_filter( $posts );
@@ -3025,16 +3093,16 @@
 
 	$posts_per_page = (int) $attachments_query->get( 'posts_per_page' );
 
-	$max_pages = $posts_per_page ? ceil( $total_posts / $posts_per_page ) : 0;
+	$max_pages = $posts_per_page ? (int) ceil( $total_posts / $posts_per_page ) : 0;
 
 	header( 'X-WP-Total: ' . (int) $total_posts );
-	header( 'X-WP-TotalPages: ' . (int) $max_pages );
+	header( 'X-WP-TotalPages: ' . $max_pages );
 
 	wp_send_json_success( $posts );
 }
 
 /**
- * Ajax handler for updating attachment attributes.
+ * Handles updating attachment attributes via AJAX.
  *
  * @since 3.5.0
  */
@@ -3120,7 +3188,7 @@
 }
 
 /**
- * Ajax handler for saving backward compatible attachment attributes.
+ * Handles saving backward compatible attachment attributes via AJAX.
  *
  * @since 3.5.0
  */
@@ -3178,7 +3246,7 @@
 }
 
 /**
- * Ajax handler for saving the attachment order.
+ * Handles saving the attachment order via AJAX.
  *
  * @since 3.5.0
  */
@@ -3231,7 +3299,7 @@
 }
 
 /**
- * Ajax handler for sending an attachment to the editor.
+ * Handles sending an attachment to the editor via AJAX.
  *
  * Generates the HTML to send an attachment to the editor.
  * Backward compatible with the {@see 'media_send_to_editor'} filter
@@ -3259,7 +3327,7 @@
 		// If this attachment is unattached, attach it. Primarily a back compat thing.
 		$insert_into_post_id = (int) $_POST['post_id'];
 
-		if ( 0 == $post->post_parent && $insert_into_post_id ) {
+		if ( 0 === $post->post_parent && $insert_into_post_id ) {
 			wp_update_post(
 				array(
 					'ID'          => $id,
@@ -3270,11 +3338,11 @@
 	}
 
 	$url = empty( $attachment['url'] ) ? '' : $attachment['url'];
-	$rel = ( strpos( $url, 'attachment_id' ) || get_attachment_link( $id ) == $url );
+	$rel = ( str_contains( $url, 'attachment_id' ) || get_attachment_link( $id ) === $url );
 
 	remove_filter( 'media_send_to_editor', 'image_media_send_to_editor' );
 
-	if ( 'image' === substr( $post->post_mime_type, 0, 5 ) ) {
+	if ( str_starts_with( $post->post_mime_type, 'image' ) ) {
 		$align = isset( $attachment['align'] ) ? $attachment['align'] : 'none';
 		$size  = isset( $attachment['image-size'] ) ? $attachment['image-size'] : 'medium';
 		$alt   = isset( $attachment['image_alt'] ) ? $attachment['image_alt'] : '';
@@ -3305,7 +3373,7 @@
 }
 
 /**
- * Ajax handler for sending a link to the editor.
+ * Handles sending a link to the editor via AJAX.
  *
  * Generates the HTML to send a non-image embed link to the editor.
  *
@@ -3317,7 +3385,7 @@
  * @since 3.5.0
  *
  * @global WP_Post  $post     Global post object.
- * @global WP_Embed $wp_embed
+ * @global WP_Embed $wp_embed WordPress Embed object.
  */
 function wp_ajax_send_link_to_editor() {
 	global $post, $wp_embed;
@@ -3333,7 +3401,7 @@
 		$src = 'http://' . $src;
 	}
 
-	$src = esc_url_raw( $src );
+	$src = sanitize_url( $src );
 	if ( ! $src ) {
 		wp_send_json_error();
 	}
@@ -3377,7 +3445,7 @@
 }
 
 /**
- * Ajax handler for the Heartbeat API.
+ * Handles the Heartbeat API via AJAX.
  *
  * Runs when the user is logged in.
  *
@@ -3464,7 +3532,7 @@
 }
 
 /**
- * Ajax handler for getting revision diffs.
+ * Handles getting revision diffs via AJAX.
  *
  * @since 3.6.0
  */
@@ -3487,7 +3555,10 @@
 	}
 
 	$return = array();
-	set_time_limit( 0 );
+
+	if ( function_exists( 'set_time_limit' ) ) {
+		set_time_limit( 0 );
+	}
 
 	foreach ( $_REQUEST['compare'] as $compare_key ) {
 		list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to
@@ -3501,8 +3572,8 @@
 }
 
 /**
- * Ajax handler for auto-saving the selected color scheme for
- * a user's own profile.
+ * Handles auto-saving the selected color scheme for
+ * a user's own profile via AJAX.
  *
  * @since 3.8.0
  *
@@ -3531,7 +3602,7 @@
 }
 
 /**
- * Ajax handler for getting themes from themes_api().
+ * Handles getting themes from themes_api() via AJAX.
  *
  * @since 3.9.0
  *
@@ -3662,12 +3733,12 @@
 }
 
 /**
- * Apply [embed] Ajax handlers to a string.
+ * Applies [embed] Ajax handlers to a string.
  *
  * @since 4.0.0
  *
- * @global WP_Post    $post       Global post object.
- * @global WP_Embed   $wp_embed   Embed API instance.
+ * @global WP_Post    $post          Global post object.
+ * @global WP_Embed   $wp_embed      WordPress Embed object.
  * @global WP_Scripts $wp_scripts
  * @global int        $content_width
  */
@@ -3716,9 +3787,11 @@
 		$wp_embed->usecache = false;
 	}
 
-	if ( is_ssl() && 0 === strpos( $url, 'http://' ) ) {
-		// Admin is ssl and the user pasted non-ssl URL.
-		// Check if the provider supports ssl embeds and use that for the preview.
+	if ( is_ssl() && str_starts_with( $url, 'http://' ) ) {
+		/*
+		 * Admin is ssl and the user pasted non-ssl URL.
+		 * Check if the provider supports ssl embeds and use that for the preview.
+		 */
 		$ssl_shortcode = preg_replace( '%^(\\[embed[^\\]]*\\])http://%i', '$1https://', $shortcode );
 		$parsed        = $wp_embed->run_shortcode( $ssl_shortcode );
 
@@ -3789,7 +3862,7 @@
 		'attr' => $wp_embed->last_attr,
 	);
 
-	if ( strpos( $parsed, 'class="wp-embedded-content' ) ) {
+	if ( str_contains( $parsed, 'class="wp-embedded-content' ) ) {
 		if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
 			$script_src = includes_url( 'js/wp-embed.js' );
 		} else {
@@ -3818,13 +3891,29 @@
 
 	$shortcode = wp_unslash( $_POST['shortcode'] );
 
+	// Only process previews for media related shortcodes:
+	$found_shortcodes = get_shortcode_tags_in_content( $shortcode );
+	$media_shortcodes = array(
+		'audio',
+		'embed',
+		'playlist',
+		'video',
+		'gallery',
+	);
+
+	$other_shortcodes = array_diff( $found_shortcodes, $media_shortcodes );
+
+	if ( ! empty( $other_shortcodes ) ) {
+		wp_send_json_error();
+	}
+
 	if ( ! empty( $_POST['post_ID'] ) ) {
 		$post = get_post( (int) $_POST['post_ID'] );
 	}
 
 	// The embed shortcode requires a post.
 	if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) {
-		if ( 'embed' === $shortcode ) {
+		if ( in_array( 'embed', $found_shortcodes, true ) ) {
 			wp_send_json_error();
 		}
 	} else {
@@ -3874,7 +3963,7 @@
 }
 
 /**
- * Ajax handler for destroying multiple open sessions for a user.
+ * Handles destroying multiple open sessions for a user via AJAX.
  *
  * @since 4.1.0
  */
@@ -3912,7 +4001,7 @@
 }
 
 /**
- * Ajax handler for cropping an image.
+ * Handles cropping an image via AJAX.
  *
  * @since 4.3.0
  */
@@ -3939,7 +4028,7 @@
 			$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 ) {
+			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 );
@@ -3950,9 +4039,10 @@
 			}
 
 			/** 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'] );
+			$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' ) );
@@ -3980,46 +4070,8 @@
 			/** 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 ) ) &&
-				/*
-				 * Check if the original image has a title other than the "filename" default,
-				 * meaning the image had a title when originally uploaded or its title was edited.
-				 */
-				( $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 ),
-				);
-			}
+			// 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 );
@@ -4051,7 +4103,7 @@
 }
 
 /**
- * Ajax handler for generating a password.
+ * Handles generating a password via AJAX.
  *
  * @since 4.4.0
  */
@@ -4060,7 +4112,7 @@
 }
 
 /**
- * Ajax handler for generating a password in the no-privilege context.
+ * Handles generating a password in the no-privilege context via AJAX.
  *
  * @since 5.7.0
  */
@@ -4069,7 +4121,7 @@
 }
 
 /**
- * Ajax handler for saving the user's WordPress.org username.
+ * Handles saving the user's WordPress.org username via AJAX.
  *
  * @since 4.4.0
  */
@@ -4090,7 +4142,7 @@
 }
 
 /**
- * Ajax handler for installing a theme.
+ * Handles installing a theme via AJAX.
  *
  * @since 4.6.0
  *
@@ -4124,7 +4176,7 @@
 	}
 
 	require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
-	include_once ABSPATH . 'wp-admin/includes/theme.php';
+	require_once ABSPATH . 'wp-admin/includes/theme.php';
 
 	$api = themes_api(
 		'theme_information',
@@ -4216,7 +4268,7 @@
 }
 
 /**
- * Ajax handler for updating a theme.
+ * Handles updating a theme via AJAX.
  *
  * @since 4.6.0
  *
@@ -4311,7 +4363,7 @@
 }
 
 /**
- * Ajax handler for deleting a theme.
+ * Handles deleting a theme via AJAX.
  *
  * @since 4.6.0
  *
@@ -4369,7 +4421,7 @@
 		wp_send_json_error( $status );
 	}
 
-	include_once ABSPATH . 'wp-admin/includes/theme.php';
+	require_once ABSPATH . 'wp-admin/includes/theme.php';
 
 	$result = delete_theme( $stylesheet );
 
@@ -4385,7 +4437,7 @@
 }
 
 /**
- * Ajax handler for installing a plugin.
+ * Handles installing a plugin via AJAX.
  *
  * @since 4.6.0
  *
@@ -4417,7 +4469,7 @@
 	}
 
 	require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
-	include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
+	require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
 
 	$api = plugins_api(
 		'plugin_information',
@@ -4494,7 +4546,57 @@
 }
 
 /**
- * Ajax handler for updating a plugin.
+ * Handles activating a plugin via AJAX.
+ *
+ * @since 6.5.0
+ */
+function wp_ajax_activate_plugin() {
+	check_ajax_referer( 'updates' );
+
+	if ( empty( $_POST['name'] ) || empty( $_POST['slug'] ) || empty( $_POST['plugin'] ) ) {
+		wp_send_json_error(
+			array(
+				'slug'         => '',
+				'pluginName'   => '',
+				'plugin'       => '',
+				'errorCode'    => 'no_plugin_specified',
+				'errorMessage' => __( 'No plugin specified.' ),
+			)
+		);
+	}
+
+	$status = array(
+		'activate'   => 'plugin',
+		'slug'       => wp_unslash( $_POST['slug'] ),
+		'pluginName' => wp_unslash( $_POST['name'] ),
+		'plugin'     => wp_unslash( $_POST['plugin'] ),
+	);
+
+	if ( ! current_user_can( 'activate_plugin', $status['plugin'] ) ) {
+		$status['errorMessage'] = __( 'Sorry, you are not allowed to activate plugins on this site.' );
+		wp_send_json_error( $status );
+	}
+
+	if ( is_plugin_active( $status['plugin'] ) ) {
+		$status['errorMessage'] = sprintf(
+			/* translators: %s: Plugin name. */
+			__( '%s is already active.' ),
+			$status['pluginName']
+		);
+	}
+
+	$activated = activate_plugin( $status['plugin'] );
+
+	if ( is_wp_error( $activated ) ) {
+		$status['errorMessage'] = $activated->get_error_message();
+		wp_send_json_error( $status );
+	}
+
+	wp_send_json_success( $status );
+}
+
+/**
+ * Handles updating a plugin via AJAX.
  *
  * @since 4.2.0
  *
@@ -4602,7 +4704,7 @@
 }
 
 /**
- * Ajax handler for deleting a plugin.
+ * Handles deleting a plugin via AJAX.
  *
  * @since 4.6.0
  *
@@ -4679,7 +4781,7 @@
 }
 
 /**
- * Ajax handler for searching plugins.
+ * Handles searching plugins via AJAX.
  *
  * @since 4.6.0
  *
@@ -4736,7 +4838,7 @@
 }
 
 /**
- * Ajax handler for searching plugins to install.
+ * Handles searching plugins to install via AJAX.
  *
  * @since 4.6.0
  */
@@ -4786,7 +4888,7 @@
 }
 
 /**
- * Ajax handler for editing a theme or plugin file.
+ * Handles editing a theme or plugin file via AJAX.
  *
  * @since 4.9.0
  *
@@ -4815,7 +4917,7 @@
 }
 
 /**
- * Ajax handler for exporting a user's personal data.
+ * Handles exporting a user's personal data via AJAX.
  *
  * @since 4.9.6
  */
@@ -4875,8 +4977,8 @@
 	 *         Array of personal data exporters.
 	 *
 	 *         @type callable $callback               Callable exporter function that accepts an
-	 *                                                email address and a page and returns an array
-	 *                                                of name => value pairs of personal data.
+	 *                                                email address and a page number and returns an
+	 *                                                array of name => value pairs of personal data.
 	 *         @type string   $exporter_friendly_name Translated user facing friendly name for the
 	 *                                                exporter.
 	 *     }
@@ -4987,10 +5089,10 @@
 	 *
 	 * @since 4.9.6
 	 *
-	 * @param array  $response        The personal data for the given exporter and page.
+	 * @param array  $response        The personal data for the given exporter and page number.
 	 * @param int    $exporter_index  The index of the exporter that provided this data.
 	 * @param string $email_address   The email address associated with this personal data.
-	 * @param int    $page            The page for this response.
+	 * @param int    $page            The page number for this response.
 	 * @param int    $request_id      The privacy request post ID associated with this request.
 	 * @param bool   $send_as_email   Whether the final results of the export should be emailed to the user.
 	 * @param string $exporter_key    The key (slug) of the exporter that provided this data.
@@ -5005,7 +5107,7 @@
 }
 
 /**
- * Ajax handler for erasing personal data.
+ * Handles erasing personal data via AJAX.
  *
  * @since 4.9.6
  */
@@ -5064,8 +5166,8 @@
 	 *     @type array ...$0 {
 	 *         Array of personal data exporters.
 	 *
-	 *         @type callable $callback               Callable eraser that accepts an email address and
-	 *                                                a page and returns an array with boolean values for
+	 *         @type callable $callback               Callable eraser that accepts an email address and a page
+	 *                                                number, and returns an array with boolean values for
 	 *                                                whether items were removed or retained and any messages
 	 *                                                from the eraser, as well as if additional pages are
 	 *                                                available.
@@ -5217,10 +5319,17 @@
 	 *
 	 * @since 4.9.6
 	 *
-	 * @param array  $response        The personal data for the given exporter and page.
+	 * @param array  $response        {
+	 *     The personal data for the given exporter and page number.
+	 *
+	 *     @type bool     $items_removed  Whether items were actually removed or not.
+	 *     @type bool     $items_retained Whether items were retained or not.
+	 *     @type string[] $messages       An array of messages to add to the personal data export file.
+	 *     @type bool     $done           Whether the eraser is finished or not.
+	 * }
 	 * @param int    $eraser_index    The index of the eraser that provided this data.
 	 * @param string $email_address   The email address associated with this personal data.
-	 * @param int    $page            The page for this response.
+	 * @param int    $page            The page number for this response.
 	 * @param int    $request_id      The privacy request post ID associated with this request.
 	 * @param string $eraser_key      The key (slug) of the eraser that provided this data.
 	 */
@@ -5234,7 +5343,7 @@
 }
 
 /**
- * Ajax handler for site health checks on server communication.
+ * Handles site health checks on server communication via AJAX.
  *
  * @since 5.2.0
  * @deprecated 5.6.0 Use WP_REST_Site_Health_Controller::test_dotorg_communication()
@@ -5267,7 +5376,7 @@
 }
 
 /**
- * Ajax handler for site health checks on background updates.
+ * Handles site health checks on background updates via AJAX.
  *
  * @since 5.2.0
  * @deprecated 5.6.0 Use WP_REST_Site_Health_Controller::test_background_updates()
@@ -5300,7 +5409,7 @@
 }
 
 /**
- * Ajax handler for site health checks on loopback requests.
+ * Handles site health checks on loopback requests via AJAX.
  *
  * @since 5.2.0
  * @deprecated 5.6.0 Use WP_REST_Site_Health_Controller::test_loopback_requests()
@@ -5333,7 +5442,7 @@
 }
 
 /**
- * Ajax handler for site health check to update the result status.
+ * Handles site health check to update the result status via AJAX.
  *
  * @since 5.2.0
  */
@@ -5350,7 +5459,7 @@
 }
 
 /**
- * Ajax handler for site health check to get directories and database sizes.
+ * Handles site health check to get directories and database sizes via AJAX.
  *
  * @since 5.2.0
  * @deprecated 5.6.0 Use WP_REST_Site_Health_Controller::get_directory_sizes()
@@ -5416,7 +5525,7 @@
 }
 
 /**
- * Ajax handler to renew the REST API nonce.
+ * Handles renewing the REST API nonce via AJAX.
  *
  * @since 5.3.0
  */
@@ -5425,7 +5534,7 @@
 }
 
 /**
- * Ajax handler to enable or disable plugin and theme auto-updates.
+ * Handles enabling or disable plugin and theme auto-updates via AJAX.
  *
  * @since 5.5.0
  */
@@ -5495,7 +5604,7 @@
 }
 
 /**
- * Ajax handler sends a password reset link.
+ * Handles sending a password reset link via AJAX.
  *
  * @since 5.7.0
  */