3 * Administration API: Core Ajax handlers
6 * @subpackage Administration
11 // No-privilege Ajax handlers.
15 * Ajax handler for the Heartbeat API in
16 * the no-privilege context.
18 * Runs when the user is not logged in.
22 function wp_ajax_nopriv_heartbeat() {
25 // screen_id is the same as $current_screen->id and the JS global 'pagenow'.
26 if ( ! empty($_POST['screen_id']) )
27 $screen_id = sanitize_key($_POST['screen_id']);
31 if ( ! empty($_POST['data']) ) {
32 $data = wp_unslash( (array) $_POST['data'] );
35 * Filter Heartbeat AJAX response in no-privilege environments.
39 * @param array|object $response The no-priv Heartbeat response object or array.
40 * @param array $data An array of data passed via $_POST.
41 * @param string $screen_id The screen id.
43 $response = apply_filters( 'heartbeat_nopriv_received', $response, $data, $screen_id );
47 * Filter Heartbeat AJAX response when no data is passed.
51 * @param array|object $response The Heartbeat response object or array.
52 * @param string $screen_id The screen id.
54 $response = apply_filters( 'heartbeat_nopriv_send', $response, $screen_id );
57 * Fires when Heartbeat ticks in no-privilege environments.
59 * Allows the transport to be easily replaced with long-polling.
63 * @param array|object $response The no-priv Heartbeat response.
64 * @param string $screen_id The screen id.
66 do_action( 'heartbeat_nopriv_tick', $response, $screen_id );
68 // Send the current time according to the server.
69 $response['server_time'] = time();
71 wp_send_json($response);
75 // GET-based Ajax handlers.
79 * Ajax handler for fetching a list table.
83 * @global WP_List_Table $wp_list_table
85 function wp_ajax_fetch_list() {
86 global $wp_list_table;
88 $list_class = $_GET['list_args']['class'];
89 check_ajax_referer( "fetch-list-$list_class", '_ajax_fetch_list_nonce' );
91 $wp_list_table = _get_list_table( $list_class, array( 'screen' => $_GET['list_args']['screen']['id'] ) );
92 if ( ! $wp_list_table )
95 if ( ! $wp_list_table->ajax_user_can() )
98 $wp_list_table->ajax_response();
104 * Ajax handler for tag search.
108 function wp_ajax_ajax_tag_search() {
109 if ( ! isset( $_GET['tax'] ) ) {
113 $taxonomy = sanitize_key( $_GET['tax'] );
114 $tax = get_taxonomy( $taxonomy );
119 if ( ! current_user_can( $tax->cap->assign_terms ) ) {
123 $s = wp_unslash( $_GET['q'] );
125 $comma = _x( ',', 'tag delimiter' );
126 if ( ',' !== $comma )
127 $s = str_replace( $comma, ',', $s );
128 if ( false !== strpos( $s, ',' ) ) {
129 $s = explode( ',', $s );
130 $s = $s[count( $s ) - 1];
135 * Filter the minimum number of characters required to fire a tag search via AJAX.
139 * @param int $characters The minimum number of characters required. Default 2.
140 * @param object $tax The taxonomy object.
141 * @param string $s The search term.
143 $term_search_min_chars = (int) apply_filters( 'term_search_min_chars', 2, $tax, $s );
146 * Require $term_search_min_chars chars for matching (default: 2)
147 * ensure it's a non-negative, non-zero integer.
149 if ( ( $term_search_min_chars == 0 ) || ( strlen( $s ) < $term_search_min_chars ) ){
153 $results = get_terms( $taxonomy, array( 'name__like' => $s, 'fields' => 'names', 'hide_empty' => false ) );
155 echo join( $results, "\n" );
160 * Ajax handler for compression testing.
164 function wp_ajax_wp_compression_test() {
165 if ( !current_user_can( 'manage_options' ) )
168 if ( ini_get('zlib.output_compression') || 'ob_gzhandler' == ini_get('output_handler') ) {
169 update_site_option('can_compress_scripts', 0);
173 if ( isset($_GET['test']) ) {
174 header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' );
175 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
176 header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
177 header( 'Pragma: no-cache' );
178 header('Content-Type: application/javascript; charset=UTF-8');
179 $force_gzip = ( defined('ENFORCE_GZIP') && ENFORCE_GZIP );
180 $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."';
182 if ( 1 == $_GET['test'] ) {
185 } elseif ( 2 == $_GET['test'] ) {
186 if ( !isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
188 if ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') && function_exists('gzdeflate') && ! $force_gzip ) {
189 header('Content-Encoding: deflate');
190 $out = gzdeflate( $test_str, 1 );
191 } elseif ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') && function_exists('gzencode') ) {
192 header('Content-Encoding: gzip');
193 $out = gzencode( $test_str, 1 );
199 } elseif ( 'no' == $_GET['test'] ) {
200 check_ajax_referer( 'update_can_compress_scripts' );
201 update_site_option('can_compress_scripts', 0);
202 } elseif ( 'yes' == $_GET['test'] ) {
203 check_ajax_referer( 'update_can_compress_scripts' );
204 update_site_option('can_compress_scripts', 1);
212 * Ajax handler for image editor previews.
216 function wp_ajax_imgedit_preview() {
217 $post_id = intval($_GET['postid']);
218 if ( empty($post_id) || !current_user_can('edit_post', $post_id) )
221 check_ajax_referer( "image_editor-$post_id" );
223 include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
224 if ( ! stream_preview_image($post_id) )
231 * Ajax handler for oEmbed caching.
235 * @global WP_Embed $wp_embed
237 function wp_ajax_oembed_cache() {
238 $GLOBALS['wp_embed']->cache_oembed( $_GET['post'] );
243 * Ajax handler for user autocomplete.
247 function wp_ajax_autocomplete_user() {
248 if ( ! is_multisite() || ! current_user_can( 'promote_users' ) || wp_is_large_network( 'users' ) )
251 /** This filter is documented in wp-admin/user-new.php */
252 if ( ! is_super_admin() && ! apply_filters( 'autocomplete_users_for_site_admins', false ) )
257 // Check the type of request
258 // Current allowed values are `add` and `search`
259 if ( isset( $_REQUEST['autocomplete_type'] ) && 'search' === $_REQUEST['autocomplete_type'] ) {
260 $type = $_REQUEST['autocomplete_type'];
265 // Check the desired field for value
266 // Current allowed values are `user_email` and `user_login`
267 if ( isset( $_REQUEST['autocomplete_field'] ) && 'user_email' === $_REQUEST['autocomplete_field'] ) {
268 $field = $_REQUEST['autocomplete_field'];
270 $field = 'user_login';
273 // Exclude current users of this blog
274 if ( isset( $_REQUEST['site_id'] ) ) {
275 $id = absint( $_REQUEST['site_id'] );
277 $id = get_current_blog_id();
280 $include_blog_users = ( $type == 'search' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
281 $exclude_blog_users = ( $type == 'add' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
283 $users = get_users( array(
285 'search' => '*' . $_REQUEST['term'] . '*',
286 'include' => $include_blog_users,
287 'exclude' => $exclude_blog_users,
288 'search_columns' => array( 'user_login', 'user_nicename', 'user_email' ),
291 foreach ( $users as $user ) {
293 /* translators: 1: user_login, 2: user_email */
294 'label' => sprintf( _x( '%1$s (%2$s)', 'user autocomplete result' ), $user->user_login, $user->user_email ),
295 'value' => $user->$field,
299 wp_die( wp_json_encode( $return ) );
303 * Ajax handler for dashboard widgets.
307 function wp_ajax_dashboard_widgets() {
308 require_once ABSPATH . 'wp-admin/includes/dashboard.php';
310 $pagenow = $_GET['pagenow'];
311 if ( $pagenow === 'dashboard-user' || $pagenow === 'dashboard-network' || $pagenow === 'dashboard' ) {
312 set_current_screen( $pagenow );
315 switch ( $_GET['widget'] ) {
316 case 'dashboard_primary' :
317 wp_dashboard_primary();
324 * Ajax handler for Customizer preview logged-in status.
328 function wp_ajax_logged_in() {
337 * Sends back current comment total and new page links if they need to be updated.
339 * Contrary to normal success AJAX response ("1"), die with time() on success.
344 * @param int $comment_id
347 function _wp_ajax_delete_comment_response( $comment_id, $delta = -1 ) {
348 $total = isset( $_POST['_total'] ) ? (int) $_POST['_total'] : 0;
349 $per_page = isset( $_POST['_per_page'] ) ? (int) $_POST['_per_page'] : 0;
350 $page = isset( $_POST['_page'] ) ? (int) $_POST['_page'] : 0;
351 $url = isset( $_POST['_url'] ) ? esc_url_raw( $_POST['_url'] ) : '';
353 // JS didn't send us everything we need to know. Just die with success message
354 if ( ! $total || ! $per_page || ! $page || ! $url ) {
356 $comment = get_comment( $comment_id );
358 $counts = wp_count_comments();
360 $x = new WP_Ajax_Response( array(
362 // Here for completeness - not used.
364 'supplemental' => array(
365 'status' => $comment ? $comment->comment_approved : '',
366 'postId' => $comment ? $comment->comment_post_ID : '',
368 'in_moderation' => $counts->moderated,
369 'i18n_comments_text' => sprintf(
370 _n( '%s Comment', '%s Comments', $counts->approved ),
371 number_format_i18n( $counts->approved )
373 'i18n_moderation_text' => sprintf(
374 _nx( '%s in moderation', '%s in moderation', $counts->moderated, 'comments' ),
375 number_format_i18n( $counts->moderated )
386 // Only do the expensive stuff on a page-break, and about 1 other time per page
387 if ( 0 == $total % $per_page || 1 == mt_rand( 1, $per_page ) ) {
389 // What type of comment count are we looking for?
391 $parsed = parse_url( $url );
392 if ( isset( $parsed['query'] ) ) {
393 parse_str( $parsed['query'], $query_vars );
394 if ( !empty( $query_vars['comment_status'] ) )
395 $status = $query_vars['comment_status'];
396 if ( !empty( $query_vars['p'] ) )
397 $post_id = (int) $query_vars['p'];
400 $comment_count = wp_count_comments($post_id);
402 // We're looking for a known type of comment count.
403 if ( isset( $comment_count->$status ) )
404 $total = $comment_count->$status;
405 // Else use the decremented value from above.
408 // The time since the last comment count.
410 $comment = get_comment( $comment_id );
412 $x = new WP_Ajax_Response( array(
414 // Here for completeness - not used.
416 'supplemental' => array(
417 'status' => $comment ? $comment->comment_approved : '',
418 'postId' => $comment ? $comment->comment_post_ID : '',
419 'total_items_i18n' => sprintf( _n( '%s item', '%s items', $total ), number_format_i18n( $total ) ),
420 'total_pages' => ceil( $total / $per_page ),
421 'total_pages_i18n' => number_format_i18n( ceil( $total / $per_page ) ),
430 // POST-based Ajax handlers.
434 * Ajax handler for adding a hierarchical term.
439 function _wp_ajax_add_hierarchical_term() {
440 $action = $_POST['action'];
441 $taxonomy = get_taxonomy(substr($action, 4));
442 check_ajax_referer( $action, '_ajax_nonce-add-' . $taxonomy->name );
443 if ( !current_user_can( $taxonomy->cap->edit_terms ) )
445 $names = explode(',', $_POST['new'.$taxonomy->name]);
446 $parent = isset($_POST['new'.$taxonomy->name.'_parent']) ? (int) $_POST['new'.$taxonomy->name.'_parent'] : 0;
449 if ( $taxonomy->name == 'category' )
450 $post_category = isset($_POST['post_category']) ? (array) $_POST['post_category'] : array();
452 $post_category = ( isset($_POST['tax_input']) && isset($_POST['tax_input'][$taxonomy->name]) ) ? (array) $_POST['tax_input'][$taxonomy->name] : array();
453 $checked_categories = array_map( 'absint', (array) $post_category );
454 $popular_ids = wp_popular_terms_checklist($taxonomy->name, 0, 10, false);
456 foreach ( $names as $cat_name ) {
457 $cat_name = trim($cat_name);
458 $category_nicename = sanitize_title($cat_name);
459 if ( '' === $category_nicename )
461 if ( !$cat_id = term_exists( $cat_name, $taxonomy->name, $parent ) )
462 $cat_id = wp_insert_term( $cat_name, $taxonomy->name, array( 'parent' => $parent ) );
463 if ( is_wp_error( $cat_id ) ) {
465 } elseif ( is_array( $cat_id ) ) {
466 $cat_id = $cat_id['term_id'];
468 $checked_categories[] = $cat_id;
469 if ( $parent ) // Do these all at once in a second
474 wp_terms_checklist( 0, array( 'taxonomy' => $taxonomy->name, 'descendants_and_self' => $cat_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids ));
476 $data = ob_get_clean();
479 'what' => $taxonomy->name,
481 'data' => str_replace( array("\n", "\t"), '', $data),
486 if ( $parent ) { // Foncy - replace the parent and all its children
487 $parent = get_term( $parent, $taxonomy->name );
488 $term_id = $parent->term_id;
490 while ( $parent->parent ) { // get the top parent
491 $parent = get_term( $parent->parent, $taxonomy->name );
492 if ( is_wp_error( $parent ) )
494 $term_id = $parent->term_id;
499 wp_terms_checklist( 0, array('taxonomy' => $taxonomy->name, 'descendants_and_self' => $term_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids));
501 $data = ob_get_clean();
504 'what' => $taxonomy->name,
506 'data' => str_replace( array("\n", "\t"), '', $data),
513 wp_dropdown_categories( array(
514 'taxonomy' => $taxonomy->name, 'hide_empty' => 0, 'name' => 'new'.$taxonomy->name.'_parent', 'orderby' => 'name',
515 'hierarchical' => 1, 'show_option_none' => '— '.$taxonomy->labels->parent_item.' —'
518 $sup = ob_get_clean();
520 $add['supplemental'] = array( 'newcat_parent' => $sup );
522 $x = new WP_Ajax_Response( $add );
527 * Ajax handler for deleting a comment.
531 function wp_ajax_delete_comment() {
532 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
534 if ( !$comment = get_comment( $id ) )
536 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
539 check_ajax_referer( "delete-comment_$id" );
540 $status = wp_get_comment_status( $comment );
543 if ( isset($_POST['trash']) && 1 == $_POST['trash'] ) {
544 if ( 'trash' == $status )
546 $r = wp_trash_comment( $comment );
547 } elseif ( isset($_POST['untrash']) && 1 == $_POST['untrash'] ) {
548 if ( 'trash' != $status )
550 $r = wp_untrash_comment( $comment );
551 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'trash' ) // undo trash, not in trash
553 } elseif ( isset($_POST['spam']) && 1 == $_POST['spam'] ) {
554 if ( 'spam' == $status )
556 $r = wp_spam_comment( $comment );
557 } elseif ( isset($_POST['unspam']) && 1 == $_POST['unspam'] ) {
558 if ( 'spam' != $status )
560 $r = wp_unspam_comment( $comment );
561 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'spam' ) // undo spam, not in spam
563 } elseif ( isset($_POST['delete']) && 1 == $_POST['delete'] ) {
564 $r = wp_delete_comment( $comment );
569 if ( $r ) // Decide if we need to send back '1' or a more complicated response including page links and comment counts
570 _wp_ajax_delete_comment_response( $comment->comment_ID, $delta );
575 * Ajax handler for deleting a tag.
579 function wp_ajax_delete_tag() {
580 $tag_id = (int) $_POST['tag_ID'];
581 check_ajax_referer( "delete-tag_$tag_id" );
583 $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
584 $tax = get_taxonomy($taxonomy);
586 if ( !current_user_can( $tax->cap->delete_terms ) )
589 $tag = get_term( $tag_id, $taxonomy );
590 if ( !$tag || is_wp_error( $tag ) )
593 if ( wp_delete_term($tag_id, $taxonomy))
600 * Ajax handler for deleting a link.
604 function wp_ajax_delete_link() {
605 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
607 check_ajax_referer( "delete-bookmark_$id" );
608 if ( !current_user_can( 'manage_links' ) )
611 $link = get_bookmark( $id );
612 if ( !$link || is_wp_error( $link ) )
615 if ( wp_delete_link( $id ) )
622 * Ajax handler for deleting meta.
626 function wp_ajax_delete_meta() {
627 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
629 check_ajax_referer( "delete-meta_$id" );
630 if ( !$meta = get_metadata_by_mid( 'post', $id ) )
633 if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta', $meta->post_id, $meta->meta_key ) )
635 if ( delete_meta( $meta->meta_id ) )
641 * Ajax handler for deleting a post.
645 * @param string $action Action to perform.
647 function wp_ajax_delete_post( $action ) {
648 if ( empty( $action ) )
649 $action = 'delete-post';
650 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
652 check_ajax_referer( "{$action}_$id" );
653 if ( !current_user_can( 'delete_post', $id ) )
656 if ( !get_post( $id ) )
659 if ( wp_delete_post( $id ) )
666 * Ajax handler for sending a post to the trash.
670 * @param string $action Action to perform.
672 function wp_ajax_trash_post( $action ) {
673 if ( empty( $action ) )
674 $action = 'trash-post';
675 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
677 check_ajax_referer( "{$action}_$id" );
678 if ( !current_user_can( 'delete_post', $id ) )
681 if ( !get_post( $id ) )
684 if ( 'trash-post' == $action )
685 $done = wp_trash_post( $id );
687 $done = wp_untrash_post( $id );
696 * Ajax handler to restore a post from the trash.
700 * @param string $action Action to perform.
702 function wp_ajax_untrash_post( $action ) {
703 if ( empty( $action ) )
704 $action = 'untrash-post';
705 wp_ajax_trash_post( $action );
711 * @param string $action
713 function wp_ajax_delete_page( $action ) {
714 if ( empty( $action ) )
715 $action = 'delete-page';
716 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
718 check_ajax_referer( "{$action}_$id" );
719 if ( !current_user_can( 'delete_page', $id ) )
722 if ( ! get_post( $id ) )
725 if ( wp_delete_post( $id ) )
732 * Ajax handler to dim a comment.
736 function wp_ajax_dim_comment() {
737 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
739 if ( !$comment = get_comment( $id ) ) {
740 $x = new WP_Ajax_Response( array(
742 'id' => new WP_Error('invalid_comment', sprintf(__('Comment %d does not exist'), $id))
747 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) && ! current_user_can( 'moderate_comments' ) )
750 $current = wp_get_comment_status( $comment );
751 if ( isset( $_POST['new'] ) && $_POST['new'] == $current )
754 check_ajax_referer( "approve-comment_$id" );
755 if ( in_array( $current, array( 'unapproved', 'spam' ) ) ) {
756 $result = wp_set_comment_status( $comment, 'approve', true );
758 $result = wp_set_comment_status( $comment, 'hold', true );
761 if ( is_wp_error($result) ) {
762 $x = new WP_Ajax_Response( array(
769 // Decide if we need to send back '1' or a more complicated response including page links and comment counts
770 _wp_ajax_delete_comment_response( $comment->comment_ID );
775 * Ajax handler for deleting a link category.
779 * @param string $action Action to perform.
781 function wp_ajax_add_link_category( $action ) {
782 if ( empty( $action ) )
783 $action = 'add-link-category';
784 check_ajax_referer( $action );
785 if ( !current_user_can( 'manage_categories' ) )
787 $names = explode(',', wp_unslash( $_POST['newcat'] ) );
788 $x = new WP_Ajax_Response();
789 foreach ( $names as $cat_name ) {
790 $cat_name = trim($cat_name);
791 $slug = sanitize_title($cat_name);
794 if ( !$cat_id = term_exists( $cat_name, 'link_category' ) )
795 $cat_id = wp_insert_term( $cat_name, 'link_category' );
796 if ( is_wp_error( $cat_id ) ) {
798 } elseif ( is_array( $cat_id ) ) {
799 $cat_id = $cat_id['term_id'];
801 $cat_name = esc_html( $cat_name );
803 'what' => 'link-category',
805 'data' => "<li id='link-category-$cat_id'><label for='in-link-category-$cat_id' class='selectit'><input value='" . esc_attr($cat_id) . "' type='checkbox' checked='checked' name='link_category[]' id='in-link-category-$cat_id'/> $cat_name</label></li>",
813 * Ajax handler to add a tag.
817 * @global WP_List_Table $wp_list_table
819 function wp_ajax_add_tag() {
820 global $wp_list_table;
822 check_ajax_referer( 'add-tag', '_wpnonce_add-tag' );
823 $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
824 $tax = get_taxonomy($taxonomy);
826 if ( !current_user_can( $tax->cap->edit_terms ) )
829 $x = new WP_Ajax_Response();
831 $tag = wp_insert_term($_POST['tag-name'], $taxonomy, $_POST );
833 if ( !$tag || is_wp_error($tag) || (!$tag = get_term( $tag['term_id'], $taxonomy )) ) {
834 $message = __('An error has occurred. Please reload the page and try again.');
835 if ( is_wp_error($tag) && $tag->get_error_message() )
836 $message = $tag->get_error_message();
839 'what' => 'taxonomy',
840 'data' => new WP_Error('error', $message )
845 $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => $_POST['screen'] ) );
848 if ( is_taxonomy_hierarchical($taxonomy) ) {
849 $level = count( get_ancestors( $tag->term_id, $taxonomy, 'taxonomy' ) );
851 $wp_list_table->single_row( $tag, $level );
852 $noparents = ob_get_clean();
856 $wp_list_table->single_row( $tag );
857 $parents = ob_get_clean();
860 'what' => 'taxonomy',
861 'supplemental' => compact('parents', 'noparents')
865 'position' => $level,
866 'supplemental' => (array) $tag
872 * Ajax handler for getting a tagcloud.
876 function wp_ajax_get_tagcloud() {
877 if ( ! isset( $_POST['tax'] ) ) {
881 $taxonomy = sanitize_key( $_POST['tax'] );
882 $tax = get_taxonomy( $taxonomy );
887 if ( ! current_user_can( $tax->cap->assign_terms ) ) {
891 $tags = get_terms( $taxonomy, array( 'number' => 45, 'orderby' => 'count', 'order' => 'DESC' ) );
893 if ( empty( $tags ) )
894 wp_die( $tax->labels->not_found );
896 if ( is_wp_error( $tags ) )
897 wp_die( $tags->get_error_message() );
899 foreach ( $tags as $key => $tag ) {
900 $tags[ $key ]->link = '#';
901 $tags[ $key ]->id = $tag->term_id;
904 // We need raw tag names here, so don't filter the output
905 $return = wp_generate_tag_cloud( $tags, array('filter' => 0) );
907 if ( empty($return) )
916 * Ajax handler for getting comments.
920 * @global WP_List_Table $wp_list_table
921 * @global int $post_id
923 * @param string $action Action to perform.
925 function wp_ajax_get_comments( $action ) {
926 global $wp_list_table, $post_id;
927 if ( empty( $action ) )
928 $action = 'get-comments';
930 check_ajax_referer( $action );
932 if ( empty( $post_id ) && ! empty( $_REQUEST['p'] ) ) {
933 $id = absint( $_REQUEST['p'] );
934 if ( ! empty( $id ) )
938 if ( empty( $post_id ) )
941 $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
943 if ( ! current_user_can( 'edit_post', $post_id ) )
946 $wp_list_table->prepare_items();
948 if ( !$wp_list_table->has_items() )
951 $x = new WP_Ajax_Response();
953 foreach ( $wp_list_table->items as $comment ) {
954 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
956 get_comment( $comment );
957 $wp_list_table->single_row( $comment );
959 $comment_list_item = ob_get_clean();
962 'what' => 'comments',
963 'data' => $comment_list_item
969 * Ajax handler for replying to a comment.
973 * @global WP_List_Table $wp_list_table
975 * @param string $action Action to perform.
977 function wp_ajax_replyto_comment( $action ) {
978 global $wp_list_table;
979 if ( empty( $action ) )
980 $action = 'replyto-comment';
982 check_ajax_referer( $action, '_ajax_nonce-replyto-comment' );
984 $comment_post_ID = (int) $_POST['comment_post_ID'];
985 $post = get_post( $comment_post_ID );
989 if ( !current_user_can( 'edit_post', $comment_post_ID ) )
992 if ( empty( $post->post_status ) )
994 elseif ( in_array($post->post_status, array('draft', 'pending', 'trash') ) )
995 wp_die( __('ERROR: you are replying to a comment on a draft post.') );
997 $user = wp_get_current_user();
998 if ( $user->exists() ) {
999 $user_ID = $user->ID;
1000 $comment_author = wp_slash( $user->display_name );
1001 $comment_author_email = wp_slash( $user->user_email );
1002 $comment_author_url = wp_slash( $user->user_url );
1003 $comment_content = trim( $_POST['content'] );
1004 $comment_type = isset( $_POST['comment_type'] ) ? trim( $_POST['comment_type'] ) : '';
1005 if ( current_user_can( 'unfiltered_html' ) ) {
1006 if ( ! isset( $_POST['_wp_unfiltered_html_comment'] ) )
1007 $_POST['_wp_unfiltered_html_comment'] = '';
1009 if ( wp_create_nonce( 'unfiltered-html-comment' ) != $_POST['_wp_unfiltered_html_comment'] ) {
1010 kses_remove_filters(); // start with a clean slate
1011 kses_init_filters(); // set up the filters
1015 wp_die( __( 'Sorry, you must be logged in to reply to a comment.' ) );
1018 if ( '' == $comment_content )
1019 wp_die( __( 'ERROR: please type a comment.' ) );
1021 $comment_parent = 0;
1022 if ( isset( $_POST['comment_ID'] ) )
1023 $comment_parent = absint( $_POST['comment_ID'] );
1024 $comment_auto_approved = false;
1025 $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID');
1027 // Automatically approve parent comment.
1028 if ( !empty($_POST['approve_parent']) ) {
1029 $parent = get_comment( $comment_parent );
1031 if ( $parent && $parent->comment_approved === '0' && $parent->comment_post_ID == $comment_post_ID ) {
1032 if ( ! current_user_can( 'edit_comment', $parent->comment_ID ) ) {
1036 if ( wp_set_comment_status( $parent, 'approve' ) )
1037 $comment_auto_approved = true;
1041 $comment_id = wp_new_comment( $commentdata );
1042 $comment = get_comment($comment_id);
1043 if ( ! $comment ) wp_die( 1 );
1045 $position = ( isset($_POST['position']) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1';
1048 if ( isset( $_REQUEST['mode'] ) && 'dashboard' == $_REQUEST['mode'] ) {
1049 require_once( ABSPATH . 'wp-admin/includes/dashboard.php' );
1050 _wp_dashboard_recent_comments_row( $comment );
1052 if ( isset( $_REQUEST['mode'] ) && 'single' == $_REQUEST['mode'] ) {
1053 $wp_list_table = _get_list_table('WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
1055 $wp_list_table = _get_list_table('WP_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
1057 $wp_list_table->single_row( $comment );
1059 $comment_list_item = ob_get_clean();
1062 'what' => 'comment',
1063 'id' => $comment->comment_ID,
1064 'data' => $comment_list_item,
1065 'position' => $position
1068 $counts = wp_count_comments();
1069 $response['supplemental'] = array(
1070 'in_moderation' => $counts->moderated,
1071 'i18n_comments_text' => sprintf(
1072 _n( '%s Comment', '%s Comments', $counts->approved ),
1073 number_format_i18n( $counts->approved )
1075 'i18n_moderation_text' => sprintf(
1076 _nx( '%s in moderation', '%s in moderation', $counts->moderated, 'comments' ),
1077 number_format_i18n( $counts->moderated )
1081 if ( $comment_auto_approved ) {
1082 $response['supplemental']['parent_approved'] = $parent->comment_ID;
1083 $response['supplemental']['parent_post_id'] = $parent->comment_post_ID;
1086 $x = new WP_Ajax_Response();
1087 $x->add( $response );
1092 * Ajax handler for editing a comment.
1096 * @global WP_List_Table $wp_list_table
1098 function wp_ajax_edit_comment() {
1099 global $wp_list_table;
1101 check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' );
1103 $comment_id = (int) $_POST['comment_ID'];
1104 if ( ! current_user_can( 'edit_comment', $comment_id ) )
1107 if ( '' == $_POST['content'] )
1108 wp_die( __( 'ERROR: please type a comment.' ) );
1110 if ( isset( $_POST['status'] ) )
1111 $_POST['comment_status'] = $_POST['status'];
1114 $position = ( isset($_POST['position']) && (int) $_POST['position']) ? (int) $_POST['position'] : '-1';
1115 $checkbox = ( isset($_POST['checkbox']) && true == $_POST['checkbox'] ) ? 1 : 0;
1116 $wp_list_table = _get_list_table( $checkbox ? 'WP_Comments_List_Table' : 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
1118 $comment = get_comment( $comment_id );
1119 if ( empty( $comment->comment_ID ) )
1123 $wp_list_table->single_row( $comment );
1124 $comment_list_item = ob_get_clean();
1126 $x = new WP_Ajax_Response();
1129 'what' => 'edit_comment',
1130 'id' => $comment->comment_ID,
1131 'data' => $comment_list_item,
1132 'position' => $position
1139 * Ajax handler for adding a menu item.
1143 function wp_ajax_add_menu_item() {
1144 check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1146 if ( ! current_user_can( 'edit_theme_options' ) )
1149 require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1151 // For performance reasons, we omit some object properties from the checklist.
1152 // The following is a hacky way to restore them when adding non-custom items.
1154 $menu_items_data = array();
1155 foreach ( (array) $_POST['menu-item'] as $menu_item_data ) {
1157 ! empty( $menu_item_data['menu-item-type'] ) &&
1158 'custom' != $menu_item_data['menu-item-type'] &&
1159 ! empty( $menu_item_data['menu-item-object-id'] )
1161 switch( $menu_item_data['menu-item-type'] ) {
1163 $_object = get_post( $menu_item_data['menu-item-object-id'] );
1166 case 'post_type_archive' :
1167 $_object = get_post_type_object( $menu_item_data['menu-item-object'] );
1171 $_object = get_term( $menu_item_data['menu-item-object-id'], $menu_item_data['menu-item-object'] );
1175 $_menu_items = array_map( 'wp_setup_nav_menu_item', array( $_object ) );
1176 $_menu_item = reset( $_menu_items );
1178 // Restore the missing menu item properties
1179 $menu_item_data['menu-item-description'] = $_menu_item->description;
1182 $menu_items_data[] = $menu_item_data;
1185 $item_ids = wp_save_nav_menu_items( 0, $menu_items_data );
1186 if ( is_wp_error( $item_ids ) )
1189 $menu_items = array();
1191 foreach ( (array) $item_ids as $menu_item_id ) {
1192 $menu_obj = get_post( $menu_item_id );
1193 if ( ! empty( $menu_obj->ID ) ) {
1194 $menu_obj = wp_setup_nav_menu_item( $menu_obj );
1195 $menu_obj->label = $menu_obj->title; // don't show "(pending)" in ajax-added items
1196 $menu_items[] = $menu_obj;
1200 /** This filter is documented in wp-admin/includes/nav-menu.php */
1201 $walker_class_name = apply_filters( 'wp_edit_nav_menu_walker', 'Walker_Nav_Menu_Edit', $_POST['menu'] );
1203 if ( ! class_exists( $walker_class_name ) )
1206 if ( ! empty( $menu_items ) ) {
1211 'link_before' => '',
1212 'walker' => new $walker_class_name,
1214 echo walk_nav_menu_tree( $menu_items, 0, (object) $args );
1220 * Ajax handler for adding meta.
1224 function wp_ajax_add_meta() {
1225 check_ajax_referer( 'add-meta', '_ajax_nonce-add-meta' );
1227 $pid = (int) $_POST['post_id'];
1228 $post = get_post( $pid );
1230 if ( isset($_POST['metakeyselect']) || isset($_POST['metakeyinput']) ) {
1231 if ( !current_user_can( 'edit_post', $pid ) )
1233 if ( isset($_POST['metakeyselect']) && '#NONE#' == $_POST['metakeyselect'] && empty($_POST['metakeyinput']) )
1236 // If the post is an autodraft, save the post as a draft and then attempt to save the meta.
1237 if ( $post->post_status == 'auto-draft' ) {
1238 $post_data = array();
1239 $post_data['action'] = 'draft'; // Warning fix
1240 $post_data['post_ID'] = $pid;
1241 $post_data['post_type'] = $post->post_type;
1242 $post_data['post_status'] = 'draft';
1243 $now = current_time('timestamp', 1);
1244 $post_data['post_title'] = sprintf( __( 'Draft created on %1$s at %2$s' ), date( __( 'F j, Y' ), $now ), date( __( 'g:i a' ), $now ) );
1246 $pid = edit_post( $post_data );
1248 if ( is_wp_error( $pid ) ) {
1249 $x = new WP_Ajax_Response( array(
1256 if ( !$mid = add_meta( $pid ) )
1257 wp_die( __( 'Please provide a custom field value.' ) );
1261 } elseif ( ! $mid = add_meta( $pid ) ) {
1262 wp_die( __( 'Please provide a custom field value.' ) );
1265 $meta = get_metadata_by_mid( 'post', $mid );
1266 $pid = (int) $meta->post_id;
1267 $meta = get_object_vars( $meta );
1268 $x = new WP_Ajax_Response( array(
1271 'data' => _list_meta_row( $meta, $c ),
1273 'supplemental' => array('postid' => $pid)
1276 $mid = (int) key( $_POST['meta'] );
1277 $key = wp_unslash( $_POST['meta'][$mid]['key'] );
1278 $value = wp_unslash( $_POST['meta'][$mid]['value'] );
1279 if ( '' == trim($key) )
1280 wp_die( __( 'Please provide a custom field name.' ) );
1281 if ( '' == trim($value) )
1282 wp_die( __( 'Please provide a custom field value.' ) );
1283 if ( ! $meta = get_metadata_by_mid( 'post', $mid ) )
1284 wp_die( 0 ); // if meta doesn't exist
1285 if ( is_protected_meta( $meta->meta_key, 'post' ) || is_protected_meta( $key, 'post' ) ||
1286 ! current_user_can( 'edit_post_meta', $meta->post_id, $meta->meta_key ) ||
1287 ! current_user_can( 'edit_post_meta', $meta->post_id, $key ) )
1289 if ( $meta->meta_value != $value || $meta->meta_key != $key ) {
1290 if ( !$u = update_metadata_by_mid( 'post', $mid, $value, $key ) )
1291 wp_die( 0 ); // We know meta exists; we also know it's unchanged (or DB error, in which case there are bigger problems).
1294 $x = new WP_Ajax_Response( array(
1296 'id' => $mid, 'old_id' => $mid,
1297 'data' => _list_meta_row( array(
1299 'meta_value' => $value,
1303 'supplemental' => array('postid' => $meta->post_id)
1310 * Ajax handler for adding a user.
1314 * @global WP_List_Table $wp_list_table
1316 * @param string $action Action to perform.
1318 function wp_ajax_add_user( $action ) {
1319 global $wp_list_table;
1320 if ( empty( $action ) )
1321 $action = 'add-user';
1323 check_ajax_referer( $action );
1324 if ( ! current_user_can('create_users') )
1326 if ( ! $user_id = edit_user() ) {
1328 } elseif ( is_wp_error( $user_id ) ) {
1329 $x = new WP_Ajax_Response( array(
1335 $user_object = get_userdata( $user_id );
1337 $wp_list_table = _get_list_table('WP_Users_List_Table');
1339 $role = current( $user_object->roles );
1341 $x = new WP_Ajax_Response( array(
1344 'data' => $wp_list_table->single_row( $user_object, '', $role ),
1345 'supplemental' => array(
1346 'show-link' => sprintf(
1347 /* translators: %s: the new user */
1348 __( 'User %s added' ),
1349 '<a href="#user-' . $user_id . '">' . $user_object->user_login . '</a>'
1358 * Ajax handler for closed post boxes.
1362 function wp_ajax_closed_postboxes() {
1363 check_ajax_referer( 'closedpostboxes', 'closedpostboxesnonce' );
1364 $closed = isset( $_POST['closed'] ) ? explode( ',', $_POST['closed']) : array();
1365 $closed = array_filter($closed);
1367 $hidden = isset( $_POST['hidden'] ) ? explode( ',', $_POST['hidden']) : array();
1368 $hidden = array_filter($hidden);
1370 $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1372 if ( $page != sanitize_key( $page ) )
1375 if ( ! $user = wp_get_current_user() )
1378 if ( is_array($closed) )
1379 update_user_option($user->ID, "closedpostboxes_$page", $closed, true);
1381 if ( is_array($hidden) ) {
1382 $hidden = array_diff( $hidden, array('submitdiv', 'linksubmitdiv', 'manage-menu', 'create-menu') ); // postboxes that are always shown
1383 update_user_option($user->ID, "metaboxhidden_$page", $hidden, true);
1390 * Ajax handler for hidden columns.
1394 function wp_ajax_hidden_columns() {
1395 check_ajax_referer( 'screen-options-nonce', 'screenoptionnonce' );
1396 $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1398 if ( $page != sanitize_key( $page ) )
1401 if ( ! $user = wp_get_current_user() )
1404 $hidden = ! empty( $_POST['hidden'] ) ? explode( ',', $_POST['hidden'] ) : array();
1405 update_user_option( $user->ID, "manage{$page}columnshidden", $hidden, true );
1411 * Ajax handler for updating whether to display the welcome panel.
1415 function wp_ajax_update_welcome_panel() {
1416 check_ajax_referer( 'welcome-panel-nonce', 'welcomepanelnonce' );
1418 if ( ! current_user_can( 'edit_theme_options' ) )
1421 update_user_meta( get_current_user_id(), 'show_welcome_panel', empty( $_POST['visible'] ) ? 0 : 1 );
1427 * Ajax handler for retrieving menu meta boxes.
1431 function wp_ajax_menu_get_metabox() {
1432 if ( ! current_user_can( 'edit_theme_options' ) )
1435 require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1437 if ( isset( $_POST['item-type'] ) && 'post_type' == $_POST['item-type'] ) {
1439 $callback = 'wp_nav_menu_item_post_type_meta_box';
1440 $items = (array) get_post_types( array( 'show_in_nav_menus' => true ), 'object' );
1441 } elseif ( isset( $_POST['item-type'] ) && 'taxonomy' == $_POST['item-type'] ) {
1443 $callback = 'wp_nav_menu_item_taxonomy_meta_box';
1444 $items = (array) get_taxonomies( array( 'show_ui' => true ), 'object' );
1447 if ( ! empty( $_POST['item-object'] ) && isset( $items[$_POST['item-object']] ) ) {
1448 $menus_meta_box_object = $items[ $_POST['item-object'] ];
1450 /** This filter is documented in wp-admin/includes/nav-menu.php */
1451 $item = apply_filters( 'nav_menu_meta_box_object', $menus_meta_box_object );
1453 call_user_func_array($callback, array(
1456 'id' => 'add-' . $item->name,
1457 'title' => $item->labels->name,
1458 'callback' => $callback,
1463 $markup = ob_get_clean();
1465 echo wp_json_encode(array(
1466 'replace-id' => $type . '-' . $item->name,
1467 'markup' => $markup,
1475 * Ajax handler for internal linking.
1479 function wp_ajax_wp_link_ajax() {
1480 check_ajax_referer( 'internal-linking', '_ajax_linking_nonce' );
1484 if ( isset( $_POST['search'] ) ) {
1485 $args['s'] = wp_unslash( $_POST['search'] );
1488 if ( isset( $_POST['term'] ) ) {
1489 $args['s'] = wp_unslash( $_POST['term'] );
1492 $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
1494 require(ABSPATH . WPINC . '/class-wp-editor.php');
1495 $results = _WP_Editors::wp_link_query( $args );
1497 if ( ! isset( $results ) )
1500 echo wp_json_encode( $results );
1507 * Ajax handler for menu locations save.
1511 function wp_ajax_menu_locations_save() {
1512 if ( ! current_user_can( 'edit_theme_options' ) )
1514 check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1515 if ( ! isset( $_POST['menu-locations'] ) )
1517 set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_POST['menu-locations'] ) );
1522 * Ajax handler for saving the meta box order.
1526 function wp_ajax_meta_box_order() {
1527 check_ajax_referer( 'meta-box-order' );
1528 $order = isset( $_POST['order'] ) ? (array) $_POST['order'] : false;
1529 $page_columns = isset( $_POST['page_columns'] ) ? $_POST['page_columns'] : 'auto';
1531 if ( $page_columns != 'auto' )
1532 $page_columns = (int) $page_columns;
1534 $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1536 if ( $page != sanitize_key( $page ) )
1539 if ( ! $user = wp_get_current_user() )
1543 update_user_option($user->ID, "meta-box-order_$page", $order, true);
1545 if ( $page_columns )
1546 update_user_option($user->ID, "screen_layout_$page", $page_columns, true);
1552 * Ajax handler for menu quick searching.
1556 function wp_ajax_menu_quick_search() {
1557 if ( ! current_user_can( 'edit_theme_options' ) )
1560 require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1562 _wp_ajax_menu_quick_search( $_POST );
1568 * Ajax handler to retrieve a permalink.
1572 function wp_ajax_get_permalink() {
1573 check_ajax_referer( 'getpermalink', 'getpermalinknonce' );
1574 $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1575 wp_die( get_preview_post_link( $post_id ) );
1579 * Ajax handler to retrieve a sample permalink.
1583 function wp_ajax_sample_permalink() {
1584 check_ajax_referer( 'samplepermalink', 'samplepermalinknonce' );
1585 $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1586 $title = isset($_POST['new_title'])? $_POST['new_title'] : '';
1587 $slug = isset($_POST['new_slug'])? $_POST['new_slug'] : null;
1588 wp_die( get_sample_permalink_html( $post_id, $title, $slug ) );
1592 * Ajax handler for Quick Edit saving a post from a list table.
1596 * @global WP_List_Table $wp_list_table
1598 function wp_ajax_inline_save() {
1599 global $wp_list_table, $mode;
1601 check_ajax_referer( 'inlineeditnonce', '_inline_edit' );
1603 if ( ! isset($_POST['post_ID']) || ! ( $post_ID = (int) $_POST['post_ID'] ) )
1606 if ( 'page' == $_POST['post_type'] ) {
1607 if ( ! current_user_can( 'edit_page', $post_ID ) )
1608 wp_die( __( 'You are not allowed to edit this page.' ) );
1610 if ( ! current_user_can( 'edit_post', $post_ID ) )
1611 wp_die( __( 'You are not allowed to edit this post.' ) );
1614 if ( $last = wp_check_post_lock( $post_ID ) ) {
1615 $last_user = get_userdata( $last );
1616 $last_user_name = $last_user ? $last_user->display_name : __( 'Someone' );
1617 printf( $_POST['post_type'] == 'page' ? __( 'Saving is disabled: %s is currently editing this page.' ) : __( 'Saving is disabled: %s is currently editing this post.' ), esc_html( $last_user_name ) );
1623 $post = get_post( $post_ID, ARRAY_A );
1625 // Since it's coming from the database.
1626 $post = wp_slash($post);
1628 $data['content'] = $post['post_content'];
1629 $data['excerpt'] = $post['post_excerpt'];
1632 $data['user_ID'] = get_current_user_id();
1634 if ( isset($data['post_parent']) )
1635 $data['parent_id'] = $data['post_parent'];
1638 if ( isset( $data['keep_private'] ) && 'private' == $data['keep_private'] ) {
1639 $data['visibility'] = 'private';
1640 $data['post_status'] = 'private';
1642 $data['post_status'] = $data['_status'];
1645 if ( empty($data['comment_status']) )
1646 $data['comment_status'] = 'closed';
1647 if ( empty($data['ping_status']) )
1648 $data['ping_status'] = 'closed';
1650 // Exclude terms from taxonomies that are not supposed to appear in Quick Edit.
1651 if ( ! empty( $data['tax_input'] ) ) {
1652 foreach ( $data['tax_input'] as $taxonomy => $terms ) {
1653 $tax_object = get_taxonomy( $taxonomy );
1654 /** This filter is documented in wp-admin/includes/class-wp-posts-list-table.php */
1655 if ( ! apply_filters( 'quick_edit_show_taxonomy', $tax_object->show_in_quick_edit, $taxonomy, $post['post_type'] ) ) {
1656 unset( $data['tax_input'][ $taxonomy ] );
1661 // Hack: wp_unique_post_slug() doesn't work for drafts, so we will fake that our post is published.
1662 if ( ! empty( $data['post_name'] ) && in_array( $post['post_status'], array( 'draft', 'pending' ) ) ) {
1663 $post['post_status'] = 'publish';
1664 $data['post_name'] = wp_unique_post_slug( $data['post_name'], $post['ID'], $post['post_status'], $post['post_type'], $post['post_parent'] );
1670 $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => $_POST['screen'] ) );
1672 $mode = $_POST['post_view'] === 'excerpt' ? 'excerpt' : 'list';
1675 $request_post = array( get_post( $_POST['post_ID'] ) );
1676 $parent = $request_post[0]->post_parent;
1678 while ( $parent > 0 ) {
1679 $parent_post = get_post( $parent );
1680 $parent = $parent_post->post_parent;
1684 $wp_list_table->display_rows( array( get_post( $_POST['post_ID'] ) ), $level );
1690 * Ajax handler for quick edit saving for a term.
1694 * @global WP_List_Table $wp_list_table
1696 function wp_ajax_inline_save_tax() {
1697 global $wp_list_table;
1699 check_ajax_referer( 'taxinlineeditnonce', '_inline_edit' );
1701 $taxonomy = sanitize_key( $_POST['taxonomy'] );
1702 $tax = get_taxonomy( $taxonomy );
1706 if ( ! current_user_can( $tax->cap->edit_terms ) )
1709 $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => 'edit-' . $taxonomy ) );
1711 if ( ! isset($_POST['tax_ID']) || ! ( $id = (int) $_POST['tax_ID'] ) )
1714 $tag = get_term( $id, $taxonomy );
1715 $_POST['description'] = $tag->description;
1717 $updated = wp_update_term($id, $taxonomy, $_POST);
1718 if ( $updated && !is_wp_error($updated) ) {
1719 $tag = get_term( $updated['term_id'], $taxonomy );
1720 if ( !$tag || is_wp_error( $tag ) ) {
1721 if ( is_wp_error($tag) && $tag->get_error_message() )
1722 wp_die( $tag->get_error_message() );
1723 wp_die( __( 'Item not updated.' ) );
1726 if ( is_wp_error($updated) && $updated->get_error_message() )
1727 wp_die( $updated->get_error_message() );
1728 wp_die( __( 'Item not updated.' ) );
1731 $parent = $tag->parent;
1732 while ( $parent > 0 ) {
1733 $parent_tag = get_term( $parent, $taxonomy );
1734 $parent = $parent_tag->parent;
1737 $wp_list_table->single_row( $tag, $level );
1742 * Ajax handler for querying posts for the Find Posts modal.
1744 * @see window.findPosts
1748 function wp_ajax_find_posts() {
1749 check_ajax_referer( 'find-posts' );
1751 $post_types = get_post_types( array( 'public' => true ), 'objects' );
1752 unset( $post_types['attachment'] );
1754 $s = wp_unslash( $_POST['ps'] );
1756 'post_type' => array_keys( $post_types ),
1757 'post_status' => 'any',
1758 'posts_per_page' => 50,
1763 $posts = get_posts( $args );
1766 wp_send_json_error( __( 'No items found.' ) );
1769 $html = '<table class="widefat"><thead><tr><th class="found-radio"><br /></th><th>'.__('Title').'</th><th class="no-break">'.__('Type').'</th><th class="no-break">'.__('Date').'</th><th class="no-break">'.__('Status').'</th></tr></thead><tbody>';
1771 foreach ( $posts as $post ) {
1772 $title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
1773 $alt = ( 'alternate' == $alt ) ? '' : 'alternate';
1775 switch ( $post->post_status ) {
1778 $stat = __('Published');
1781 $stat = __('Scheduled');
1784 $stat = __('Pending Review');
1787 $stat = __('Draft');
1791 if ( '0000-00-00 00:00:00' == $post->post_date ) {
1794 /* translators: date format in table columns, see http://php.net/date */
1795 $time = mysql2date(__('Y/m/d'), $post->post_date);
1798 $html .= '<tr class="' . trim( 'found-posts ' . $alt ) . '"><td class="found-radio"><input type="radio" id="found-'.$post->ID.'" name="found_post_id" value="' . esc_attr($post->ID) . '"></td>';
1799 $html .= '<td><label for="found-'.$post->ID.'">' . esc_html( $title ) . '</label></td><td class="no-break">' . esc_html( $post_types[$post->post_type]->labels->singular_name ) . '</td><td class="no-break">'.esc_html( $time ) . '</td><td class="no-break">' . esc_html( $stat ). ' </td></tr>' . "\n\n";
1802 $html .= '</tbody></table>';
1804 wp_send_json_success( $html );
1808 * Ajax handler for saving the widgets order.
1812 function wp_ajax_widgets_order() {
1813 check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1815 if ( !current_user_can('edit_theme_options') )
1818 unset( $_POST['savewidgets'], $_POST['action'] );
1820 // Save widgets order for all sidebars.
1821 if ( is_array($_POST['sidebars']) ) {
1822 $sidebars = array();
1823 foreach ( $_POST['sidebars'] as $key => $val ) {
1825 if ( !empty($val) ) {
1826 $val = explode(',', $val);
1827 foreach ( $val as $k => $v ) {
1828 if ( strpos($v, 'widget-') === false )
1831 $sb[$k] = substr($v, strpos($v, '_') + 1);
1834 $sidebars[$key] = $sb;
1836 wp_set_sidebars_widgets($sidebars);
1844 * Ajax handler for saving a widget.
1848 * @global array $wp_registered_widgets
1849 * @global array $wp_registered_widget_controls
1850 * @global array $wp_registered_widget_updates
1852 function wp_ajax_save_widget() {
1853 global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
1855 check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1857 if ( !current_user_can('edit_theme_options') || !isset($_POST['id_base']) )
1860 unset( $_POST['savewidgets'], $_POST['action'] );
1863 * Fires early when editing the widgets displayed in sidebars.
1867 do_action( 'load-widgets.php' );
1870 * Fires early when editing the widgets displayed in sidebars.
1874 do_action( 'widgets.php' );
1876 /** This action is documented in wp-admin/widgets.php */
1877 do_action( 'sidebar_admin_setup' );
1879 $id_base = $_POST['id_base'];
1880 $widget_id = $_POST['widget-id'];
1881 $sidebar_id = $_POST['sidebar'];
1882 $multi_number = !empty($_POST['multi_number']) ? (int) $_POST['multi_number'] : 0;
1883 $settings = isset($_POST['widget-' . $id_base]) && is_array($_POST['widget-' . $id_base]) ? $_POST['widget-' . $id_base] : false;
1884 $error = '<p>' . __('An error has occurred. Please reload the page and try again.') . '</p>';
1886 $sidebars = wp_get_sidebars_widgets();
1887 $sidebar = isset($sidebars[$sidebar_id]) ? $sidebars[$sidebar_id] : array();
1890 if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1892 if ( !isset($wp_registered_widgets[$widget_id]) )
1895 $sidebar = array_diff( $sidebar, array($widget_id) );
1896 $_POST = array('sidebar' => $sidebar_id, 'widget-' . $id_base => array(), 'the-widget-id' => $widget_id, 'delete_widget' => '1');
1898 /** This action is documented in wp-admin/widgets.php */
1899 do_action( 'delete_widget', $widget_id, $sidebar_id, $id_base );
1901 } elseif ( $settings && preg_match( '/__i__|%i%/', key($settings) ) ) {
1902 if ( !$multi_number )
1905 $_POST[ 'widget-' . $id_base ] = array( $multi_number => reset( $settings ) );
1906 $widget_id = $id_base . '-' . $multi_number;
1907 $sidebar[] = $widget_id;
1909 $_POST['widget-id'] = $sidebar;
1911 foreach ( (array) $wp_registered_widget_updates as $name => $control ) {
1913 if ( $name == $id_base ) {
1914 if ( !is_callable( $control['callback'] ) )
1918 call_user_func_array( $control['callback'], $control['params'] );
1924 if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1925 $sidebars[$sidebar_id] = $sidebar;
1926 wp_set_sidebars_widgets($sidebars);
1927 echo "deleted:$widget_id";
1931 if ( !empty($_POST['add_new']) )
1934 if ( $form = $wp_registered_widget_controls[$widget_id] )
1935 call_user_func_array( $form['callback'], $form['params'] );
1941 * Ajax handler for saving a widget.
1945 * @global WP_Customize_Manager $wp_customize
1947 function wp_ajax_update_widget() {
1948 global $wp_customize;
1949 $wp_customize->widgets->wp_ajax_update_widget();
1953 * Ajax handler for removing inactive widgets.
1957 function wp_ajax_delete_inactive_widgets() {
1958 check_ajax_referer( 'remove-inactive-widgets', 'removeinactivewidgets' );
1960 if ( ! current_user_can( 'edit_theme_options' ) ) {
1964 unset( $_POST['removeinactivewidgets'], $_POST['action'] );
1966 do_action( 'load-widgets.php' );
1967 do_action( 'widgets.php' );
1968 do_action( 'sidebar_admin_setup' );
1970 $sidebars_widgets = wp_get_sidebars_widgets();
1972 foreach ( $sidebars_widgets['wp_inactive_widgets'] as $key => $widget_id ) {
1973 $pieces = explode( '-', $widget_id );
1974 $multi_number = array_pop( $pieces );
1975 $id_base = implode( '-', $pieces );
1976 $widget = get_option( 'widget_' . $id_base );
1977 unset( $widget[$multi_number] );
1978 update_option( 'widget_' . $id_base, $widget );
1979 unset( $sidebars_widgets['wp_inactive_widgets'][$key] );
1982 wp_set_sidebars_widgets( $sidebars_widgets );
1988 * Ajax handler for uploading attachments
1992 function wp_ajax_upload_attachment() {
1993 check_ajax_referer( 'media-form' );
1995 * This function does not use wp_send_json_success() / wp_send_json_error()
1996 * as the html4 Plupload handler requires a text/html content-type for older IE.
1997 * See https://core.trac.wordpress.org/ticket/31037
2000 if ( ! current_user_can( 'upload_files' ) ) {
2001 echo wp_json_encode( array(
2004 'message' => __( 'You do not have permission to upload files.' ),
2005 'filename' => $_FILES['async-upload']['name'],
2012 if ( isset( $_REQUEST['post_id'] ) ) {
2013 $post_id = $_REQUEST['post_id'];
2014 if ( ! current_user_can( 'edit_post', $post_id ) ) {
2015 echo wp_json_encode( array(
2018 'message' => __( "You don't have permission to attach files to this post." ),
2019 'filename' => $_FILES['async-upload']['name'],
2029 $post_data = isset( $_REQUEST['post_data'] ) ? $_REQUEST['post_data'] : array();
2031 // If the context is custom header or background, make sure the uploaded file is an image.
2032 if ( isset( $post_data['context'] ) && in_array( $post_data['context'], array( 'custom-header', 'custom-background' ) ) ) {
2033 $wp_filetype = wp_check_filetype_and_ext( $_FILES['async-upload']['tmp_name'], $_FILES['async-upload']['name'] );
2034 if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) {
2035 echo wp_json_encode( array(
2038 'message' => __( 'The uploaded file is not a valid image. Please try again.' ),
2039 'filename' => $_FILES['async-upload']['name'],
2047 $attachment_id = media_handle_upload( 'async-upload', $post_id, $post_data );
2049 if ( is_wp_error( $attachment_id ) ) {
2050 echo wp_json_encode( array(
2053 'message' => $attachment_id->get_error_message(),
2054 'filename' => $_FILES['async-upload']['name'],
2061 if ( isset( $post_data['context'] ) && isset( $post_data['theme'] ) ) {
2062 if ( 'custom-background' === $post_data['context'] )
2063 update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', $post_data['theme'] );
2065 if ( 'custom-header' === $post_data['context'] )
2066 update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', $post_data['theme'] );
2069 if ( ! $attachment = wp_prepare_attachment_for_js( $attachment_id ) )
2072 echo wp_json_encode( array(
2074 'data' => $attachment,
2081 * Ajax handler for image editing.
2085 function wp_ajax_image_editor() {
2086 $attachment_id = intval($_POST['postid']);
2087 if ( empty($attachment_id) || !current_user_can('edit_post', $attachment_id) )
2090 check_ajax_referer( "image_editor-$attachment_id" );
2091 include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
2094 switch ( $_POST['do'] ) {
2096 $msg = wp_save_image($attachment_id);
2097 $msg = wp_json_encode($msg);
2101 $msg = wp_save_image($attachment_id);
2104 $msg = wp_restore_image($attachment_id);
2108 wp_image_editor($attachment_id, $msg);
2113 * Ajax handler for setting the featured image.
2117 function wp_ajax_set_post_thumbnail() {
2118 $json = ! empty( $_REQUEST['json'] ); // New-style request
2120 $post_ID = intval( $_POST['post_id'] );
2121 if ( ! current_user_can( 'edit_post', $post_ID ) )
2124 $thumbnail_id = intval( $_POST['thumbnail_id'] );
2127 check_ajax_referer( "update-post_$post_ID" );
2129 check_ajax_referer( "set_post_thumbnail-$post_ID" );
2131 if ( $thumbnail_id == '-1' ) {
2132 if ( delete_post_thumbnail( $post_ID ) ) {
2133 $return = _wp_post_thumbnail_html( null, $post_ID );
2134 $json ? wp_send_json_success( $return ) : wp_die( $return );
2140 if ( set_post_thumbnail( $post_ID, $thumbnail_id ) ) {
2141 $return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID );
2142 $json ? wp_send_json_success( $return ) : wp_die( $return );
2149 * AJAX handler for setting the featured image for an attachment.
2153 * @see set_post_thumbnail()
2155 function wp_ajax_set_attachment_thumbnail() {
2156 if ( empty( $_POST['urls'] ) || ! is_array( $_POST['urls'] ) ) {
2157 wp_send_json_error();
2160 $thumbnail_id = (int) $_POST['thumbnail_id'];
2161 if ( empty( $thumbnail_id ) ) {
2162 wp_send_json_error();
2165 $post_ids = array();
2166 // For each URL, try to find its corresponding post ID.
2167 foreach ( $_POST['urls'] as $url ) {
2168 $post_id = attachment_url_to_postid( $url );
2169 if ( ! empty( $post_id ) ) {
2170 $post_ids[] = $post_id;
2174 if ( empty( $post_ids ) ) {
2175 wp_send_json_error();
2179 // For each found attachment, set its thumbnail.
2180 foreach ( $post_ids as $post_id ) {
2181 if ( ! current_user_can( 'edit_post', $post_id ) ) {
2185 if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) {
2190 if ( 0 === $success ) {
2191 wp_send_json_error();
2193 wp_send_json_success();
2196 wp_send_json_error();
2200 * Ajax handler for date formatting.
2204 function wp_ajax_date_format() {
2205 wp_die( date_i18n( sanitize_option( 'date_format', wp_unslash( $_POST['date'] ) ) ) );
2209 * Ajax handler for time formatting.
2213 function wp_ajax_time_format() {
2214 wp_die( date_i18n( sanitize_option( 'time_format', wp_unslash( $_POST['date'] ) ) ) );
2218 * Ajax handler for saving posts from the fullscreen editor.
2223 function wp_ajax_wp_fullscreen_save_post() {
2224 $post_id = isset( $_POST['post_ID'] ) ? (int) $_POST['post_ID'] : 0;
2229 $post = get_post( $post_id );
2231 check_ajax_referer('update-post_' . $post_id, '_wpnonce');
2233 $post_id = edit_post();
2235 if ( is_wp_error( $post_id ) ) {
2236 wp_send_json_error();
2240 $last_date = mysql2date( __( 'F j, Y' ), $post->post_modified );
2241 $last_time = mysql2date( __( 'g:i a' ), $post->post_modified );
2243 $last_date = date_i18n( __( 'F j, Y' ) );
2244 $last_time = date_i18n( __( 'g:i a' ) );
2247 if ( $last_id = get_post_meta( $post_id, '_edit_last', true ) ) {
2248 $last_user = get_userdata( $last_id );
2249 $last_edited = sprintf( __('Last edited by %1$s on %2$s at %3$s'), esc_html( $last_user->display_name ), $last_date, $last_time );
2251 $last_edited = sprintf( __('Last edited on %1$s at %2$s'), $last_date, $last_time );
2254 wp_send_json_success( array( 'last_edited' => $last_edited ) );
2258 * Ajax handler for removing a post lock.
2262 function wp_ajax_wp_remove_post_lock() {
2263 if ( empty( $_POST['post_ID'] ) || empty( $_POST['active_post_lock'] ) )
2265 $post_id = (int) $_POST['post_ID'];
2266 if ( ! $post = get_post( $post_id ) )
2269 check_ajax_referer( 'update-post_' . $post_id );
2271 if ( ! current_user_can( 'edit_post', $post_id ) )
2274 $active_lock = array_map( 'absint', explode( ':', $_POST['active_post_lock'] ) );
2275 if ( $active_lock[1] != get_current_user_id() )
2279 * Filter the post lock window duration.
2283 * @param int $interval The interval in seconds the post lock duration
2284 * should last, plus 5 seconds. Default 150.
2286 $new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 150 ) + 5 ) . ':' . $active_lock[1];
2287 update_post_meta( $post_id, '_edit_lock', $new_lock, implode( ':', $active_lock ) );
2292 * Ajax handler for dismissing a WordPress pointer.
2296 function wp_ajax_dismiss_wp_pointer() {
2297 $pointer = $_POST['pointer'];
2298 if ( $pointer != sanitize_key( $pointer ) )
2301 // check_ajax_referer( 'dismiss-pointer_' . $pointer );
2303 $dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) );
2305 if ( in_array( $pointer, $dismissed ) )
2308 $dismissed[] = $pointer;
2309 $dismissed = implode( ',', $dismissed );
2311 update_user_meta( get_current_user_id(), 'dismissed_wp_pointers', $dismissed );
2316 * Ajax handler for getting an attachment.
2320 function wp_ajax_get_attachment() {
2321 if ( ! isset( $_REQUEST['id'] ) )
2322 wp_send_json_error();
2324 if ( ! $id = absint( $_REQUEST['id'] ) )
2325 wp_send_json_error();
2327 if ( ! $post = get_post( $id ) )
2328 wp_send_json_error();
2330 if ( 'attachment' != $post->post_type )
2331 wp_send_json_error();
2333 if ( ! current_user_can( 'upload_files' ) )
2334 wp_send_json_error();
2336 if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2337 wp_send_json_error();
2339 wp_send_json_success( $attachment );
2343 * Ajax handler for querying attachments.
2347 function wp_ajax_query_attachments() {
2348 if ( ! current_user_can( 'upload_files' ) )
2349 wp_send_json_error();
2351 $query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array();
2353 's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type',
2354 'post_parent', 'post__in', 'post__not_in', 'year', 'monthnum'
2356 foreach ( get_taxonomies_for_attachments( 'objects' ) as $t ) {
2357 if ( $t->query_var && isset( $query[ $t->query_var ] ) ) {
2358 $keys[] = $t->query_var;
2362 $query = array_intersect_key( $query, array_flip( $keys ) );
2363 $query['post_type'] = 'attachment';
2365 && ! empty( $_REQUEST['query']['post_status'] )
2366 && 'trash' === $_REQUEST['query']['post_status'] ) {
2367 $query['post_status'] = 'trash';
2369 $query['post_status'] = 'inherit';
2372 if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) )
2373 $query['post_status'] .= ',private';
2376 * Filter the arguments passed to WP_Query during an AJAX
2377 * call for querying attachments.
2381 * @see WP_Query::parse_query()
2383 * @param array $query An array of query variables.
2385 $query = apply_filters( 'ajax_query_attachments_args', $query );
2386 $query = new WP_Query( $query );
2388 $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts );
2389 $posts = array_filter( $posts );
2391 wp_send_json_success( $posts );
2395 * Ajax handler for updating attachment attributes.
2399 function wp_ajax_save_attachment() {
2400 if ( ! isset( $_REQUEST['id'] ) || ! isset( $_REQUEST['changes'] ) )
2401 wp_send_json_error();
2403 if ( ! $id = absint( $_REQUEST['id'] ) )
2404 wp_send_json_error();
2406 check_ajax_referer( 'update-post_' . $id, 'nonce' );
2408 if ( ! current_user_can( 'edit_post', $id ) )
2409 wp_send_json_error();
2411 $changes = $_REQUEST['changes'];
2412 $post = get_post( $id, ARRAY_A );
2414 if ( 'attachment' != $post['post_type'] )
2415 wp_send_json_error();
2417 if ( isset( $changes['parent'] ) )
2418 $post['post_parent'] = $changes['parent'];
2420 if ( isset( $changes['title'] ) )
2421 $post['post_title'] = $changes['title'];
2423 if ( isset( $changes['caption'] ) )
2424 $post['post_excerpt'] = $changes['caption'];
2426 if ( isset( $changes['description'] ) )
2427 $post['post_content'] = $changes['description'];
2429 if ( MEDIA_TRASH && isset( $changes['status'] ) )
2430 $post['post_status'] = $changes['status'];
2432 if ( isset( $changes['alt'] ) ) {
2433 $alt = wp_unslash( $changes['alt'] );
2434 if ( $alt != get_post_meta( $id, '_wp_attachment_image_alt', true ) ) {
2435 $alt = wp_strip_all_tags( $alt, true );
2436 update_post_meta( $id, '_wp_attachment_image_alt', wp_slash( $alt ) );
2440 if ( wp_attachment_is( 'audio', $post['ID'] ) ) {
2442 $id3data = wp_get_attachment_metadata( $post['ID'] );
2443 if ( ! is_array( $id3data ) ) {
2447 foreach ( wp_get_attachment_id3_keys( (object) $post, 'edit' ) as $key => $label ) {
2448 if ( isset( $changes[ $key ] ) ) {
2450 $id3data[ $key ] = sanitize_text_field( wp_unslash( $changes[ $key ] ) );
2455 wp_update_attachment_metadata( $id, $id3data );
2459 if ( MEDIA_TRASH && isset( $changes['status'] ) && 'trash' === $changes['status'] ) {
2460 wp_delete_post( $id );
2462 wp_update_post( $post );
2465 wp_send_json_success();
2469 * Ajax handler for saving backwards compatible attachment attributes.
2473 function wp_ajax_save_attachment_compat() {
2474 if ( ! isset( $_REQUEST['id'] ) )
2475 wp_send_json_error();
2477 if ( ! $id = absint( $_REQUEST['id'] ) )
2478 wp_send_json_error();
2480 if ( empty( $_REQUEST['attachments'] ) || empty( $_REQUEST['attachments'][ $id ] ) )
2481 wp_send_json_error();
2482 $attachment_data = $_REQUEST['attachments'][ $id ];
2484 check_ajax_referer( 'update-post_' . $id, 'nonce' );
2486 if ( ! current_user_can( 'edit_post', $id ) )
2487 wp_send_json_error();
2489 $post = get_post( $id, ARRAY_A );
2491 if ( 'attachment' != $post['post_type'] )
2492 wp_send_json_error();
2494 /** This filter is documented in wp-admin/includes/media.php */
2495 $post = apply_filters( 'attachment_fields_to_save', $post, $attachment_data );
2497 if ( isset( $post['errors'] ) ) {
2498 $errors = $post['errors']; // @todo return me and display me!
2499 unset( $post['errors'] );
2502 wp_update_post( $post );
2504 foreach ( get_attachment_taxonomies( $post ) as $taxonomy ) {
2505 if ( isset( $attachment_data[ $taxonomy ] ) )
2506 wp_set_object_terms( $id, array_map( 'trim', preg_split( '/,+/', $attachment_data[ $taxonomy ] ) ), $taxonomy, false );
2509 if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2510 wp_send_json_error();
2512 wp_send_json_success( $attachment );
2516 * Ajax handler for saving the attachment order.
2520 function wp_ajax_save_attachment_order() {
2521 if ( ! isset( $_REQUEST['post_id'] ) )
2522 wp_send_json_error();
2524 if ( ! $post_id = absint( $_REQUEST['post_id'] ) )
2525 wp_send_json_error();
2527 if ( empty( $_REQUEST['attachments'] ) )
2528 wp_send_json_error();
2530 check_ajax_referer( 'update-post_' . $post_id, 'nonce' );
2532 $attachments = $_REQUEST['attachments'];
2534 if ( ! current_user_can( 'edit_post', $post_id ) )
2535 wp_send_json_error();
2537 foreach ( $attachments as $attachment_id => $menu_order ) {
2538 if ( ! current_user_can( 'edit_post', $attachment_id ) )
2540 if ( ! $attachment = get_post( $attachment_id ) )
2542 if ( 'attachment' != $attachment->post_type )
2545 wp_update_post( array( 'ID' => $attachment_id, 'menu_order' => $menu_order ) );
2548 wp_send_json_success();
2552 * Ajax handler for sending an attachment to the editor.
2554 * Generates the HTML to send an attachment to the editor.
2555 * Backwards compatible with the media_send_to_editor filter
2556 * and the chain of filters that follow.
2560 function wp_ajax_send_attachment_to_editor() {
2561 check_ajax_referer( 'media-send-to-editor', 'nonce' );
2563 $attachment = wp_unslash( $_POST['attachment'] );
2565 $id = intval( $attachment['id'] );
2567 if ( ! $post = get_post( $id ) )
2568 wp_send_json_error();
2570 if ( 'attachment' != $post->post_type )
2571 wp_send_json_error();
2573 if ( current_user_can( 'edit_post', $id ) ) {
2574 // If this attachment is unattached, attach it. Primarily a back compat thing.
2575 if ( 0 == $post->post_parent && $insert_into_post_id = intval( $_POST['post_id'] ) ) {
2576 wp_update_post( array( 'ID' => $id, 'post_parent' => $insert_into_post_id ) );
2580 $url = empty( $attachment['url'] ) ? '' : $attachment['url'];
2581 $rel = ( strpos( $url, 'attachment_id') || get_attachment_link( $id ) == $url );
2583 remove_filter( 'media_send_to_editor', 'image_media_send_to_editor' );
2585 if ( 'image' === substr( $post->post_mime_type, 0, 5 ) ) {
2586 $align = isset( $attachment['align'] ) ? $attachment['align'] : 'none';
2587 $size = isset( $attachment['image-size'] ) ? $attachment['image-size'] : 'medium';
2588 $alt = isset( $attachment['image_alt'] ) ? $attachment['image_alt'] : '';
2590 // No whitespace-only captions.
2591 $caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : '';
2592 if ( '' === trim( $caption ) ) {
2596 $title = ''; // We no longer insert title tags into <img> tags, as they are redundant.
2597 $html = get_image_send_to_editor( $id, $caption, $title, $align, $url, $rel, $size, $alt );
2598 } elseif ( wp_attachment_is( 'video', $post ) || wp_attachment_is( 'audio', $post ) ) {
2599 $html = stripslashes_deep( $_POST['html'] );
2601 $html = isset( $attachment['post_title'] ) ? $attachment['post_title'] : '';
2602 $rel = $rel ? ' rel="attachment wp-att-' . $id . '"' : ''; // Hard-coded string, $id is already sanitized
2604 if ( ! empty( $url ) ) {
2605 $html = '<a href="' . esc_url( $url ) . '"' . $rel . '>' . $html . '</a>';
2609 /** This filter is documented in wp-admin/includes/media.php */
2610 $html = apply_filters( 'media_send_to_editor', $html, $id, $attachment );
2612 wp_send_json_success( $html );
2616 * Ajax handler for sending a link to the editor.
2618 * Generates the HTML to send a non-image embed link to the editor.
2620 * Backwards compatible with the following filters:
2621 * - file_send_to_editor_url
2622 * - audio_send_to_editor_url
2623 * - video_send_to_editor_url
2627 * @global WP_Post $post
2628 * @global WP_Embed $wp_embed
2630 function wp_ajax_send_link_to_editor() {
2631 global $post, $wp_embed;
2633 check_ajax_referer( 'media-send-to-editor', 'nonce' );
2635 if ( ! $src = wp_unslash( $_POST['src'] ) )
2636 wp_send_json_error();
2638 if ( ! strpos( $src, '://' ) )
2639 $src = 'http://' . $src;
2641 if ( ! $src = esc_url_raw( $src ) )
2642 wp_send_json_error();
2644 if ( ! $link_text = trim( wp_unslash( $_POST['link_text'] ) ) )
2645 $link_text = wp_basename( $src );
2647 $post = get_post( isset( $_POST['post_id'] ) ? $_POST['post_id'] : 0 );
2649 // Ping WordPress for an embed.
2650 $check_embed = $wp_embed->run_shortcode( '[embed]'. $src .'[/embed]' );
2652 // Fallback that WordPress creates when no oEmbed was found.
2653 $fallback = $wp_embed->maybe_make_link( $src );
2655 if ( $check_embed !== $fallback ) {
2656 // TinyMCE view for [embed] will parse this
2657 $html = '[embed]' . $src . '[/embed]';
2658 } elseif ( $link_text ) {
2659 $html = '<a href="' . esc_url( $src ) . '">' . $link_text . '</a>';
2664 // Figure out what filter to run:
2666 if ( ( $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $src ) ) && ( $ext_type = wp_ext2type( $ext ) )
2667 && ( 'audio' == $ext_type || 'video' == $ext_type ) )
2670 /** This filter is documented in wp-admin/includes/media.php */
2671 $html = apply_filters( $type . '_send_to_editor_url', $html, $src, $link_text );
2673 wp_send_json_success( $html );
2677 * Ajax handler for the Heartbeat API.
2679 * Runs when the user is logged in.
2683 function wp_ajax_heartbeat() {
2684 if ( empty( $_POST['_nonce'] ) ) {
2685 wp_send_json_error();
2688 $response = $data = array();
2689 $nonce_state = wp_verify_nonce( $_POST['_nonce'], 'heartbeat-nonce' );
2691 // screen_id is the same as $current_screen->id and the JS global 'pagenow'.
2692 if ( ! empty( $_POST['screen_id'] ) ) {
2693 $screen_id = sanitize_key($_POST['screen_id']);
2695 $screen_id = 'front';
2698 if ( ! empty( $_POST['data'] ) ) {
2699 $data = wp_unslash( (array) $_POST['data'] );
2702 if ( 1 !== $nonce_state ) {
2703 $response = apply_filters( 'wp_refresh_nonces', $response, $data, $screen_id );
2705 if ( false === $nonce_state ) {
2706 // User is logged in but nonces have expired.
2707 $response['nonces_expired'] = true;
2708 wp_send_json( $response );
2712 if ( ! empty( $data ) ) {
2714 * Filter the Heartbeat response received.
2718 * @param array $response The Heartbeat response.
2719 * @param array $data The $_POST data sent.
2720 * @param string $screen_id The screen id.
2722 $response = apply_filters( 'heartbeat_received', $response, $data, $screen_id );
2726 * Filter the Heartbeat response sent.
2730 * @param array $response The Heartbeat response.
2731 * @param string $screen_id The screen id.
2733 $response = apply_filters( 'heartbeat_send', $response, $screen_id );
2736 * Fires when Heartbeat ticks in logged-in environments.
2738 * Allows the transport to be easily replaced with long-polling.
2742 * @param array $response The Heartbeat response.
2743 * @param string $screen_id The screen id.
2745 do_action( 'heartbeat_tick', $response, $screen_id );
2747 // Send the current time according to the server
2748 $response['server_time'] = time();
2750 wp_send_json( $response );
2754 * Ajax handler for getting revision diffs.
2758 function wp_ajax_get_revision_diffs() {
2759 require ABSPATH . 'wp-admin/includes/revision.php';
2761 if ( ! $post = get_post( (int) $_REQUEST['post_id'] ) )
2762 wp_send_json_error();
2764 if ( ! current_user_can( 'read_post', $post->ID ) )
2765 wp_send_json_error();
2767 // Really just pre-loading the cache here.
2768 if ( ! $revisions = wp_get_post_revisions( $post->ID, array( 'check_enabled' => false ) ) )
2769 wp_send_json_error();
2772 @set_time_limit( 0 );
2774 foreach ( $_REQUEST['compare'] as $compare_key ) {
2775 list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to
2778 'id' => $compare_key,
2779 'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ),
2782 wp_send_json_success( $return );
2786 * Ajax handler for auto-saving the selected color scheme for
2787 * a user's own profile.
2791 * @global array $_wp_admin_css_colors
2793 function wp_ajax_save_user_color_scheme() {
2794 global $_wp_admin_css_colors;
2796 check_ajax_referer( 'save-color-scheme', 'nonce' );
2798 $color_scheme = sanitize_key( $_POST['color_scheme'] );
2800 if ( ! isset( $_wp_admin_css_colors[ $color_scheme ] ) ) {
2801 wp_send_json_error();
2804 $previous_color_scheme = get_user_meta( get_current_user_id(), 'admin_color', true );
2805 update_user_meta( get_current_user_id(), 'admin_color', $color_scheme );
2807 wp_send_json_success( array(
2808 'previousScheme' => 'admin-color-' . $previous_color_scheme,
2809 'currentScheme' => 'admin-color-' . $color_scheme
2814 * Ajax handler for getting themes from themes_api().
2818 * @global array $themes_allowedtags
2819 * @global array $theme_field_defaults
2821 function wp_ajax_query_themes() {
2822 global $themes_allowedtags, $theme_field_defaults;
2824 if ( ! current_user_can( 'install_themes' ) ) {
2825 wp_send_json_error();
2828 $args = wp_parse_args( wp_unslash( $_REQUEST['request'] ), array(
2830 'fields' => $theme_field_defaults
2833 if ( isset( $args['browse'] ) && 'favorites' === $args['browse'] && ! isset( $args['user'] ) ) {
2834 $user = get_user_option( 'wporg_favorites' );
2836 $args['user'] = $user;
2840 $old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search';
2842 /** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */
2843 $args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args );
2845 $api = themes_api( 'query_themes', $args );
2847 if ( is_wp_error( $api ) ) {
2848 wp_send_json_error();
2851 $update_php = network_admin_url( 'update.php?action=install-theme' );
2852 foreach ( $api->themes as &$theme ) {
2853 $theme->install_url = add_query_arg( array(
2854 'theme' => $theme->slug,
2855 '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug )
2858 $theme->name = wp_kses( $theme->name, $themes_allowedtags );
2859 $theme->author = wp_kses( $theme->author, $themes_allowedtags );
2860 $theme->version = wp_kses( $theme->version, $themes_allowedtags );
2861 $theme->description = wp_kses( $theme->description, $themes_allowedtags );
2862 $theme->stars = wp_star_rating( array( 'rating' => $theme->rating, 'type' => 'percent', 'number' => $theme->num_ratings, 'echo' => false ) );
2863 $theme->num_ratings = number_format_i18n( $theme->num_ratings );
2864 $theme->preview_url = set_url_scheme( $theme->preview_url );
2867 wp_send_json_success( $api );
2871 * Apply [embed] AJAX handlers to a string.
2875 * @global WP_Post $post Global $post.
2876 * @global WP_Embed $wp_embed Embed API instance.
2877 * @global WP_Scripts $wp_scripts
2879 function wp_ajax_parse_embed() {
2880 global $post, $wp_embed;
2882 if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) {
2883 wp_send_json_error();
2886 if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) {
2887 wp_send_json_error();
2890 $shortcode = wp_unslash( $_POST['shortcode'] );
2892 preg_match( '/' . get_shortcode_regex() . '/s', $shortcode, $matches );
2893 $atts = shortcode_parse_atts( $matches[3] );
2894 if ( ! empty( $matches[5] ) ) {
2896 } elseif ( ! empty( $atts['src'] ) ) {
2897 $url = $atts['src'];
2903 setup_postdata( $post );
2905 $wp_embed->return_false_on_fail = true;
2907 if ( is_ssl() && 0 === strpos( $url, 'http://' ) ) {
2908 // Admin is ssl and the user pasted non-ssl URL.
2909 // Check if the provider supports ssl embeds and use that for the preview.
2910 $ssl_shortcode = preg_replace( '%^(\\[embed[^\\]]*\\])http://%i', '$1https://', $shortcode );
2911 $parsed = $wp_embed->run_shortcode( $ssl_shortcode );
2914 $no_ssl_support = true;
2918 if ( $url && ! $parsed ) {
2919 $parsed = $wp_embed->run_shortcode( $shortcode );
2923 wp_send_json_error( array(
2924 'type' => 'not-embeddable',
2925 'message' => sprintf( __( '%s failed to embed.' ), '<code>' . esc_html( $url ) . '</code>' ),
2929 if ( has_shortcode( $parsed, 'audio' ) || has_shortcode( $parsed, 'video' ) ) {
2931 $mce_styles = wpview_media_sandbox_styles();
2932 foreach ( $mce_styles as $style ) {
2933 $styles .= sprintf( '<link rel="stylesheet" href="%s"/>', $style );
2936 $html = do_shortcode( $parsed );
2939 if ( ! empty( $wp_scripts ) ) {
2940 $wp_scripts->done = array();
2943 wp_print_scripts( 'wp-mediaelement' );
2944 $scripts = ob_get_clean();
2946 $parsed = $styles . $html . $scripts;
2950 if ( ! empty( $no_ssl_support ) || ( is_ssl() && ( preg_match( '%<(iframe|script|embed) [^>]*src="http://%', $parsed ) ||
2951 preg_match( '%<link [^>]*href="http://%', $parsed ) ) ) ) {
2952 // Admin is ssl and the embed is not. Iframes, scripts, and other "active content" will be blocked.
2953 wp_send_json_error( array(
2954 'type' => 'not-ssl',
2955 'message' => __( 'This preview is unavailable in the editor.' ),
2959 wp_send_json_success( array(
2961 'attr' => $wp_embed->last_attr
2968 * @global WP_Post $post
2969 * @global WP_Scripts $wp_scripts
2971 function wp_ajax_parse_media_shortcode() {
2972 global $post, $wp_scripts;
2974 if ( empty( $_POST['shortcode'] ) ) {
2975 wp_send_json_error();
2978 $shortcode = wp_unslash( $_POST['shortcode'] );
2980 if ( ! empty( $_POST['post_ID'] ) ) {
2981 $post = get_post( (int) $_POST['post_ID'] );
2984 // the embed shortcode requires a post
2985 if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) {
2986 if ( 'embed' === $shortcode ) {
2987 wp_send_json_error();
2990 setup_postdata( $post );
2993 $parsed = do_shortcode( $shortcode );
2995 if ( empty( $parsed ) ) {
2996 wp_send_json_error( array(
2997 'type' => 'no-items',
2998 'message' => __( 'No items found.' ),
3003 $styles = wpview_media_sandbox_styles();
3005 foreach ( $styles as $style ) {
3006 $head .= '<link type="text/css" rel="stylesheet" href="' . $style . '">';
3009 if ( ! empty( $wp_scripts ) ) {
3010 $wp_scripts->done = array();
3017 if ( 'playlist' === $_REQUEST['type'] ) {
3018 wp_underscore_playlist_templates();
3020 wp_print_scripts( 'wp-playlist' );
3022 wp_print_scripts( array( 'froogaloop', 'wp-mediaelement' ) );
3025 wp_send_json_success( array(
3027 'body' => ob_get_clean()
3032 * AJAX handler for destroying multiple open sessions for a user.
3036 function wp_ajax_destroy_sessions() {
3037 $user = get_userdata( (int) $_POST['user_id'] );
3039 if ( ! current_user_can( 'edit_user', $user->ID ) ) {
3041 } elseif ( ! wp_verify_nonce( $_POST['nonce'], 'update-user_' . $user->ID ) ) {
3047 wp_send_json_error( array(
3048 'message' => __( 'Could not log out user sessions. Please try again.' ),
3052 $sessions = WP_Session_Tokens::get_instance( $user->ID );
3054 if ( $user->ID === get_current_user_id() ) {
3055 $sessions->destroy_others( wp_get_session_token() );
3056 $message = __( 'You are now logged out everywhere else.' );
3058 $sessions->destroy_all();
3059 /* translators: 1: User's display name. */
3060 $message = sprintf( __( '%s has been logged out.' ), $user->display_name );
3063 wp_send_json_success( array( 'message' => $message ) );
3068 * AJAX handler for updating a plugin.
3072 * @see Plugin_Upgrader
3074 function wp_ajax_update_plugin() {
3075 global $wp_filesystem;
3077 $plugin = urldecode( $_POST['plugin'] );
3080 'update' => 'plugin',
3081 'plugin' => $plugin,
3082 'slug' => sanitize_key( $_POST['slug'] ),
3087 $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
3088 if ( $plugin_data['Version'] ) {
3089 $status['oldVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
3092 if ( ! current_user_can( 'update_plugins' ) ) {
3093 $status['error'] = __( 'You do not have sufficient permissions to update plugins for this site.' );
3094 wp_send_json_error( $status );
3097 check_ajax_referer( 'updates' );
3099 include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
3101 wp_update_plugins();
3103 $skin = new Automatic_Upgrader_Skin();
3104 $upgrader = new Plugin_Upgrader( $skin );
3105 $result = $upgrader->bulk_upgrade( array( $plugin ) );
3107 if ( is_array( $result ) && empty( $result[$plugin] ) && is_wp_error( $skin->result ) ) {
3108 $result = $skin->result;
3111 if ( is_array( $result ) && !empty( $result[ $plugin ] ) ) {
3112 $plugin_update_data = current( $result );
3115 * If the `update_plugins` site transient is empty (e.g. when you update
3116 * two plugins in quick succession before the transient repopulates),
3117 * this may be the return.
3119 * Preferably something can be done to ensure `update_plugins` isn't empty.
3120 * For now, surface some sort of error here.
3122 if ( $plugin_update_data === true ) {
3123 $status['error'] = __( 'Plugin update failed.' );
3124 wp_send_json_error( $status );
3127 $plugin_data = get_plugins( '/' . $result[ $plugin ]['destination_name'] );
3128 $plugin_data = reset( $plugin_data );
3130 if ( $plugin_data['Version'] ) {
3131 $status['newVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
3134 wp_send_json_success( $status );
3135 } else if ( is_wp_error( $result ) ) {
3136 $status['error'] = $result->get_error_message();
3137 wp_send_json_error( $status );
3139 } else if ( is_bool( $result ) && ! $result ) {
3140 $status['errorCode'] = 'unable_to_connect_to_filesystem';
3141 $status['error'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
3143 // Pass through the error from WP_Filesystem if one was raised
3144 if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
3145 $status['error'] = $wp_filesystem->errors->get_error_message();
3148 wp_send_json_error( $status );
3151 // An unhandled error occured
3152 $status['error'] = __( 'Plugin update failed.' );
3153 wp_send_json_error( $status );
3158 * AJAX handler for saving a post from Press This.
3162 * @global WP_Press_This $wp_press_this
3164 function wp_ajax_press_this_save_post() {
3165 if ( empty( $GLOBALS['wp_press_this'] ) ) {
3166 include( ABSPATH . 'wp-admin/includes/class-wp-press-this.php' );
3169 $GLOBALS['wp_press_this']->save_post();
3173 * AJAX handler for creating new category from Press This.
3177 * @global WP_Press_This $wp_press_this
3179 function wp_ajax_press_this_add_category() {
3180 if ( empty( $GLOBALS['wp_press_this'] ) ) {
3181 include( ABSPATH . 'wp-admin/includes/class-wp-press-this.php' );
3184 $GLOBALS['wp_press_this']->add_category();
3188 * AJAX handler for cropping an image.
3192 * @global WP_Site_Icon $wp_site_icon
3194 function wp_ajax_crop_image() {
3195 $attachment_id = absint( $_POST['id'] );
3197 check_ajax_referer( 'image_editor-' . $attachment_id, 'nonce' );
3198 if ( ! current_user_can( 'customize' ) ) {
3199 wp_send_json_error();
3202 $context = str_replace( '_', '-', $_POST['context'] );
3203 $data = array_map( 'absint', $_POST['cropDetails'] );
3204 $cropped = wp_crop_image( $attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height'] );
3206 if ( ! $cropped || is_wp_error( $cropped ) ) {
3207 wp_send_json_error( array( 'message' => __( 'Image could not be processed.' ) ) );
3210 switch ( $context ) {
3212 require_once ABSPATH . '/wp-admin/includes/class-wp-site-icon.php';
3213 global $wp_site_icon;
3215 // Skip creating a new attachment if the attachment is a Site Icon.
3216 if ( get_post_meta( $attachment_id, '_wp_attachment_context', true ) == $context ) {
3218 // Delete the temporary cropped file, we don't need it.
3219 wp_delete_file( $cropped );
3221 // Additional sizes in wp_prepare_attachment_for_js().
3222 add_filter( 'image_size_names_choose', array( $wp_site_icon, 'additional_sizes' ) );
3226 /** This filter is documented in wp-admin/custom-header.php */
3227 $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.
3228 $object = $wp_site_icon->create_attachment_object( $cropped, $attachment_id );
3229 unset( $object['ID'] );
3231 // Update the attachment.
3232 add_filter( 'intermediate_image_sizes_advanced', array( $wp_site_icon, 'additional_sizes' ) );
3233 $attachment_id = $wp_site_icon->insert_attachment( $object, $cropped );
3234 remove_filter( 'intermediate_image_sizes_advanced', array( $wp_site_icon, 'additional_sizes' ) );
3236 // Additional sizes in wp_prepare_attachment_for_js().
3237 add_filter( 'image_size_names_choose', array( $wp_site_icon, 'additional_sizes' ) );
3243 * Fires before a cropped image is saved.
3245 * Allows to add filters to modify the way a cropped image is saved.
3249 * @param string $context The Customizer control requesting the cropped image.
3250 * @param int $attachment_id The attachment ID of the original image.
3251 * @param string $cropped Path to the cropped image file.
3253 do_action( 'wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped );
3255 /** This filter is documented in wp-admin/custom-header.php */
3256 $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.
3258 $parent_url = wp_get_attachment_url( $attachment_id );
3259 $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
3261 $size = @getimagesize( $cropped );
3262 $image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
3265 'post_title' => basename( $cropped ),
3266 'post_content' => $url,
3267 'post_mime_type' => $image_type,
3269 'context' => $context,
3272 $attachment_id = wp_insert_attachment( $object, $cropped );
3273 $metadata = wp_generate_attachment_metadata( $attachment_id, $cropped );
3276 * Filter the cropped image attachment metadata.
3280 * @see wp_generate_attachment_metadata()
3282 * @param array $metadata Attachment metadata.
3284 $metadata = apply_filters( 'wp_ajax_cropped_attachment_metadata', $metadata );
3285 wp_update_attachment_metadata( $attachment_id, $metadata );
3288 * Filter the attachment ID for a cropped image.
3292 * @param int $attachment_id The attachment ID of the cropped image.
3293 * @param string $context The Customizer control requesting the cropped image.
3295 $attachment_id = apply_filters( 'wp_ajax_cropped_attachment_id', $attachment_id, $context );
3298 wp_send_json_success( wp_prepare_attachment_for_js( $attachment_id ) );
3302 * Ajax handler for generating a password.
3306 function wp_ajax_generate_password() {
3307 wp_send_json_success( wp_generate_password( 24 ) );
3311 * Ajax handler for saving the user's WordPress.org username.
3315 function wp_ajax_save_wporg_username() {
3316 if ( ! current_user_can( 'install_themes' ) && ! current_user_can( 'install_plugins' ) ) {
3317 wp_send_json_error();
3320 check_ajax_referer( 'save_wporg_username_' . get_current_user_id() );
3322 $username = isset( $_REQUEST['username'] ) ? wp_unslash( $_REQUEST['username'] ) : false;
3324 if ( ! $username ) {
3325 wp_send_json_error();
3328 wp_send_json_success( update_user_meta( get_current_user_id(), 'wporg_favorites', $username ) );