3 * WordPress Core Ajax Handlers.
6 * @subpackage Administration
10 // No-privilege Ajax handlers.
14 * Ajax handler for the Heartbeat API in
15 * the no-privilege context.
17 * Runs when the user is not logged in.
21 function wp_ajax_nopriv_heartbeat() {
24 // screen_id is the same as $current_screen->id and the JS global 'pagenow'.
25 if ( ! empty($_POST['screen_id']) )
26 $screen_id = sanitize_key($_POST['screen_id']);
30 if ( ! empty($_POST['data']) ) {
31 $data = wp_unslash( (array) $_POST['data'] );
34 * Filter Heartbeat AJAX response in no-privilege environments.
38 * @param array|object $response The no-priv Heartbeat response object or array.
39 * @param array $data An array of data passed via $_POST.
40 * @param string $screen_id The screen id.
42 $response = apply_filters( 'heartbeat_nopriv_received', $response, $data, $screen_id );
46 * Filter Heartbeat AJAX response when no data is passed.
50 * @param array|object $response The Heartbeat response object or array.
51 * @param string $screen_id The screen id.
53 $response = apply_filters( 'heartbeat_nopriv_send', $response, $screen_id );
56 * Fires when Heartbeat ticks in no-privilege environments.
58 * Allows the transport to be easily replaced with long-polling.
62 * @param array|object $response The no-priv Heartbeat response.
63 * @param string $screen_id The screen id.
65 do_action( 'heartbeat_nopriv_tick', $response, $screen_id );
67 // Send the current time according to the server.
68 $response['server_time'] = time();
70 wp_send_json($response);
74 // GET-based Ajax handlers.
78 * Ajax handler for fetching a list table.
82 function wp_ajax_fetch_list() {
83 global $wp_list_table;
85 $list_class = $_GET['list_args']['class'];
86 check_ajax_referer( "fetch-list-$list_class", '_ajax_fetch_list_nonce' );
88 $wp_list_table = _get_list_table( $list_class, array( 'screen' => $_GET['list_args']['screen']['id'] ) );
89 if ( ! $wp_list_table )
92 if ( ! $wp_list_table->ajax_user_can() )
95 $wp_list_table->ajax_response();
101 * Ajax handler for tag search.
105 function wp_ajax_ajax_tag_search() {
106 if ( ! isset( $_GET['tax'] ) ) {
110 $taxonomy = sanitize_key( $_GET['tax'] );
111 $tax = get_taxonomy( $taxonomy );
116 if ( ! current_user_can( $tax->cap->assign_terms ) ) {
120 $s = wp_unslash( $_GET['q'] );
122 $comma = _x( ',', 'tag delimiter' );
123 if ( ',' !== $comma )
124 $s = str_replace( $comma, ',', $s );
125 if ( false !== strpos( $s, ',' ) ) {
126 $s = explode( ',', $s );
127 $s = $s[count( $s ) - 1];
132 * Filter the minimum number of characters required to fire a tag search via AJAX.
136 * @param int $characters The minimum number of characters required. Default 2.
137 * @param object $tax The taxonomy object.
138 * @param string $s The search term.
140 $term_search_min_chars = (int) apply_filters( 'term_search_min_chars', 2, $tax, $s );
143 * Require $term_search_min_chars chars for matching (default: 2)
144 * ensure it's a non-negative, non-zero integer.
146 if ( ( $term_search_min_chars == 0 ) || ( strlen( $s ) < $term_search_min_chars ) ){
150 $results = get_terms( $taxonomy, array( 'name__like' => $s, 'fields' => 'names', 'hide_empty' => false ) );
152 echo join( $results, "\n" );
157 * Ajax handler for compression testing.
161 function wp_ajax_wp_compression_test() {
162 if ( !current_user_can( 'manage_options' ) )
165 if ( ini_get('zlib.output_compression') || 'ob_gzhandler' == ini_get('output_handler') ) {
166 update_site_option('can_compress_scripts', 0);
170 if ( isset($_GET['test']) ) {
171 header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' );
172 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
173 header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
174 header( 'Pragma: no-cache' );
175 header('Content-Type: application/x-javascript; charset=UTF-8');
176 $force_gzip = ( defined('ENFORCE_GZIP') && ENFORCE_GZIP );
177 $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."';
179 if ( 1 == $_GET['test'] ) {
182 } elseif ( 2 == $_GET['test'] ) {
183 if ( !isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
185 if ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') && function_exists('gzdeflate') && ! $force_gzip ) {
186 header('Content-Encoding: deflate');
187 $out = gzdeflate( $test_str, 1 );
188 } elseif ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') && function_exists('gzencode') ) {
189 header('Content-Encoding: gzip');
190 $out = gzencode( $test_str, 1 );
196 } elseif ( 'no' == $_GET['test'] ) {
197 update_site_option('can_compress_scripts', 0);
198 } elseif ( 'yes' == $_GET['test'] ) {
199 update_site_option('can_compress_scripts', 1);
207 * Ajax handler for image editor previews.
211 function wp_ajax_imgedit_preview() {
212 $post_id = intval($_GET['postid']);
213 if ( empty($post_id) || !current_user_can('edit_post', $post_id) )
216 check_ajax_referer( "image_editor-$post_id" );
218 include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
219 if ( ! stream_preview_image($post_id) )
226 * Ajax handler for oEmbed caching.
230 function wp_ajax_oembed_cache() {
231 $GLOBALS['wp_embed']->cache_oembed( $_GET['post'] );
236 * Ajax handler for user autocomplete.
240 function wp_ajax_autocomplete_user() {
241 if ( ! is_multisite() || ! current_user_can( 'promote_users' ) || wp_is_large_network( 'users' ) )
244 /** This filter is documented in wp-admin/user-new.php */
245 if ( ! is_super_admin() && ! apply_filters( 'autocomplete_users_for_site_admins', false ) )
250 // Check the type of request
251 // Current allowed values are `add` and `search`
252 if ( isset( $_REQUEST['autocomplete_type'] ) && 'search' === $_REQUEST['autocomplete_type'] ) {
253 $type = $_REQUEST['autocomplete_type'];
258 // Check the desired field for value
259 // Current allowed values are `user_email` and `user_login`
260 if ( isset( $_REQUEST['autocomplete_field'] ) && 'user_email' === $_REQUEST['autocomplete_field'] ) {
261 $field = $_REQUEST['autocomplete_field'];
263 $field = 'user_login';
266 // Exclude current users of this blog
267 if ( isset( $_REQUEST['site_id'] ) ) {
268 $id = absint( $_REQUEST['site_id'] );
270 $id = get_current_blog_id();
273 $include_blog_users = ( $type == 'search' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
274 $exclude_blog_users = ( $type == 'add' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
276 $users = get_users( array(
278 'search' => '*' . $_REQUEST['term'] . '*',
279 'include' => $include_blog_users,
280 'exclude' => $exclude_blog_users,
281 'search_columns' => array( 'user_login', 'user_nicename', 'user_email' ),
284 foreach ( $users as $user ) {
286 /* translators: 1: user_login, 2: user_email */
287 'label' => sprintf( __( '%1$s (%2$s)' ), $user->user_login, $user->user_email ),
288 'value' => $user->$field,
292 wp_die( wp_json_encode( $return ) );
296 * Ajax handler for dashboard widgets.
300 function wp_ajax_dashboard_widgets() {
301 require_once ABSPATH . 'wp-admin/includes/dashboard.php';
303 $pagenow = $_GET['pagenow'];
304 if ( $pagenow === 'dashboard-user' || $pagenow === 'dashboard-network' || $pagenow === 'dashboard' ) {
305 set_current_screen( $pagenow );
308 switch ( $_GET['widget'] ) {
309 case 'dashboard_primary' :
310 wp_dashboard_primary();
317 * Ajax handler for Customizer preview logged-in status.
321 function wp_ajax_logged_in() {
330 * Sends back current comment total and new page links if they need to be updated.
332 * Contrary to normal success AJAX response ("1"), die with time() on success.
336 * @param int $comment_id
339 function _wp_ajax_delete_comment_response( $comment_id, $delta = -1 ) {
340 $total = isset( $_POST['_total'] ) ? (int) $_POST['_total'] : 0;
341 $per_page = isset( $_POST['_per_page'] ) ? (int) $_POST['_per_page'] : 0;
342 $page = isset( $_POST['_page'] ) ? (int) $_POST['_page'] : 0;
343 $url = isset( $_POST['_url'] ) ? esc_url_raw( $_POST['_url'] ) : '';
345 // JS didn't send us everything we need to know. Just die with success message
346 if ( !$total || !$per_page || !$page || !$url )
353 // Only do the expensive stuff on a page-break, and about 1 other time per page
354 if ( 0 == $total % $per_page || 1 == mt_rand( 1, $per_page ) ) {
356 $status = 'total_comments'; // What type of comment count are we looking for?
357 $parsed = parse_url( $url );
358 if ( isset( $parsed['query'] ) ) {
359 parse_str( $parsed['query'], $query_vars );
360 if ( !empty( $query_vars['comment_status'] ) )
361 $status = $query_vars['comment_status'];
362 if ( !empty( $query_vars['p'] ) )
363 $post_id = (int) $query_vars['p'];
366 $comment_count = wp_count_comments($post_id);
368 // We're looking for a known type of comment count.
369 if ( isset( $comment_count->$status ) )
370 $total = $comment_count->$status;
371 // Else use the decremented value from above.
374 // The time since the last comment count.
377 $x = new WP_Ajax_Response( array(
379 // Here for completeness - not used.
381 'supplemental' => array(
382 'total_items_i18n' => sprintf( _n( '1 item', '%s items', $total ), number_format_i18n( $total ) ),
383 'total_pages' => ceil( $total / $per_page ),
384 'total_pages_i18n' => number_format_i18n( ceil( $total / $per_page ) ),
393 // POST-based Ajax handlers.
397 * Ajax handler for adding a hierarchical term.
401 function _wp_ajax_add_hierarchical_term() {
402 $action = $_POST['action'];
403 $taxonomy = get_taxonomy(substr($action, 4));
404 check_ajax_referer( $action, '_ajax_nonce-add-' . $taxonomy->name );
405 if ( !current_user_can( $taxonomy->cap->edit_terms ) )
407 $names = explode(',', $_POST['new'.$taxonomy->name]);
408 $parent = isset($_POST['new'.$taxonomy->name.'_parent']) ? (int) $_POST['new'.$taxonomy->name.'_parent'] : 0;
411 if ( $taxonomy->name == 'category' )
412 $post_category = isset($_POST['post_category']) ? (array) $_POST['post_category'] : array();
414 $post_category = ( isset($_POST['tax_input']) && isset($_POST['tax_input'][$taxonomy->name]) ) ? (array) $_POST['tax_input'][$taxonomy->name] : array();
415 $checked_categories = array_map( 'absint', (array) $post_category );
416 $popular_ids = wp_popular_terms_checklist($taxonomy->name, 0, 10, false);
418 foreach ( $names as $cat_name ) {
419 $cat_name = trim($cat_name);
420 $category_nicename = sanitize_title($cat_name);
421 if ( '' === $category_nicename )
423 if ( !$cat_id = term_exists( $cat_name, $taxonomy->name, $parent ) )
424 $cat_id = wp_insert_term( $cat_name, $taxonomy->name, array( 'parent' => $parent ) );
425 if ( is_wp_error( $cat_id ) )
427 else if ( is_array( $cat_id ) )
428 $cat_id = $cat_id['term_id'];
429 $checked_categories[] = $cat_id;
430 if ( $parent ) // Do these all at once in a second
433 wp_terms_checklist( 0, array( 'taxonomy' => $taxonomy->name, 'descendants_and_self' => $cat_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids ));
434 $data = ob_get_contents();
437 'what' => $taxonomy->name,
439 'data' => str_replace( array("\n", "\t"), '', $data),
444 if ( $parent ) { // Foncy - replace the parent and all its children
445 $parent = get_term( $parent, $taxonomy->name );
446 $term_id = $parent->term_id;
448 while ( $parent->parent ) { // get the top parent
449 $parent = get_term( $parent->parent, $taxonomy->name );
450 if ( is_wp_error( $parent ) )
452 $term_id = $parent->term_id;
456 wp_terms_checklist( 0, array('taxonomy' => $taxonomy->name, 'descendants_and_self' => $term_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids));
457 $data = ob_get_contents();
460 'what' => $taxonomy->name,
462 'data' => str_replace( array("\n", "\t"), '', $data),
468 wp_dropdown_categories( array(
469 'taxonomy' => $taxonomy->name, 'hide_empty' => 0, 'name' => 'new'.$taxonomy->name.'_parent', 'orderby' => 'name',
470 'hierarchical' => 1, 'show_option_none' => '— '.$taxonomy->labels->parent_item.' —'
472 $sup = ob_get_contents();
474 $add['supplemental'] = array( 'newcat_parent' => $sup );
476 $x = new WP_Ajax_Response( $add );
481 * Ajax handler for deleting a comment.
485 function wp_ajax_delete_comment() {
486 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
488 if ( !$comment = get_comment( $id ) )
490 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
493 check_ajax_referer( "delete-comment_$id" );
494 $status = wp_get_comment_status( $comment->comment_ID );
497 if ( isset($_POST['trash']) && 1 == $_POST['trash'] ) {
498 if ( 'trash' == $status )
500 $r = wp_trash_comment( $comment->comment_ID );
501 } elseif ( isset($_POST['untrash']) && 1 == $_POST['untrash'] ) {
502 if ( 'trash' != $status )
504 $r = wp_untrash_comment( $comment->comment_ID );
505 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'trash' ) // undo trash, not in trash
507 } elseif ( isset($_POST['spam']) && 1 == $_POST['spam'] ) {
508 if ( 'spam' == $status )
510 $r = wp_spam_comment( $comment->comment_ID );
511 } elseif ( isset($_POST['unspam']) && 1 == $_POST['unspam'] ) {
512 if ( 'spam' != $status )
514 $r = wp_unspam_comment( $comment->comment_ID );
515 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'spam' ) // undo spam, not in spam
517 } elseif ( isset($_POST['delete']) && 1 == $_POST['delete'] ) {
518 $r = wp_delete_comment( $comment->comment_ID );
523 if ( $r ) // Decide if we need to send back '1' or a more complicated response including page links and comment counts
524 _wp_ajax_delete_comment_response( $comment->comment_ID, $delta );
529 * Ajax handler for deleting a tag.
533 function wp_ajax_delete_tag() {
534 $tag_id = (int) $_POST['tag_ID'];
535 check_ajax_referer( "delete-tag_$tag_id" );
537 $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
538 $tax = get_taxonomy($taxonomy);
540 if ( !current_user_can( $tax->cap->delete_terms ) )
543 $tag = get_term( $tag_id, $taxonomy );
544 if ( !$tag || is_wp_error( $tag ) )
547 if ( wp_delete_term($tag_id, $taxonomy))
554 * Ajax handler for deleting a link.
558 function wp_ajax_delete_link() {
559 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
561 check_ajax_referer( "delete-bookmark_$id" );
562 if ( !current_user_can( 'manage_links' ) )
565 $link = get_bookmark( $id );
566 if ( !$link || is_wp_error( $link ) )
569 if ( wp_delete_link( $id ) )
576 * Ajax handler for deleting meta.
580 function wp_ajax_delete_meta() {
581 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
583 check_ajax_referer( "delete-meta_$id" );
584 if ( !$meta = get_metadata_by_mid( 'post', $id ) )
587 if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta', $meta->post_id, $meta->meta_key ) )
589 if ( delete_meta( $meta->meta_id ) )
595 * Ajax handler for deleting a post.
599 * @param string $action Action to perform.
601 function wp_ajax_delete_post( $action ) {
602 if ( empty( $action ) )
603 $action = 'delete-post';
604 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
606 check_ajax_referer( "{$action}_$id" );
607 if ( !current_user_can( 'delete_post', $id ) )
610 if ( !get_post( $id ) )
613 if ( wp_delete_post( $id ) )
620 * Ajax handler for sending a post to the trash.
624 * @param string $action Action to perform.
626 function wp_ajax_trash_post( $action ) {
627 if ( empty( $action ) )
628 $action = 'trash-post';
629 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
631 check_ajax_referer( "{$action}_$id" );
632 if ( !current_user_can( 'delete_post', $id ) )
635 if ( !get_post( $id ) )
638 if ( 'trash-post' == $action )
639 $done = wp_trash_post( $id );
641 $done = wp_untrash_post( $id );
650 * Ajax handler to restore a post from the trash.
654 * @param string $action Action to perform.
656 function wp_ajax_untrash_post( $action ) {
657 if ( empty( $action ) )
658 $action = 'untrash-post';
659 wp_ajax_trash_post( $action );
662 function wp_ajax_delete_page( $action ) {
663 if ( empty( $action ) )
664 $action = 'delete-page';
665 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
667 check_ajax_referer( "{$action}_$id" );
668 if ( !current_user_can( 'delete_page', $id ) )
671 if ( ! get_post( $id ) )
674 if ( wp_delete_post( $id ) )
681 * Ajax handler to dim a comment.
685 function wp_ajax_dim_comment() {
686 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
688 if ( !$comment = get_comment( $id ) ) {
689 $x = new WP_Ajax_Response( array(
691 'id' => new WP_Error('invalid_comment', sprintf(__('Comment %d does not exist'), $id))
696 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) && ! current_user_can( 'moderate_comments' ) )
699 $current = wp_get_comment_status( $comment->comment_ID );
700 if ( isset( $_POST['new'] ) && $_POST['new'] == $current )
703 check_ajax_referer( "approve-comment_$id" );
704 if ( in_array( $current, array( 'unapproved', 'spam' ) ) )
705 $result = wp_set_comment_status( $comment->comment_ID, 'approve', true );
707 $result = wp_set_comment_status( $comment->comment_ID, 'hold', true );
709 if ( is_wp_error($result) ) {
710 $x = new WP_Ajax_Response( array(
717 // Decide if we need to send back '1' or a more complicated response including page links and comment counts
718 _wp_ajax_delete_comment_response( $comment->comment_ID );
723 * Ajax handler for deleting a link category.
727 * @param string $action Action to perform.
729 function wp_ajax_add_link_category( $action ) {
730 if ( empty( $action ) )
731 $action = 'add-link-category';
732 check_ajax_referer( $action );
733 if ( !current_user_can( 'manage_categories' ) )
735 $names = explode(',', wp_unslash( $_POST['newcat'] ) );
736 $x = new WP_Ajax_Response();
737 foreach ( $names as $cat_name ) {
738 $cat_name = trim($cat_name);
739 $slug = sanitize_title($cat_name);
742 if ( !$cat_id = term_exists( $cat_name, 'link_category' ) )
743 $cat_id = wp_insert_term( $cat_name, 'link_category' );
744 if ( is_wp_error( $cat_id ) )
746 else if ( is_array( $cat_id ) )
747 $cat_id = $cat_id['term_id'];
748 $cat_name = esc_html( $cat_name );
750 'what' => 'link-category',
752 '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>",
760 * Ajax handler to add a tag.
764 function wp_ajax_add_tag() {
765 global $wp_list_table;
767 check_ajax_referer( 'add-tag', '_wpnonce_add-tag' );
768 $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
769 $tax = get_taxonomy($taxonomy);
771 if ( !current_user_can( $tax->cap->edit_terms ) )
774 $x = new WP_Ajax_Response();
776 $tag = wp_insert_term($_POST['tag-name'], $taxonomy, $_POST );
778 if ( !$tag || is_wp_error($tag) || (!$tag = get_term( $tag['term_id'], $taxonomy )) ) {
779 $message = __('An error has occurred. Please reload the page and try again.');
780 if ( is_wp_error($tag) && $tag->get_error_message() )
781 $message = $tag->get_error_message();
784 'what' => 'taxonomy',
785 'data' => new WP_Error('error', $message )
790 $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => $_POST['screen'] ) );
793 if ( is_taxonomy_hierarchical($taxonomy) ) {
794 $level = count( get_ancestors( $tag->term_id, $taxonomy, 'taxonomy' ) );
796 $wp_list_table->single_row( $tag, $level );
797 $noparents = ob_get_clean();
801 $wp_list_table->single_row( $tag );
802 $parents = ob_get_clean();
805 'what' => 'taxonomy',
806 'supplemental' => compact('parents', 'noparents')
810 'position' => $level,
811 'supplemental' => (array) $tag
817 * Ajax handler for getting a tagcloud.
821 function wp_ajax_get_tagcloud() {
822 if ( ! isset( $_POST['tax'] ) ) {
826 $taxonomy = sanitize_key( $_POST['tax'] );
827 $tax = get_taxonomy( $taxonomy );
832 if ( ! current_user_can( $tax->cap->assign_terms ) ) {
836 $tags = get_terms( $taxonomy, array( 'number' => 45, 'orderby' => 'count', 'order' => 'DESC' ) );
838 if ( empty( $tags ) )
839 wp_die( $tax->labels->not_found );
841 if ( is_wp_error( $tags ) )
842 wp_die( $tags->get_error_message() );
844 foreach ( $tags as $key => $tag ) {
845 $tags[ $key ]->link = '#';
846 $tags[ $key ]->id = $tag->term_id;
849 // We need raw tag names here, so don't filter the output
850 $return = wp_generate_tag_cloud( $tags, array('filter' => 0) );
852 if ( empty($return) )
861 * Ajax handler for getting comments.
865 * @param string $action Action to perform.
867 function wp_ajax_get_comments( $action ) {
868 global $wp_list_table, $post_id;
869 if ( empty( $action ) )
870 $action = 'get-comments';
872 check_ajax_referer( $action );
874 if ( empty( $post_id ) && ! empty( $_REQUEST['p'] ) ) {
875 $id = absint( $_REQUEST['p'] );
876 if ( ! empty( $id ) )
880 if ( empty( $post_id ) )
883 $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
885 if ( ! current_user_can( 'edit_post', $post_id ) )
888 $wp_list_table->prepare_items();
890 if ( !$wp_list_table->has_items() )
893 $x = new WP_Ajax_Response();
895 foreach ( $wp_list_table->items as $comment ) {
896 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
898 get_comment( $comment );
899 $wp_list_table->single_row( $comment );
901 $comment_list_item = ob_get_contents();
905 'what' => 'comments',
906 'data' => $comment_list_item
912 * Ajax handler for replying to a comment.
916 * @param string $action Action to perform.
918 function wp_ajax_replyto_comment( $action ) {
919 global $wp_list_table;
920 if ( empty( $action ) )
921 $action = 'replyto-comment';
923 check_ajax_referer( $action, '_ajax_nonce-replyto-comment' );
925 $comment_post_ID = (int) $_POST['comment_post_ID'];
926 $post = get_post( $comment_post_ID );
930 if ( !current_user_can( 'edit_post', $comment_post_ID ) )
933 if ( empty( $post->post_status ) )
935 elseif ( in_array($post->post_status, array('draft', 'pending', 'trash') ) )
936 wp_die( __('ERROR: you are replying to a comment on a draft post.') );
938 $user = wp_get_current_user();
939 if ( $user->exists() ) {
940 $user_ID = $user->ID;
941 $comment_author = wp_slash( $user->display_name );
942 $comment_author_email = wp_slash( $user->user_email );
943 $comment_author_url = wp_slash( $user->user_url );
944 $comment_content = trim( $_POST['content'] );
945 $comment_type = isset( $_POST['comment_type'] ) ? trim( $_POST['comment_type'] ) : '';
946 if ( current_user_can( 'unfiltered_html' ) ) {
947 if ( ! isset( $_POST['_wp_unfiltered_html_comment'] ) )
948 $_POST['_wp_unfiltered_html_comment'] = '';
950 if ( wp_create_nonce( 'unfiltered-html-comment' ) != $_POST['_wp_unfiltered_html_comment'] ) {
951 kses_remove_filters(); // start with a clean slate
952 kses_init_filters(); // set up the filters
956 wp_die( __( 'Sorry, you must be logged in to reply to a comment.' ) );
959 if ( '' == $comment_content )
960 wp_die( __( 'ERROR: please type a comment.' ) );
963 if ( isset( $_POST['comment_ID'] ) )
964 $comment_parent = absint( $_POST['comment_ID'] );
965 $comment_auto_approved = false;
966 $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID');
968 // Automatically approve parent comment.
969 if ( !empty($_POST['approve_parent']) ) {
970 $parent = get_comment( $comment_parent );
972 if ( $parent && $parent->comment_approved === '0' && $parent->comment_post_ID == $comment_post_ID ) {
973 if ( wp_set_comment_status( $parent->comment_ID, 'approve' ) )
974 $comment_auto_approved = true;
978 $comment_id = wp_new_comment( $commentdata );
979 $comment = get_comment($comment_id);
980 if ( ! $comment ) wp_die( 1 );
982 $position = ( isset($_POST['position']) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1';
985 if ( isset( $_REQUEST['mode'] ) && 'dashboard' == $_REQUEST['mode'] ) {
986 require_once( ABSPATH . 'wp-admin/includes/dashboard.php' );
987 _wp_dashboard_recent_comments_row( $comment );
989 if ( isset( $_REQUEST['mode'] ) && 'single' == $_REQUEST['mode'] ) {
990 $wp_list_table = _get_list_table('WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
992 $wp_list_table = _get_list_table('WP_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
994 $wp_list_table->single_row( $comment );
996 $comment_list_item = ob_get_clean();
1000 'id' => $comment->comment_ID,
1001 'data' => $comment_list_item,
1002 'position' => $position
1005 if ( $comment_auto_approved )
1006 $response['supplemental'] = array( 'parent_approved' => $parent->comment_ID );
1008 $x = new WP_Ajax_Response();
1009 $x->add( $response );
1014 * Ajax handler for editing a comment.
1018 function wp_ajax_edit_comment() {
1019 global $wp_list_table;
1021 check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' );
1023 $comment_id = (int) $_POST['comment_ID'];
1024 if ( ! current_user_can( 'edit_comment', $comment_id ) )
1027 if ( '' == $_POST['content'] )
1028 wp_die( __( 'ERROR: please type a comment.' ) );
1030 if ( isset( $_POST['status'] ) )
1031 $_POST['comment_status'] = $_POST['status'];
1034 $position = ( isset($_POST['position']) && (int) $_POST['position']) ? (int) $_POST['position'] : '-1';
1035 $checkbox = ( isset($_POST['checkbox']) && true == $_POST['checkbox'] ) ? 1 : 0;
1036 $wp_list_table = _get_list_table( $checkbox ? 'WP_Comments_List_Table' : 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
1038 $comment = get_comment( $comment_id );
1039 if ( empty( $comment->comment_ID ) )
1043 $wp_list_table->single_row( $comment );
1044 $comment_list_item = ob_get_clean();
1046 $x = new WP_Ajax_Response();
1049 'what' => 'edit_comment',
1050 'id' => $comment->comment_ID,
1051 'data' => $comment_list_item,
1052 'position' => $position
1059 * Ajax handler for adding a menu item.
1063 function wp_ajax_add_menu_item() {
1064 check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1066 if ( ! current_user_can( 'edit_theme_options' ) )
1069 require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1071 // For performance reasons, we omit some object properties from the checklist.
1072 // The following is a hacky way to restore them when adding non-custom items.
1074 $menu_items_data = array();
1075 foreach ( (array) $_POST['menu-item'] as $menu_item_data ) {
1077 ! empty( $menu_item_data['menu-item-type'] ) &&
1078 'custom' != $menu_item_data['menu-item-type'] &&
1079 ! empty( $menu_item_data['menu-item-object-id'] )
1081 switch( $menu_item_data['menu-item-type'] ) {
1083 $_object = get_post( $menu_item_data['menu-item-object-id'] );
1087 $_object = get_term( $menu_item_data['menu-item-object-id'], $menu_item_data['menu-item-object'] );
1091 $_menu_items = array_map( 'wp_setup_nav_menu_item', array( $_object ) );
1092 $_menu_item = array_shift( $_menu_items );
1094 // Restore the missing menu item properties
1095 $menu_item_data['menu-item-description'] = $_menu_item->description;
1098 $menu_items_data[] = $menu_item_data;
1101 $item_ids = wp_save_nav_menu_items( 0, $menu_items_data );
1102 if ( is_wp_error( $item_ids ) )
1105 $menu_items = array();
1107 foreach ( (array) $item_ids as $menu_item_id ) {
1108 $menu_obj = get_post( $menu_item_id );
1109 if ( ! empty( $menu_obj->ID ) ) {
1110 $menu_obj = wp_setup_nav_menu_item( $menu_obj );
1111 $menu_obj->label = $menu_obj->title; // don't show "(pending)" in ajax-added items
1112 $menu_items[] = $menu_obj;
1116 /** This filter is documented in wp-admin/includes/nav-menu.php */
1117 $walker_class_name = apply_filters( 'wp_edit_nav_menu_walker', 'Walker_Nav_Menu_Edit', $_POST['menu'] );
1119 if ( ! class_exists( $walker_class_name ) )
1122 if ( ! empty( $menu_items ) ) {
1127 'link_before' => '',
1128 'walker' => new $walker_class_name,
1130 echo walk_nav_menu_tree( $menu_items, 0, (object) $args );
1136 * Ajax handler for adding meta.
1140 function wp_ajax_add_meta() {
1141 check_ajax_referer( 'add-meta', '_ajax_nonce-add-meta' );
1143 $pid = (int) $_POST['post_id'];
1144 $post = get_post( $pid );
1146 if ( isset($_POST['metakeyselect']) || isset($_POST['metakeyinput']) ) {
1147 if ( !current_user_can( 'edit_post', $pid ) )
1149 if ( isset($_POST['metakeyselect']) && '#NONE#' == $_POST['metakeyselect'] && empty($_POST['metakeyinput']) )
1152 // If the post is an autodraft, save the post as a draft and then attempt to save the meta.
1153 if ( $post->post_status == 'auto-draft' ) {
1154 $save_POST = $_POST; // Backup $_POST
1155 $_POST = array(); // Make it empty for edit_post()
1156 $_POST['action'] = 'draft'; // Warning fix
1157 $_POST['post_ID'] = $pid;
1158 $_POST['post_type'] = $post->post_type;
1159 $_POST['post_status'] = 'draft';
1160 $now = current_time('timestamp', 1);
1161 $_POST['post_title'] = sprintf( __( 'Draft created on %1$s at %2$s' ), date( get_option( 'date_format' ), $now ), date( get_option( 'time_format' ), $now ) );
1163 if ( $pid = edit_post() ) {
1164 if ( is_wp_error( $pid ) ) {
1165 $x = new WP_Ajax_Response( array(
1171 $_POST = $save_POST; // Now we can restore original $_POST again
1172 if ( !$mid = add_meta( $pid ) )
1173 wp_die( __( 'Please provide a custom field value.' ) );
1177 } else if ( !$mid = add_meta( $pid ) ) {
1178 wp_die( __( 'Please provide a custom field value.' ) );
1181 $meta = get_metadata_by_mid( 'post', $mid );
1182 $pid = (int) $meta->post_id;
1183 $meta = get_object_vars( $meta );
1184 $x = new WP_Ajax_Response( array(
1187 'data' => _list_meta_row( $meta, $c ),
1189 'supplemental' => array('postid' => $pid)
1192 $mid = (int) key( $_POST['meta'] );
1193 $key = wp_unslash( $_POST['meta'][$mid]['key'] );
1194 $value = wp_unslash( $_POST['meta'][$mid]['value'] );
1195 if ( '' == trim($key) )
1196 wp_die( __( 'Please provide a custom field name.' ) );
1197 if ( '' == trim($value) )
1198 wp_die( __( 'Please provide a custom field value.' ) );
1199 if ( ! $meta = get_metadata_by_mid( 'post', $mid ) )
1200 wp_die( 0 ); // if meta doesn't exist
1201 if ( is_protected_meta( $meta->meta_key, 'post' ) || is_protected_meta( $key, 'post' ) ||
1202 ! current_user_can( 'edit_post_meta', $meta->post_id, $meta->meta_key ) ||
1203 ! current_user_can( 'edit_post_meta', $meta->post_id, $key ) )
1205 if ( $meta->meta_value != $value || $meta->meta_key != $key ) {
1206 if ( !$u = update_metadata_by_mid( 'post', $mid, $value, $key ) )
1207 wp_die( 0 ); // We know meta exists; we also know it's unchanged (or DB error, in which case there are bigger problems).
1210 $x = new WP_Ajax_Response( array(
1212 'id' => $mid, 'old_id' => $mid,
1213 'data' => _list_meta_row( array(
1215 'meta_value' => $value,
1219 'supplemental' => array('postid' => $meta->post_id)
1226 * Ajax handler for adding a user.
1230 * @param string $action Action to perform.
1232 function wp_ajax_add_user( $action ) {
1233 global $wp_list_table;
1234 if ( empty( $action ) )
1235 $action = 'add-user';
1237 check_ajax_referer( $action );
1238 if ( ! current_user_can('create_users') )
1240 if ( ! $user_id = edit_user() ) {
1242 } elseif ( is_wp_error( $user_id ) ) {
1243 $x = new WP_Ajax_Response( array(
1249 $user_object = get_userdata( $user_id );
1251 $wp_list_table = _get_list_table('WP_Users_List_Table');
1253 $role = current( $user_object->roles );
1255 $x = new WP_Ajax_Response( array(
1258 'data' => $wp_list_table->single_row( $user_object, '', $role ),
1259 'supplemental' => array(
1260 'show-link' => sprintf(__( 'User <a href="#%s">%s</a> added' ), "user-$user_id", $user_object->user_login),
1268 * Ajax handler for closed post boxes.
1272 function wp_ajax_closed_postboxes() {
1273 check_ajax_referer( 'closedpostboxes', 'closedpostboxesnonce' );
1274 $closed = isset( $_POST['closed'] ) ? explode( ',', $_POST['closed']) : array();
1275 $closed = array_filter($closed);
1277 $hidden = isset( $_POST['hidden'] ) ? explode( ',', $_POST['hidden']) : array();
1278 $hidden = array_filter($hidden);
1280 $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1282 if ( $page != sanitize_key( $page ) )
1285 if ( ! $user = wp_get_current_user() )
1288 if ( is_array($closed) )
1289 update_user_option($user->ID, "closedpostboxes_$page", $closed, true);
1291 if ( is_array($hidden) ) {
1292 $hidden = array_diff( $hidden, array('submitdiv', 'linksubmitdiv', 'manage-menu', 'create-menu') ); // postboxes that are always shown
1293 update_user_option($user->ID, "metaboxhidden_$page", $hidden, true);
1300 * Ajax handler for hidden columns.
1304 function wp_ajax_hidden_columns() {
1305 check_ajax_referer( 'screen-options-nonce', 'screenoptionnonce' );
1306 $hidden = explode( ',', isset( $_POST['hidden'] ) ? $_POST['hidden'] : '' );
1307 $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1309 if ( $page != sanitize_key( $page ) )
1312 if ( ! $user = wp_get_current_user() )
1315 if ( is_array($hidden) )
1316 update_user_option($user->ID, "manage{$page}columnshidden", $hidden, true);
1322 * Ajax handler for updating whether to display the welcome panel.
1326 function wp_ajax_update_welcome_panel() {
1327 check_ajax_referer( 'welcome-panel-nonce', 'welcomepanelnonce' );
1329 if ( ! current_user_can( 'edit_theme_options' ) )
1332 update_user_meta( get_current_user_id(), 'show_welcome_panel', empty( $_POST['visible'] ) ? 0 : 1 );
1338 * Ajax handler for retrieving menu meta boxes.
1342 function wp_ajax_menu_get_metabox() {
1343 if ( ! current_user_can( 'edit_theme_options' ) )
1346 require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1348 if ( isset( $_POST['item-type'] ) && 'post_type' == $_POST['item-type'] ) {
1350 $callback = 'wp_nav_menu_item_post_type_meta_box';
1351 $items = (array) get_post_types( array( 'show_in_nav_menus' => true ), 'object' );
1352 } elseif ( isset( $_POST['item-type'] ) && 'taxonomy' == $_POST['item-type'] ) {
1354 $callback = 'wp_nav_menu_item_taxonomy_meta_box';
1355 $items = (array) get_taxonomies( array( 'show_ui' => true ), 'object' );
1358 if ( ! empty( $_POST['item-object'] ) && isset( $items[$_POST['item-object']] ) ) {
1359 $menus_meta_box_object = $items[ $_POST['item-object'] ];
1361 /** This filter is documented in wp-admin/includes/nav-menu.php */
1362 $item = apply_filters( 'nav_menu_meta_box_object', $menus_meta_box_object );
1364 call_user_func_array($callback, array(
1367 'id' => 'add-' . $item->name,
1368 'title' => $item->labels->name,
1369 'callback' => $callback,
1374 $markup = ob_get_clean();
1376 echo wp_json_encode(array(
1377 'replace-id' => $type . '-' . $item->name,
1378 'markup' => $markup,
1386 * Ajax handler for internal linking.
1390 function wp_ajax_wp_link_ajax() {
1391 check_ajax_referer( 'internal-linking', '_ajax_linking_nonce' );
1395 if ( isset( $_POST['search'] ) )
1396 $args['s'] = wp_unslash( $_POST['search'] );
1397 $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
1399 require(ABSPATH . WPINC . '/class-wp-editor.php');
1400 $results = _WP_Editors::wp_link_query( $args );
1402 if ( ! isset( $results ) )
1405 echo wp_json_encode( $results );
1412 * Ajax handler for menu locations save.
1416 function wp_ajax_menu_locations_save() {
1417 if ( ! current_user_can( 'edit_theme_options' ) )
1419 check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1420 if ( ! isset( $_POST['menu-locations'] ) )
1422 set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_POST['menu-locations'] ) );
1427 * Ajax handler for saving the meta box order.
1431 function wp_ajax_meta_box_order() {
1432 check_ajax_referer( 'meta-box-order' );
1433 $order = isset( $_POST['order'] ) ? (array) $_POST['order'] : false;
1434 $page_columns = isset( $_POST['page_columns'] ) ? $_POST['page_columns'] : 'auto';
1436 if ( $page_columns != 'auto' )
1437 $page_columns = (int) $page_columns;
1439 $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1441 if ( $page != sanitize_key( $page ) )
1444 if ( ! $user = wp_get_current_user() )
1448 update_user_option($user->ID, "meta-box-order_$page", $order, true);
1450 if ( $page_columns )
1451 update_user_option($user->ID, "screen_layout_$page", $page_columns, true);
1457 * Ajax handler for menu quick searching.
1461 function wp_ajax_menu_quick_search() {
1462 if ( ! current_user_can( 'edit_theme_options' ) )
1465 require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1467 _wp_ajax_menu_quick_search( $_POST );
1473 * Ajax handler to retrieve a permalink.
1477 function wp_ajax_get_permalink() {
1478 check_ajax_referer( 'getpermalink', 'getpermalinknonce' );
1479 $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1480 wp_die( add_query_arg( array( 'preview' => 'true' ), get_permalink( $post_id ) ) );
1484 * Ajax handler to retrieve a sample permalink.
1488 function wp_ajax_sample_permalink() {
1489 check_ajax_referer( 'samplepermalink', 'samplepermalinknonce' );
1490 $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1491 $title = isset($_POST['new_title'])? $_POST['new_title'] : '';
1492 $slug = isset($_POST['new_slug'])? $_POST['new_slug'] : null;
1493 wp_die( get_sample_permalink_html( $post_id, $title, $slug ) );
1497 * Ajax handler for Quick Edit saving a post from a list table.
1501 function wp_ajax_inline_save() {
1502 global $wp_list_table;
1504 check_ajax_referer( 'inlineeditnonce', '_inline_edit' );
1506 if ( ! isset($_POST['post_ID']) || ! ( $post_ID = (int) $_POST['post_ID'] ) )
1509 if ( 'page' == $_POST['post_type'] ) {
1510 if ( ! current_user_can( 'edit_page', $post_ID ) )
1511 wp_die( __( 'You are not allowed to edit this page.' ) );
1513 if ( ! current_user_can( 'edit_post', $post_ID ) )
1514 wp_die( __( 'You are not allowed to edit this post.' ) );
1517 if ( $last = wp_check_post_lock( $post_ID ) ) {
1518 $last_user = get_userdata( $last );
1519 $last_user_name = $last_user ? $last_user->display_name : __( 'Someone' );
1520 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 ) );
1526 $post = get_post( $post_ID, ARRAY_A );
1528 // Since it's coming from the database.
1529 $post = wp_slash($post);
1531 $data['content'] = $post['post_content'];
1532 $data['excerpt'] = $post['post_excerpt'];
1535 $data['user_ID'] = get_current_user_id();
1537 if ( isset($data['post_parent']) )
1538 $data['parent_id'] = $data['post_parent'];
1541 if ( isset($data['keep_private']) && 'private' == $data['keep_private'] )
1542 $data['post_status'] = 'private';
1544 $data['post_status'] = $data['_status'];
1546 if ( empty($data['comment_status']) )
1547 $data['comment_status'] = 'closed';
1548 if ( empty($data['ping_status']) )
1549 $data['ping_status'] = 'closed';
1551 // Hack: wp_unique_post_slug() doesn't work for drafts, so we will fake that our post is published.
1552 if ( ! empty( $data['post_name'] ) && in_array( $post['post_status'], array( 'draft', 'pending' ) ) ) {
1553 $post['post_status'] = 'publish';
1554 $data['post_name'] = wp_unique_post_slug( $data['post_name'], $post['ID'], $post['post_status'], $post['post_type'], $post['post_parent'] );
1560 $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => $_POST['screen'] ) );
1563 $request_post = array( get_post( $_POST['post_ID'] ) );
1564 $parent = $request_post[0]->post_parent;
1566 while ( $parent > 0 ) {
1567 $parent_post = get_post( $parent );
1568 $parent = $parent_post->post_parent;
1572 $wp_list_table->display_rows( array( get_post( $_POST['post_ID'] ) ), $level );
1578 * Ajax handler for quick edit saving for a term.
1582 function wp_ajax_inline_save_tax() {
1583 global $wp_list_table;
1585 check_ajax_referer( 'taxinlineeditnonce', '_inline_edit' );
1587 $taxonomy = sanitize_key( $_POST['taxonomy'] );
1588 $tax = get_taxonomy( $taxonomy );
1592 if ( ! current_user_can( $tax->cap->edit_terms ) )
1595 $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => 'edit-' . $taxonomy ) );
1597 if ( ! isset($_POST['tax_ID']) || ! ( $id = (int) $_POST['tax_ID'] ) )
1600 $tag = get_term( $id, $taxonomy );
1601 $_POST['description'] = $tag->description;
1603 $updated = wp_update_term($id, $taxonomy, $_POST);
1604 if ( $updated && !is_wp_error($updated) ) {
1605 $tag = get_term( $updated['term_id'], $taxonomy );
1606 if ( !$tag || is_wp_error( $tag ) ) {
1607 if ( is_wp_error($tag) && $tag->get_error_message() )
1608 wp_die( $tag->get_error_message() );
1609 wp_die( __( 'Item not updated.' ) );
1612 if ( is_wp_error($updated) && $updated->get_error_message() )
1613 wp_die( $updated->get_error_message() );
1614 wp_die( __( 'Item not updated.' ) );
1617 $parent = $tag->parent;
1618 while ( $parent > 0 ) {
1619 $parent_tag = get_term( $parent, $taxonomy );
1620 $parent = $parent_tag->parent;
1623 $wp_list_table->single_row( $tag, $level );
1628 * Ajax handler for querying posts for the Find Posts modal.
1630 * @see window.findPosts
1634 function wp_ajax_find_posts() {
1635 check_ajax_referer( 'find-posts' );
1637 $post_types = get_post_types( array( 'public' => true ), 'objects' );
1638 unset( $post_types['attachment'] );
1640 $s = wp_unslash( $_POST['ps'] );
1642 'post_type' => array_keys( $post_types ),
1643 'post_status' => 'any',
1644 'posts_per_page' => 50,
1649 $posts = get_posts( $args );
1652 wp_send_json_error( __( 'No items found.' ) );
1655 $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>';
1657 foreach ( $posts as $post ) {
1658 $title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
1659 $alt = ( 'alternate' == $alt ) ? '' : 'alternate';
1661 switch ( $post->post_status ) {
1664 $stat = __('Published');
1667 $stat = __('Scheduled');
1670 $stat = __('Pending Review');
1673 $stat = __('Draft');
1677 if ( '0000-00-00 00:00:00' == $post->post_date ) {
1680 /* translators: date format in table columns, see http://php.net/date */
1681 $time = mysql2date(__('Y/m/d'), $post->post_date);
1684 $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>';
1685 $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";
1688 $html .= '</tbody></table>';
1690 wp_send_json_success( $html );
1694 * Ajax handler for saving the widgets order.
1698 function wp_ajax_widgets_order() {
1699 check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1701 if ( !current_user_can('edit_theme_options') )
1704 unset( $_POST['savewidgets'], $_POST['action'] );
1706 // Save widgets order for all sidebars.
1707 if ( is_array($_POST['sidebars']) ) {
1708 $sidebars = array();
1709 foreach ( $_POST['sidebars'] as $key => $val ) {
1711 if ( !empty($val) ) {
1712 $val = explode(',', $val);
1713 foreach ( $val as $k => $v ) {
1714 if ( strpos($v, 'widget-') === false )
1717 $sb[$k] = substr($v, strpos($v, '_') + 1);
1720 $sidebars[$key] = $sb;
1722 wp_set_sidebars_widgets($sidebars);
1730 * Ajax handler for saving a widget.
1734 function wp_ajax_save_widget() {
1735 global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
1737 check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1739 if ( !current_user_can('edit_theme_options') || !isset($_POST['id_base']) )
1742 unset( $_POST['savewidgets'], $_POST['action'] );
1745 * Fires early when editing the widgets displayed in sidebars.
1749 do_action( 'load-widgets.php' );
1752 * Fires early when editing the widgets displayed in sidebars.
1756 do_action( 'widgets.php' );
1758 /** This action is documented in wp-admin/widgets.php */
1759 do_action( 'sidebar_admin_setup' );
1761 $id_base = $_POST['id_base'];
1762 $widget_id = $_POST['widget-id'];
1763 $sidebar_id = $_POST['sidebar'];
1764 $multi_number = !empty($_POST['multi_number']) ? (int) $_POST['multi_number'] : 0;
1765 $settings = isset($_POST['widget-' . $id_base]) && is_array($_POST['widget-' . $id_base]) ? $_POST['widget-' . $id_base] : false;
1766 $error = '<p>' . __('An error has occurred. Please reload the page and try again.') . '</p>';
1768 $sidebars = wp_get_sidebars_widgets();
1769 $sidebar = isset($sidebars[$sidebar_id]) ? $sidebars[$sidebar_id] : array();
1772 if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1774 if ( !isset($wp_registered_widgets[$widget_id]) )
1777 $sidebar = array_diff( $sidebar, array($widget_id) );
1778 $_POST = array('sidebar' => $sidebar_id, 'widget-' . $id_base => array(), 'the-widget-id' => $widget_id, 'delete_widget' => '1');
1779 } elseif ( $settings && preg_match( '/__i__|%i%/', key($settings) ) ) {
1780 if ( !$multi_number )
1783 $_POST['widget-' . $id_base] = array( $multi_number => array_shift($settings) );
1784 $widget_id = $id_base . '-' . $multi_number;
1785 $sidebar[] = $widget_id;
1787 $_POST['widget-id'] = $sidebar;
1789 foreach ( (array) $wp_registered_widget_updates as $name => $control ) {
1791 if ( $name == $id_base ) {
1792 if ( !is_callable( $control['callback'] ) )
1796 call_user_func_array( $control['callback'], $control['params'] );
1802 if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1803 $sidebars[$sidebar_id] = $sidebar;
1804 wp_set_sidebars_widgets($sidebars);
1805 echo "deleted:$widget_id";
1809 if ( !empty($_POST['add_new']) )
1812 if ( $form = $wp_registered_widget_controls[$widget_id] )
1813 call_user_func_array( $form['callback'], $form['params'] );
1819 * Ajax handler for saving a widget.
1823 function wp_ajax_update_widget() {
1824 global $wp_customize;
1825 $wp_customize->widgets->wp_ajax_update_widget();
1829 * Ajax handler for uploading attachments
1833 function wp_ajax_upload_attachment() {
1834 check_ajax_referer( 'media-form' );
1836 * This function does not use wp_send_json_success() / wp_send_json_error()
1837 * as the html4 Plupload handler requires a text/html content-type for older IE.
1838 * See https://core.trac.wordpress.org/ticket/31037
1841 if ( ! current_user_can( 'upload_files' ) ) {
1842 echo wp_json_encode( array(
1845 'message' => __( "You don't have permission to upload files." ),
1846 'filename' => $_FILES['async-upload']['name'],
1853 if ( isset( $_REQUEST['post_id'] ) ) {
1854 $post_id = $_REQUEST['post_id'];
1855 if ( ! current_user_can( 'edit_post', $post_id ) ) {
1856 echo wp_json_encode( array(
1859 'message' => __( "You don't have permission to attach files to this post." ),
1860 'filename' => $_FILES['async-upload']['name'],
1870 $post_data = isset( $_REQUEST['post_data'] ) ? $_REQUEST['post_data'] : array();
1872 // If the context is custom header or background, make sure the uploaded file is an image.
1873 if ( isset( $post_data['context'] ) && in_array( $post_data['context'], array( 'custom-header', 'custom-background' ) ) ) {
1874 $wp_filetype = wp_check_filetype_and_ext( $_FILES['async-upload']['tmp_name'], $_FILES['async-upload']['name'], false );
1875 if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) {
1876 echo wp_json_encode( array(
1879 'message' => __( 'The uploaded file is not a valid image. Please try again.' ),
1880 'filename' => $_FILES['async-upload']['name'],
1888 $attachment_id = media_handle_upload( 'async-upload', $post_id, $post_data );
1890 if ( is_wp_error( $attachment_id ) ) {
1891 echo wp_json_encode( array(
1894 'message' => $attachment_id->get_error_message(),
1895 'filename' => $_FILES['async-upload']['name'],
1902 if ( isset( $post_data['context'] ) && isset( $post_data['theme'] ) ) {
1903 if ( 'custom-background' === $post_data['context'] )
1904 update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', $post_data['theme'] );
1906 if ( 'custom-header' === $post_data['context'] )
1907 update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', $post_data['theme'] );
1910 if ( ! $attachment = wp_prepare_attachment_for_js( $attachment_id ) )
1913 echo wp_json_encode( array(
1915 'data' => $attachment,
1922 * Ajax handler for image editing.
1926 function wp_ajax_image_editor() {
1927 $attachment_id = intval($_POST['postid']);
1928 if ( empty($attachment_id) || !current_user_can('edit_post', $attachment_id) )
1931 check_ajax_referer( "image_editor-$attachment_id" );
1932 include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
1935 switch ( $_POST['do'] ) {
1937 $msg = wp_save_image($attachment_id);
1938 $msg = wp_json_encode($msg);
1942 $msg = wp_save_image($attachment_id);
1945 $msg = wp_restore_image($attachment_id);
1949 wp_image_editor($attachment_id, $msg);
1954 * Ajax handler for setting the featured image.
1958 function wp_ajax_set_post_thumbnail() {
1959 $json = ! empty( $_REQUEST['json'] ); // New-style request
1961 $post_ID = intval( $_POST['post_id'] );
1962 if ( ! current_user_can( 'edit_post', $post_ID ) )
1965 $thumbnail_id = intval( $_POST['thumbnail_id'] );
1968 check_ajax_referer( "update-post_$post_ID" );
1970 check_ajax_referer( "set_post_thumbnail-$post_ID" );
1972 if ( $thumbnail_id == '-1' ) {
1973 if ( delete_post_thumbnail( $post_ID ) ) {
1974 $return = _wp_post_thumbnail_html( null, $post_ID );
1975 $json ? wp_send_json_success( $return ) : wp_die( $return );
1981 if ( set_post_thumbnail( $post_ID, $thumbnail_id ) ) {
1982 $return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID );
1983 $json ? wp_send_json_success( $return ) : wp_die( $return );
1990 * AJAX handler for setting the featured image for an attachment.
1994 * @see set_post_thumbnail()
1996 function wp_ajax_set_attachment_thumbnail() {
1997 if ( empty( $_POST['urls'] ) || ! is_array( $_POST['urls'] ) ) {
1998 wp_send_json_error();
2001 $thumbnail_id = (int) $_POST['thumbnail_id'];
2002 if ( empty( $thumbnail_id ) ) {
2003 wp_send_json_error();
2006 $post_ids = array();
2007 // For each URL, try to find its corresponding post ID.
2008 foreach ( $_POST['urls'] as $url ) {
2009 $post_id = attachment_url_to_postid( $url );
2010 if ( ! empty( $post_id ) ) {
2011 $post_ids[] = $post_id;
2015 if ( empty( $post_ids ) ) {
2016 wp_send_json_error();
2020 // For each found attachment, set its thumbnail.
2021 foreach ( $post_ids as $post_id ) {
2022 if ( ! current_user_can( 'edit_post', $post_id ) ) {
2026 if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) {
2031 if ( 0 === $success ) {
2032 wp_send_json_error();
2034 wp_send_json_success();
2037 wp_send_json_error();
2041 * Ajax handler for date formatting.
2045 function wp_ajax_date_format() {
2046 wp_die( date_i18n( sanitize_option( 'date_format', wp_unslash( $_POST['date'] ) ) ) );
2050 * Ajax handler for time formatting.
2054 function wp_ajax_time_format() {
2055 wp_die( date_i18n( sanitize_option( 'time_format', wp_unslash( $_POST['date'] ) ) ) );
2059 * Ajax handler for saving posts from the fullscreen editor.
2063 function wp_ajax_wp_fullscreen_save_post() {
2064 $post_id = isset( $_POST['post_ID'] ) ? (int) $_POST['post_ID'] : 0;
2069 $post = get_post( $post_id );
2071 check_ajax_referer('update-post_' . $post_id, '_wpnonce');
2073 $post_id = edit_post();
2075 if ( is_wp_error( $post_id ) ) {
2076 wp_send_json_error();
2080 $last_date = mysql2date( get_option('date_format'), $post->post_modified );
2081 $last_time = mysql2date( get_option('time_format'), $post->post_modified );
2083 $last_date = date_i18n( get_option('date_format') );
2084 $last_time = date_i18n( get_option('time_format') );
2087 if ( $last_id = get_post_meta( $post_id, '_edit_last', true ) ) {
2088 $last_user = get_userdata( $last_id );
2089 $last_edited = sprintf( __('Last edited by %1$s on %2$s at %3$s'), esc_html( $last_user->display_name ), $last_date, $last_time );
2091 $last_edited = sprintf( __('Last edited on %1$s at %2$s'), $last_date, $last_time );
2094 wp_send_json_success( array( 'last_edited' => $last_edited ) );
2098 * Ajax handler for removing a post lock.
2102 function wp_ajax_wp_remove_post_lock() {
2103 if ( empty( $_POST['post_ID'] ) || empty( $_POST['active_post_lock'] ) )
2105 $post_id = (int) $_POST['post_ID'];
2106 if ( ! $post = get_post( $post_id ) )
2109 check_ajax_referer( 'update-post_' . $post_id );
2111 if ( ! current_user_can( 'edit_post', $post_id ) )
2114 $active_lock = array_map( 'absint', explode( ':', $_POST['active_post_lock'] ) );
2115 if ( $active_lock[1] != get_current_user_id() )
2119 * Filter the post lock window duration.
2123 * @param int $interval The interval in seconds the post lock duration
2124 * should last, plus 5 seconds. Default 150.
2126 $new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 150 ) + 5 ) . ':' . $active_lock[1];
2127 update_post_meta( $post_id, '_edit_lock', $new_lock, implode( ':', $active_lock ) );
2132 * Ajax handler for dismissing a WordPress pointer.
2136 function wp_ajax_dismiss_wp_pointer() {
2137 $pointer = $_POST['pointer'];
2138 if ( $pointer != sanitize_key( $pointer ) )
2141 // check_ajax_referer( 'dismiss-pointer_' . $pointer );
2143 $dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) );
2145 if ( in_array( $pointer, $dismissed ) )
2148 $dismissed[] = $pointer;
2149 $dismissed = implode( ',', $dismissed );
2151 update_user_meta( get_current_user_id(), 'dismissed_wp_pointers', $dismissed );
2156 * Ajax handler for getting an attachment.
2160 function wp_ajax_get_attachment() {
2161 if ( ! isset( $_REQUEST['id'] ) )
2162 wp_send_json_error();
2164 if ( ! $id = absint( $_REQUEST['id'] ) )
2165 wp_send_json_error();
2167 if ( ! $post = get_post( $id ) )
2168 wp_send_json_error();
2170 if ( 'attachment' != $post->post_type )
2171 wp_send_json_error();
2173 if ( ! current_user_can( 'upload_files' ) )
2174 wp_send_json_error();
2176 if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2177 wp_send_json_error();
2179 wp_send_json_success( $attachment );
2183 * Ajax handler for querying attachments.
2187 function wp_ajax_query_attachments() {
2188 if ( ! current_user_can( 'upload_files' ) )
2189 wp_send_json_error();
2191 $query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array();
2192 $query = array_intersect_key( $query, array_flip( array(
2193 's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type',
2194 'post_parent', 'post__in', 'post__not_in', 'year', 'monthnum'
2197 $query['post_type'] = 'attachment';
2199 && ! empty( $_REQUEST['query']['post_status'] )
2200 && 'trash' === $_REQUEST['query']['post_status'] ) {
2201 $query['post_status'] = 'trash';
2203 $query['post_status'] = 'inherit';
2206 if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) )
2207 $query['post_status'] .= ',private';
2210 * Filter the arguments passed to WP_Query during an AJAX
2211 * call for querying attachments.
2215 * @see WP_Query::parse_query()
2217 * @param array $query An array of query variables.
2219 $query = apply_filters( 'ajax_query_attachments_args', $query );
2220 $query = new WP_Query( $query );
2222 $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts );
2223 $posts = array_filter( $posts );
2225 wp_send_json_success( $posts );
2229 * Ajax handler for updating attachment attributes.
2233 function wp_ajax_save_attachment() {
2234 if ( ! isset( $_REQUEST['id'] ) || ! isset( $_REQUEST['changes'] ) )
2235 wp_send_json_error();
2237 if ( ! $id = absint( $_REQUEST['id'] ) )
2238 wp_send_json_error();
2240 check_ajax_referer( 'update-post_' . $id, 'nonce' );
2242 if ( ! current_user_can( 'edit_post', $id ) )
2243 wp_send_json_error();
2245 $changes = $_REQUEST['changes'];
2246 $post = get_post( $id, ARRAY_A );
2248 if ( 'attachment' != $post['post_type'] )
2249 wp_send_json_error();
2251 if ( isset( $changes['title'] ) )
2252 $post['post_title'] = $changes['title'];
2254 if ( isset( $changes['caption'] ) )
2255 $post['post_excerpt'] = $changes['caption'];
2257 if ( isset( $changes['description'] ) )
2258 $post['post_content'] = $changes['description'];
2260 if ( MEDIA_TRASH && isset( $changes['status'] ) )
2261 $post['post_status'] = $changes['status'];
2263 if ( isset( $changes['alt'] ) ) {
2264 $alt = wp_unslash( $changes['alt'] );
2265 if ( $alt != get_post_meta( $id, '_wp_attachment_image_alt', true ) ) {
2266 $alt = wp_strip_all_tags( $alt, true );
2267 update_post_meta( $id, '_wp_attachment_image_alt', wp_slash( $alt ) );
2271 if ( 0 === strpos( $post['post_mime_type'], 'audio/' ) ) {
2273 $id3data = wp_get_attachment_metadata( $post['ID'] );
2274 if ( ! is_array( $id3data ) ) {
2278 foreach ( wp_get_attachment_id3_keys( (object) $post, 'edit' ) as $key => $label ) {
2279 if ( isset( $changes[ $key ] ) ) {
2281 $id3data[ $key ] = sanitize_text_field( wp_unslash( $changes[ $key ] ) );
2286 wp_update_attachment_metadata( $id, $id3data );
2290 if ( MEDIA_TRASH && isset( $changes['status'] ) && 'trash' === $changes['status'] ) {
2291 wp_delete_post( $id );
2293 wp_update_post( $post );
2296 wp_send_json_success();
2300 * Ajax handler for saving backwards compatible attachment attributes.
2304 function wp_ajax_save_attachment_compat() {
2305 if ( ! isset( $_REQUEST['id'] ) )
2306 wp_send_json_error();
2308 if ( ! $id = absint( $_REQUEST['id'] ) )
2309 wp_send_json_error();
2311 if ( empty( $_REQUEST['attachments'] ) || empty( $_REQUEST['attachments'][ $id ] ) )
2312 wp_send_json_error();
2313 $attachment_data = $_REQUEST['attachments'][ $id ];
2315 check_ajax_referer( 'update-post_' . $id, 'nonce' );
2317 if ( ! current_user_can( 'edit_post', $id ) )
2318 wp_send_json_error();
2320 $post = get_post( $id, ARRAY_A );
2322 if ( 'attachment' != $post['post_type'] )
2323 wp_send_json_error();
2325 /** This filter is documented in wp-admin/includes/media.php */
2326 $post = apply_filters( 'attachment_fields_to_save', $post, $attachment_data );
2328 if ( isset( $post['errors'] ) ) {
2329 $errors = $post['errors']; // @todo return me and display me!
2330 unset( $post['errors'] );
2333 wp_update_post( $post );
2335 foreach ( get_attachment_taxonomies( $post ) as $taxonomy ) {
2336 if ( isset( $attachment_data[ $taxonomy ] ) )
2337 wp_set_object_terms( $id, array_map( 'trim', preg_split( '/,+/', $attachment_data[ $taxonomy ] ) ), $taxonomy, false );
2340 if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2341 wp_send_json_error();
2343 wp_send_json_success( $attachment );
2347 * Ajax handler for saving the attachment order.
2351 function wp_ajax_save_attachment_order() {
2352 if ( ! isset( $_REQUEST['post_id'] ) )
2353 wp_send_json_error();
2355 if ( ! $post_id = absint( $_REQUEST['post_id'] ) )
2356 wp_send_json_error();
2358 if ( empty( $_REQUEST['attachments'] ) )
2359 wp_send_json_error();
2361 check_ajax_referer( 'update-post_' . $post_id, 'nonce' );
2363 $attachments = $_REQUEST['attachments'];
2365 if ( ! current_user_can( 'edit_post', $post_id ) )
2366 wp_send_json_error();
2368 foreach ( $attachments as $attachment_id => $menu_order ) {
2369 if ( ! current_user_can( 'edit_post', $attachment_id ) )
2371 if ( ! $attachment = get_post( $attachment_id ) )
2373 if ( 'attachment' != $attachment->post_type )
2376 wp_update_post( array( 'ID' => $attachment_id, 'menu_order' => $menu_order ) );
2379 wp_send_json_success();
2383 * Ajax handler for sending an attachment to the editor.
2385 * Generates the HTML to send an attachment to the editor.
2386 * Backwards compatible with the media_send_to_editor filter
2387 * and the chain of filters that follow.
2391 function wp_ajax_send_attachment_to_editor() {
2392 check_ajax_referer( 'media-send-to-editor', 'nonce' );
2394 $attachment = wp_unslash( $_POST['attachment'] );
2396 $id = intval( $attachment['id'] );
2398 if ( ! $post = get_post( $id ) )
2399 wp_send_json_error();
2401 if ( 'attachment' != $post->post_type )
2402 wp_send_json_error();
2404 if ( current_user_can( 'edit_post', $id ) ) {
2405 // If this attachment is unattached, attach it. Primarily a back compat thing.
2406 if ( 0 == $post->post_parent && $insert_into_post_id = intval( $_POST['post_id'] ) ) {
2407 wp_update_post( array( 'ID' => $id, 'post_parent' => $insert_into_post_id ) );
2412 $html = isset( $attachment['post_title'] ) ? $attachment['post_title'] : '';
2413 if ( ! empty( $attachment['url'] ) ) {
2414 $url = $attachment['url'];
2415 if ( strpos( $url, 'attachment_id') || get_attachment_link( $id ) == $url )
2416 $rel = ' rel="attachment wp-att-' . $id . '"';
2417 $html = '<a href="' . esc_url( $url ) . '"' . $rel . '>' . $html . '</a>';
2420 remove_filter( 'media_send_to_editor', 'image_media_send_to_editor' );
2422 if ( 'image' === substr( $post->post_mime_type, 0, 5 ) ) {
2423 $align = isset( $attachment['align'] ) ? $attachment['align'] : 'none';
2424 $size = isset( $attachment['image-size'] ) ? $attachment['image-size'] : 'medium';
2425 $alt = isset( $attachment['image_alt'] ) ? $attachment['image_alt'] : '';
2426 $caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : '';
2427 $title = ''; // We no longer insert title tags into <img> tags, as they are redundant.
2428 $html = get_image_send_to_editor( $id, $caption, $title, $align, $url, (bool) $rel, $size, $alt );
2429 } elseif ( 'video' === substr( $post->post_mime_type, 0, 5 ) || 'audio' === substr( $post->post_mime_type, 0, 5 ) ) {
2430 $html = stripslashes_deep( $_POST['html'] );
2433 /** This filter is documented in wp-admin/includes/media.php */
2434 $html = apply_filters( 'media_send_to_editor', $html, $id, $attachment );
2436 wp_send_json_success( $html );
2440 * Ajax handler for sending a link to the editor.
2442 * Generates the HTML to send a non-image embed link to the editor.
2444 * Backwards compatible with the following filters:
2445 * - file_send_to_editor_url
2446 * - audio_send_to_editor_url
2447 * - video_send_to_editor_url
2451 function wp_ajax_send_link_to_editor() {
2452 global $post, $wp_embed;
2454 check_ajax_referer( 'media-send-to-editor', 'nonce' );
2456 if ( ! $src = wp_unslash( $_POST['src'] ) )
2457 wp_send_json_error();
2459 if ( ! strpos( $src, '://' ) )
2460 $src = 'http://' . $src;
2462 if ( ! $src = esc_url_raw( $src ) )
2463 wp_send_json_error();
2465 if ( ! $title = trim( wp_unslash( $_POST['title'] ) ) )
2466 $title = wp_basename( $src );
2468 $post = get_post( isset( $_POST['post_id'] ) ? $_POST['post_id'] : 0 );
2470 // Ping WordPress for an embed.
2471 $check_embed = $wp_embed->run_shortcode( '[embed]'. $src .'[/embed]' );
2473 // Fallback that WordPress creates when no oEmbed was found.
2474 $fallback = $wp_embed->maybe_make_link( $src );
2476 if ( $check_embed !== $fallback ) {
2477 // TinyMCE view for [embed] will parse this
2478 $html = '[embed]' . $src . '[/embed]';
2479 } elseif ( $title ) {
2480 $html = '<a href="' . esc_url( $src ) . '">' . $title . '</a>';
2485 // Figure out what filter to run:
2487 if ( ( $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $src ) ) && ( $ext_type = wp_ext2type( $ext ) )
2488 && ( 'audio' == $ext_type || 'video' == $ext_type ) )
2491 /** This filter is documented in wp-admin/includes/media.php */
2492 $html = apply_filters( $type . '_send_to_editor_url', $html, $src, $title );
2494 wp_send_json_success( $html );
2498 * Ajax handler for the Heartbeat API.
2500 * Runs when the user is logged in.
2504 function wp_ajax_heartbeat() {
2505 if ( empty( $_POST['_nonce'] ) )
2506 wp_send_json_error();
2508 $response = array();
2510 if ( false === wp_verify_nonce( $_POST['_nonce'], 'heartbeat-nonce' ) ) {
2511 // User is logged in but nonces have expired.
2512 $response['nonces_expired'] = true;
2513 wp_send_json($response);
2516 // screen_id is the same as $current_screen->id and the JS global 'pagenow'.
2517 if ( ! empty($_POST['screen_id']) )
2518 $screen_id = sanitize_key($_POST['screen_id']);
2520 $screen_id = 'front';
2522 if ( ! empty($_POST['data']) ) {
2523 $data = wp_unslash( (array) $_POST['data'] );
2526 * Filter the Heartbeat response received.
2530 * @param array|object $response The Heartbeat response object or array.
2531 * @param array $data The $_POST data sent.
2532 * @param string $screen_id The screen id.
2534 $response = apply_filters( 'heartbeat_received', $response, $data, $screen_id );
2538 * Filter the Heartbeat response sent.
2542 * @param array|object $response The Heartbeat response object or array.
2543 * @param string $screen_id The screen id.
2545 $response = apply_filters( 'heartbeat_send', $response, $screen_id );
2548 * Fires when Heartbeat ticks in logged-in environments.
2550 * Allows the transport to be easily replaced with long-polling.
2554 * @param array|object $response The Heartbeat response object or array.
2555 * @param string $screen_id The screen id.
2557 do_action( 'heartbeat_tick', $response, $screen_id );
2559 // Send the current time according to the server
2560 $response['server_time'] = time();
2562 wp_send_json($response);
2566 * Ajax handler for getting revision diffs.
2570 function wp_ajax_get_revision_diffs() {
2571 require ABSPATH . 'wp-admin/includes/revision.php';
2573 if ( ! $post = get_post( (int) $_REQUEST['post_id'] ) )
2574 wp_send_json_error();
2576 if ( ! current_user_can( 'read_post', $post->ID ) )
2577 wp_send_json_error();
2579 // Really just pre-loading the cache here.
2580 if ( ! $revisions = wp_get_post_revisions( $post->ID, array( 'check_enabled' => false ) ) )
2581 wp_send_json_error();
2584 @set_time_limit( 0 );
2586 foreach ( $_REQUEST['compare'] as $compare_key ) {
2587 list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to
2590 'id' => $compare_key,
2591 'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ),
2594 wp_send_json_success( $return );
2598 * Ajax handler for auto-saving the selected color scheme for
2599 * a user's own profile.
2603 function wp_ajax_save_user_color_scheme() {
2604 global $_wp_admin_css_colors;
2606 check_ajax_referer( 'save-color-scheme', 'nonce' );
2608 $color_scheme = sanitize_key( $_POST['color_scheme'] );
2610 if ( ! isset( $_wp_admin_css_colors[ $color_scheme ] ) ) {
2611 wp_send_json_error();
2614 update_user_meta( get_current_user_id(), 'admin_color', $color_scheme );
2615 wp_send_json_success();
2619 * Ajax handler for getting themes from themes_api().
2623 function wp_ajax_query_themes() {
2624 global $themes_allowedtags, $theme_field_defaults;
2626 if ( ! current_user_can( 'install_themes' ) ) {
2627 wp_send_json_error();
2630 $args = wp_parse_args( wp_unslash( $_REQUEST['request'] ), array(
2632 'fields' => $theme_field_defaults
2635 $old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search';
2637 /** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */
2638 $args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args );
2640 $api = themes_api( 'query_themes', $args );
2642 if ( is_wp_error( $api ) ) {
2643 wp_send_json_error();
2646 $update_php = network_admin_url( 'update.php?action=install-theme' );
2647 foreach ( $api->themes as &$theme ) {
2648 $theme->install_url = add_query_arg( array(
2649 'theme' => $theme->slug,
2650 '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug )
2653 $theme->name = wp_kses( $theme->name, $themes_allowedtags );
2654 $theme->author = wp_kses( $theme->author, $themes_allowedtags );
2655 $theme->version = wp_kses( $theme->version, $themes_allowedtags );
2656 $theme->description = wp_kses( $theme->description, $themes_allowedtags );
2657 $theme->num_ratings = sprintf( _n( '(based on %s rating)', '(based on %s ratings)', $theme->num_ratings ), number_format_i18n( $theme->num_ratings ) );
2658 $theme->preview_url = set_url_scheme( $theme->preview_url );
2661 wp_send_json_success( $api );
2665 * Apply [embed] AJAX handlers to a string.
2669 * @global WP_Post $post Global $post.
2670 * @global WP_Embed $wp_embed Embed API instance.
2672 function wp_ajax_parse_embed() {
2673 global $post, $wp_embed;
2675 if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) {
2676 wp_send_json_error();
2679 if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) {
2680 wp_send_json_error();
2683 $shortcode = wp_unslash( $_POST['shortcode'] );
2684 $url = str_replace( '[embed]', '', str_replace( '[/embed]', '', $shortcode ) );
2686 setup_postdata( $post );
2688 $wp_embed->return_false_on_fail = true;
2690 if ( is_ssl() && preg_match( '%^\\[embed[^\\]]*\\]http://%i', $shortcode ) ) {
2691 // Admin is ssl and the user pasted non-ssl URL.
2692 // Check if the provider supports ssl embeds and use that for the preview.
2693 $ssl_shortcode = preg_replace( '%^(\\[embed[^\\]]*\\])http://%i', '$1https://', $shortcode );
2694 $parsed = $wp_embed->run_shortcode( $ssl_shortcode );
2697 $no_ssl_support = true;
2702 $parsed = $wp_embed->run_shortcode( $shortcode );
2706 wp_send_json_error( array(
2707 'type' => 'not-embeddable',
2708 'message' => sprintf( __( '%s failed to embed.' ), '<code>' . esc_html( $url ) . '</code>' ),
2712 if ( has_shortcode( $parsed, 'audio' ) || has_shortcode( $parsed, 'video' ) ) {
2714 $mce_styles = wpview_media_sandbox_styles();
2715 foreach ( $mce_styles as $style ) {
2716 $styles .= sprintf( '<link rel="stylesheet" href="%s"/>', $style );
2719 $html = do_shortcode( $parsed );
2722 if ( ! empty( $wp_scripts ) ) {
2723 $wp_scripts->done = array();
2726 wp_print_scripts( 'wp-mediaelement' );
2727 $scripts = ob_get_clean();
2729 $parsed = $styles . $html . $scripts;
2733 if ( ! empty( $no_ssl_support ) || ( is_ssl() && ( preg_match( '%<(iframe|script|embed) [^>]*src="http://%', $parsed ) ||
2734 preg_match( '%<link [^>]*href="http://%', $parsed ) ) ) ) {
2735 // Admin is ssl and the embed is not. Iframes, scripts, and other "active content" will be blocked.
2736 wp_send_json_error( array(
2737 'type' => 'not-ssl',
2738 'message' => __( 'This preview is unavailable in the editor.' ),
2742 wp_send_json_success( array(
2747 function wp_ajax_parse_media_shortcode() {
2748 global $post, $wp_scripts;
2750 if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) {
2751 wp_send_json_error();
2754 if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) {
2755 wp_send_json_error();
2758 setup_postdata( $post );
2759 $shortcode = do_shortcode( wp_unslash( $_POST['shortcode'] ) );
2761 if ( empty( $shortcode ) ) {
2762 wp_send_json_error( array(
2763 'type' => 'no-items',
2764 'message' => __( 'No items found.' ),
2769 $styles = wpview_media_sandbox_styles();
2771 foreach ( $styles as $style ) {
2772 $head .= '<link type="text/css" rel="stylesheet" href="' . $style . '">';
2775 if ( ! empty( $wp_scripts ) ) {
2776 $wp_scripts->done = array();
2783 if ( 'playlist' === $_REQUEST['type'] ) {
2784 wp_underscore_playlist_templates();
2786 wp_print_scripts( 'wp-playlist' );
2788 wp_print_scripts( 'wp-mediaelement' );
2791 wp_send_json_success( array(
2793 'body' => ob_get_clean()
2798 * AJAX handler for destroying multiple open sessions for a user.
2802 function wp_ajax_destroy_sessions() {
2804 $user = get_userdata( (int) $_POST['user_id'] );
2806 if ( ! current_user_can( 'edit_user', $user->ID ) ) {
2808 } elseif ( ! wp_verify_nonce( $_POST['nonce'], 'update-user_' . $user->ID ) ) {
2814 wp_send_json_error( array(
2815 'message' => __( 'Could not log out user sessions. Please try again.' ),
2819 $sessions = WP_Session_Tokens::get_instance( $user->ID );
2821 if ( $user->ID === get_current_user_id() ) {
2822 $sessions->destroy_others( wp_get_session_token() );
2823 $message = __( 'You are now logged out everywhere else.' );
2825 $sessions->destroy_all();
2826 /* translators: 1: User's display name. */
2827 $message = sprintf( __( '%s has been logged out.' ), $user->display_name );
2830 wp_send_json_success( array( 'message' => $message ) );