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'] ) ) {
107 $taxonomy = sanitize_key( $_GET['tax'] );
108 $tax = get_taxonomy( $taxonomy );
111 if ( ! current_user_can( $tax->cap->assign_terms ) )
117 $s = wp_unslash( $_GET['q'] );
119 $comma = _x( ',', 'tag delimiter' );
120 if ( ',' !== $comma )
121 $s = str_replace( $comma, ',', $s );
122 if ( false !== strpos( $s, ',' ) ) {
123 $s = explode( ',', $s );
124 $s = $s[count( $s ) - 1];
129 * Filter the minimum number of characters required to fire a tag search via AJAX.
133 * @param int $characters The minimum number of characters required. Default 2.
134 * @param object $tax The taxonomy object.
135 * @param string $s The search term.
137 $term_search_min_chars = (int) apply_filters( 'term_search_min_chars', 2, $tax, $s );
140 * Require $term_search_min_chars chars for matching (default: 2)
141 * ensure it's a non-negative, non-zero integer.
143 if ( ( $term_search_min_chars == 0 ) || ( strlen( $s ) < $term_search_min_chars ) ){
147 $results = get_terms( $taxonomy, array( 'name__like' => $s, 'fields' => 'names', 'hide_empty' => false ) );
149 echo join( $results, "\n" );
154 * Ajax handler for compression testing.
158 function wp_ajax_wp_compression_test() {
159 if ( !current_user_can( 'manage_options' ) )
162 if ( ini_get('zlib.output_compression') || 'ob_gzhandler' == ini_get('output_handler') ) {
163 update_site_option('can_compress_scripts', 0);
167 if ( isset($_GET['test']) ) {
168 header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' );
169 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
170 header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
171 header( 'Pragma: no-cache' );
172 header('Content-Type: application/x-javascript; charset=UTF-8');
173 $force_gzip = ( defined('ENFORCE_GZIP') && ENFORCE_GZIP );
174 $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."';
176 if ( 1 == $_GET['test'] ) {
179 } elseif ( 2 == $_GET['test'] ) {
180 if ( !isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
182 if ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') && function_exists('gzdeflate') && ! $force_gzip ) {
183 header('Content-Encoding: deflate');
184 $out = gzdeflate( $test_str, 1 );
185 } elseif ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') && function_exists('gzencode') ) {
186 header('Content-Encoding: gzip');
187 $out = gzencode( $test_str, 1 );
193 } elseif ( 'no' == $_GET['test'] ) {
194 update_site_option('can_compress_scripts', 0);
195 } elseif ( 'yes' == $_GET['test'] ) {
196 update_site_option('can_compress_scripts', 1);
204 * Ajax handler for image editor previews.
208 function wp_ajax_imgedit_preview() {
209 $post_id = intval($_GET['postid']);
210 if ( empty($post_id) || !current_user_can('edit_post', $post_id) )
213 check_ajax_referer( "image_editor-$post_id" );
215 include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
216 if ( ! stream_preview_image($post_id) )
223 * Ajax handler for oEmbed caching.
227 function wp_ajax_oembed_cache() {
228 $GLOBALS['wp_embed']->cache_oembed( $_GET['post'] );
233 * Ajax handler for user autocomplete.
237 function wp_ajax_autocomplete_user() {
238 if ( ! is_multisite() || ! current_user_can( 'promote_users' ) || wp_is_large_network( 'users' ) )
241 /** This filter is documented in wp-admin/user-new.php */
242 if ( ! is_super_admin() && ! apply_filters( 'autocomplete_users_for_site_admins', false ) )
247 // Check the type of request
248 // Current allowed values are `add` and `search`
249 if ( isset( $_REQUEST['autocomplete_type'] ) && 'search' === $_REQUEST['autocomplete_type'] ) {
250 $type = $_REQUEST['autocomplete_type'];
255 // Check the desired field for value
256 // Current allowed values are `user_email` and `user_login`
257 if ( isset( $_REQUEST['autocomplete_field'] ) && 'user_email' === $_REQUEST['autocomplete_field'] ) {
258 $field = $_REQUEST['autocomplete_field'];
260 $field = 'user_login';
263 // Exclude current users of this blog
264 if ( isset( $_REQUEST['site_id'] ) ) {
265 $id = absint( $_REQUEST['site_id'] );
267 $id = get_current_blog_id();
270 $include_blog_users = ( $type == 'search' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
271 $exclude_blog_users = ( $type == 'add' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
273 $users = get_users( array(
275 'search' => '*' . $_REQUEST['term'] . '*',
276 'include' => $include_blog_users,
277 'exclude' => $exclude_blog_users,
278 'search_columns' => array( 'user_login', 'user_nicename', 'user_email' ),
281 foreach ( $users as $user ) {
283 /* translators: 1: user_login, 2: user_email */
284 'label' => sprintf( __( '%1$s (%2$s)' ), $user->user_login, $user->user_email ),
285 'value' => $user->$field,
289 wp_die( json_encode( $return ) );
293 * Ajax handler for dashboard widgets.
297 function wp_ajax_dashboard_widgets() {
298 require_once ABSPATH . 'wp-admin/includes/dashboard.php';
300 $pagenow = $_GET['pagenow'];
301 if ( $pagenow === 'dashboard-user' || $pagenow === 'dashboard-network' || $pagenow === 'dashboard' ) {
302 set_current_screen( $pagenow );
305 switch ( $_GET['widget'] ) {
306 case 'dashboard_primary' :
307 wp_dashboard_primary();
314 * Ajax handler for Customizer preview logged-in status.
318 function wp_ajax_logged_in() {
327 * Sends back current comment total and new page links if they need to be updated.
329 * Contrary to normal success AJAX response ("1"), die with time() on success.
333 * @param int $comment_id
336 function _wp_ajax_delete_comment_response( $comment_id, $delta = -1 ) {
337 $total = isset( $_POST['_total'] ) ? (int) $_POST['_total'] : 0;
338 $per_page = isset( $_POST['_per_page'] ) ? (int) $_POST['_per_page'] : 0;
339 $page = isset( $_POST['_page'] ) ? (int) $_POST['_page'] : 0;
340 $url = isset( $_POST['_url'] ) ? esc_url_raw( $_POST['_url'] ) : '';
342 // JS didn't send us everything we need to know. Just die with success message
343 if ( !$total || !$per_page || !$page || !$url )
350 // Only do the expensive stuff on a page-break, and about 1 other time per page
351 if ( 0 == $total % $per_page || 1 == mt_rand( 1, $per_page ) ) {
353 $status = 'total_comments'; // What type of comment count are we looking for?
354 $parsed = parse_url( $url );
355 if ( isset( $parsed['query'] ) ) {
356 parse_str( $parsed['query'], $query_vars );
357 if ( !empty( $query_vars['comment_status'] ) )
358 $status = $query_vars['comment_status'];
359 if ( !empty( $query_vars['p'] ) )
360 $post_id = (int) $query_vars['p'];
363 $comment_count = wp_count_comments($post_id);
365 // We're looking for a known type of comment count.
366 if ( isset( $comment_count->$status ) )
367 $total = $comment_count->$status;
368 // Else use the decremented value from above.
371 // The time since the last comment count.
374 $x = new WP_Ajax_Response( array(
376 // Here for completeness - not used.
378 'supplemental' => array(
379 'total_items_i18n' => sprintf( _n( '1 item', '%s items', $total ), number_format_i18n( $total ) ),
380 'total_pages' => ceil( $total / $per_page ),
381 'total_pages_i18n' => number_format_i18n( ceil( $total / $per_page ) ),
390 // POST-based Ajax handlers.
394 * Ajax handler for adding a hierarchical term.
398 function _wp_ajax_add_hierarchical_term() {
399 $action = $_POST['action'];
400 $taxonomy = get_taxonomy(substr($action, 4));
401 check_ajax_referer( $action, '_ajax_nonce-add-' . $taxonomy->name );
402 if ( !current_user_can( $taxonomy->cap->edit_terms ) )
404 $names = explode(',', $_POST['new'.$taxonomy->name]);
405 $parent = isset($_POST['new'.$taxonomy->name.'_parent']) ? (int) $_POST['new'.$taxonomy->name.'_parent'] : 0;
408 if ( $taxonomy->name == 'category' )
409 $post_category = isset($_POST['post_category']) ? (array) $_POST['post_category'] : array();
411 $post_category = ( isset($_POST['tax_input']) && isset($_POST['tax_input'][$taxonomy->name]) ) ? (array) $_POST['tax_input'][$taxonomy->name] : array();
412 $checked_categories = array_map( 'absint', (array) $post_category );
413 $popular_ids = wp_popular_terms_checklist($taxonomy->name, 0, 10, false);
415 foreach ( $names as $cat_name ) {
416 $cat_name = trim($cat_name);
417 $category_nicename = sanitize_title($cat_name);
418 if ( '' === $category_nicename )
420 if ( !$cat_id = term_exists( $cat_name, $taxonomy->name, $parent ) )
421 $cat_id = wp_insert_term( $cat_name, $taxonomy->name, array( 'parent' => $parent ) );
422 if ( is_wp_error( $cat_id ) )
424 else if ( is_array( $cat_id ) )
425 $cat_id = $cat_id['term_id'];
426 $checked_categories[] = $cat_id;
427 if ( $parent ) // Do these all at once in a second
430 wp_terms_checklist( 0, array( 'taxonomy' => $taxonomy->name, 'descendants_and_self' => $cat_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids ));
431 $data = ob_get_contents();
434 'what' => $taxonomy->name,
436 'data' => str_replace( array("\n", "\t"), '', $data),
441 if ( $parent ) { // Foncy - replace the parent and all its children
442 $parent = get_term( $parent, $taxonomy->name );
443 $term_id = $parent->term_id;
445 while ( $parent->parent ) { // get the top parent
446 $parent = get_term( $parent->parent, $taxonomy->name );
447 if ( is_wp_error( $parent ) )
449 $term_id = $parent->term_id;
453 wp_terms_checklist( 0, array('taxonomy' => $taxonomy->name, 'descendants_and_self' => $term_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids));
454 $data = ob_get_contents();
457 'what' => $taxonomy->name,
459 'data' => str_replace( array("\n", "\t"), '', $data),
465 wp_dropdown_categories( array(
466 'taxonomy' => $taxonomy->name, 'hide_empty' => 0, 'name' => 'new'.$taxonomy->name.'_parent', 'orderby' => 'name',
467 'hierarchical' => 1, 'show_option_none' => '— '.$taxonomy->labels->parent_item.' —'
469 $sup = ob_get_contents();
471 $add['supplemental'] = array( 'newcat_parent' => $sup );
473 $x = new WP_Ajax_Response( $add );
478 * Ajax handler for deleting a comment.
482 function wp_ajax_delete_comment() {
483 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
485 if ( !$comment = get_comment( $id ) )
487 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
490 check_ajax_referer( "delete-comment_$id" );
491 $status = wp_get_comment_status( $comment->comment_ID );
494 if ( isset($_POST['trash']) && 1 == $_POST['trash'] ) {
495 if ( 'trash' == $status )
497 $r = wp_trash_comment( $comment->comment_ID );
498 } elseif ( isset($_POST['untrash']) && 1 == $_POST['untrash'] ) {
499 if ( 'trash' != $status )
501 $r = wp_untrash_comment( $comment->comment_ID );
502 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'trash' ) // undo trash, not in trash
504 } elseif ( isset($_POST['spam']) && 1 == $_POST['spam'] ) {
505 if ( 'spam' == $status )
507 $r = wp_spam_comment( $comment->comment_ID );
508 } elseif ( isset($_POST['unspam']) && 1 == $_POST['unspam'] ) {
509 if ( 'spam' != $status )
511 $r = wp_unspam_comment( $comment->comment_ID );
512 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'spam' ) // undo spam, not in spam
514 } elseif ( isset($_POST['delete']) && 1 == $_POST['delete'] ) {
515 $r = wp_delete_comment( $comment->comment_ID );
520 if ( $r ) // Decide if we need to send back '1' or a more complicated response including page links and comment counts
521 _wp_ajax_delete_comment_response( $comment->comment_ID, $delta );
526 * Ajax handler for deleting a tag.
530 function wp_ajax_delete_tag() {
531 $tag_id = (int) $_POST['tag_ID'];
532 check_ajax_referer( "delete-tag_$tag_id" );
534 $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
535 $tax = get_taxonomy($taxonomy);
537 if ( !current_user_can( $tax->cap->delete_terms ) )
540 $tag = get_term( $tag_id, $taxonomy );
541 if ( !$tag || is_wp_error( $tag ) )
544 if ( wp_delete_term($tag_id, $taxonomy))
551 * Ajax handler for deleting a link.
555 function wp_ajax_delete_link() {
556 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
558 check_ajax_referer( "delete-bookmark_$id" );
559 if ( !current_user_can( 'manage_links' ) )
562 $link = get_bookmark( $id );
563 if ( !$link || is_wp_error( $link ) )
566 if ( wp_delete_link( $id ) )
573 * Ajax handler for deleting meta.
577 function wp_ajax_delete_meta() {
578 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
580 check_ajax_referer( "delete-meta_$id" );
581 if ( !$meta = get_metadata_by_mid( 'post', $id ) )
584 if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta', $meta->post_id, $meta->meta_key ) )
586 if ( delete_meta( $meta->meta_id ) )
592 * Ajax handler for deleting a post.
596 * @param string $action Action to perform.
598 function wp_ajax_delete_post( $action ) {
599 if ( empty( $action ) )
600 $action = 'delete-post';
601 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
603 check_ajax_referer( "{$action}_$id" );
604 if ( !current_user_can( 'delete_post', $id ) )
607 if ( !get_post( $id ) )
610 if ( wp_delete_post( $id ) )
617 * Ajax handler for sending a post to the trash.
621 * @param string $action Action to perform.
623 function wp_ajax_trash_post( $action ) {
624 if ( empty( $action ) )
625 $action = 'trash-post';
626 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
628 check_ajax_referer( "{$action}_$id" );
629 if ( !current_user_can( 'delete_post', $id ) )
632 if ( !get_post( $id ) )
635 if ( 'trash-post' == $action )
636 $done = wp_trash_post( $id );
638 $done = wp_untrash_post( $id );
647 * Ajax handler to restore a post from the trash.
651 * @param string $action Action to perform.
653 function wp_ajax_untrash_post( $action ) {
654 if ( empty( $action ) )
655 $action = 'untrash-post';
656 wp_ajax_trash_post( $action );
659 function wp_ajax_delete_page( $action ) {
660 if ( empty( $action ) )
661 $action = 'delete-page';
662 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
664 check_ajax_referer( "{$action}_$id" );
665 if ( !current_user_can( 'delete_page', $id ) )
668 if ( ! get_post( $id ) )
671 if ( wp_delete_post( $id ) )
678 * Ajax handler to dim a comment.
682 function wp_ajax_dim_comment() {
683 $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
685 if ( !$comment = get_comment( $id ) ) {
686 $x = new WP_Ajax_Response( array(
688 'id' => new WP_Error('invalid_comment', sprintf(__('Comment %d does not exist'), $id))
693 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) && ! current_user_can( 'moderate_comments' ) )
696 $current = wp_get_comment_status( $comment->comment_ID );
697 if ( isset( $_POST['new'] ) && $_POST['new'] == $current )
700 check_ajax_referer( "approve-comment_$id" );
701 if ( in_array( $current, array( 'unapproved', 'spam' ) ) )
702 $result = wp_set_comment_status( $comment->comment_ID, 'approve', true );
704 $result = wp_set_comment_status( $comment->comment_ID, 'hold', true );
706 if ( is_wp_error($result) ) {
707 $x = new WP_Ajax_Response( array(
714 // Decide if we need to send back '1' or a more complicated response including page links and comment counts
715 _wp_ajax_delete_comment_response( $comment->comment_ID );
720 * Ajax handler for deleting a link category.
724 * @param string $action Action to perform.
726 function wp_ajax_add_link_category( $action ) {
727 if ( empty( $action ) )
728 $action = 'add-link-category';
729 check_ajax_referer( $action );
730 if ( !current_user_can( 'manage_categories' ) )
732 $names = explode(',', wp_unslash( $_POST['newcat'] ) );
733 $x = new WP_Ajax_Response();
734 foreach ( $names as $cat_name ) {
735 $cat_name = trim($cat_name);
736 $slug = sanitize_title($cat_name);
739 if ( !$cat_id = term_exists( $cat_name, 'link_category' ) )
740 $cat_id = wp_insert_term( $cat_name, 'link_category' );
741 if ( is_wp_error( $cat_id ) )
743 else if ( is_array( $cat_id ) )
744 $cat_id = $cat_id['term_id'];
745 $cat_name = esc_html( $cat_name );
747 'what' => 'link-category',
749 '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>",
757 * Ajax handler to add a tag.
761 function wp_ajax_add_tag() {
762 global $wp_list_table;
764 check_ajax_referer( 'add-tag', '_wpnonce_add-tag' );
765 $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
766 $tax = get_taxonomy($taxonomy);
768 if ( !current_user_can( $tax->cap->edit_terms ) )
771 $x = new WP_Ajax_Response();
773 $tag = wp_insert_term($_POST['tag-name'], $taxonomy, $_POST );
775 if ( !$tag || is_wp_error($tag) || (!$tag = get_term( $tag['term_id'], $taxonomy )) ) {
776 $message = __('An error has occurred. Please reload the page and try again.');
777 if ( is_wp_error($tag) && $tag->get_error_message() )
778 $message = $tag->get_error_message();
781 'what' => 'taxonomy',
782 'data' => new WP_Error('error', $message )
787 $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => $_POST['screen'] ) );
790 if ( is_taxonomy_hierarchical($taxonomy) ) {
791 $level = count( get_ancestors( $tag->term_id, $taxonomy ) );
793 $wp_list_table->single_row( $tag, $level );
794 $noparents = ob_get_clean();
798 $wp_list_table->single_row( $tag );
799 $parents = ob_get_clean();
802 'what' => 'taxonomy',
803 'supplemental' => compact('parents', 'noparents')
807 'position' => $level,
808 'supplemental' => (array) $tag
814 * Ajax handler for getting a tagcloud.
818 function wp_ajax_get_tagcloud() {
819 if ( isset( $_POST['tax'] ) ) {
820 $taxonomy = sanitize_key( $_POST['tax'] );
821 $tax = get_taxonomy( $taxonomy );
824 if ( ! current_user_can( $tax->cap->assign_terms ) )
830 $tags = get_terms( $taxonomy, array( 'number' => 45, 'orderby' => 'count', 'order' => 'DESC' ) );
832 if ( empty( $tags ) )
833 wp_die( $tax->labels->not_found );
835 if ( is_wp_error( $tags ) )
836 wp_die( $tags->get_error_message() );
838 foreach ( $tags as $key => $tag ) {
839 $tags[ $key ]->link = '#';
840 $tags[ $key ]->id = $tag->term_id;
843 // We need raw tag names here, so don't filter the output
844 $return = wp_generate_tag_cloud( $tags, array('filter' => 0) );
846 if ( empty($return) )
855 * Ajax handler for getting comments.
859 * @param string $action Action to perform.
861 function wp_ajax_get_comments( $action ) {
862 global $wp_list_table, $post_id;
863 if ( empty( $action ) )
864 $action = 'get-comments';
866 check_ajax_referer( $action );
868 if ( empty( $post_id ) && ! empty( $_REQUEST['p'] ) ) {
869 $id = absint( $_REQUEST['p'] );
870 if ( ! empty( $id ) )
874 if ( empty( $post_id ) )
877 $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
879 if ( ! current_user_can( 'edit_post', $post_id ) )
882 $wp_list_table->prepare_items();
884 if ( !$wp_list_table->has_items() )
887 $x = new WP_Ajax_Response();
889 foreach ( $wp_list_table->items as $comment ) {
890 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
892 get_comment( $comment );
893 $wp_list_table->single_row( $comment );
895 $comment_list_item = ob_get_contents();
899 'what' => 'comments',
900 'data' => $comment_list_item
906 * Ajax handler for replying to a comment.
910 * @param string $action Action to perform.
912 function wp_ajax_replyto_comment( $action ) {
913 global $wp_list_table;
914 if ( empty( $action ) )
915 $action = 'replyto-comment';
917 check_ajax_referer( $action, '_ajax_nonce-replyto-comment' );
919 $comment_post_ID = (int) $_POST['comment_post_ID'];
920 $post = get_post( $comment_post_ID );
924 if ( !current_user_can( 'edit_post', $comment_post_ID ) )
927 if ( empty( $post->post_status ) )
929 elseif ( in_array($post->post_status, array('draft', 'pending', 'trash') ) )
930 wp_die( __('ERROR: you are replying to a comment on a draft post.') );
932 $user = wp_get_current_user();
933 if ( $user->exists() ) {
934 $user_ID = $user->ID;
935 $comment_author = wp_slash( $user->display_name );
936 $comment_author_email = wp_slash( $user->user_email );
937 $comment_author_url = wp_slash( $user->user_url );
938 $comment_content = trim($_POST['content']);
939 if ( current_user_can( 'unfiltered_html' ) ) {
940 if ( ! isset( $_POST['_wp_unfiltered_html_comment'] ) )
941 $_POST['_wp_unfiltered_html_comment'] = '';
943 if ( wp_create_nonce( 'unfiltered-html-comment' ) != $_POST['_wp_unfiltered_html_comment'] ) {
944 kses_remove_filters(); // start with a clean slate
945 kses_init_filters(); // set up the filters
949 wp_die( __( 'Sorry, you must be logged in to reply to a comment.' ) );
952 if ( '' == $comment_content )
953 wp_die( __( 'ERROR: please type a comment.' ) );
956 if ( isset( $_POST['comment_ID'] ) )
957 $comment_parent = absint( $_POST['comment_ID'] );
958 $comment_auto_approved = false;
959 $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID');
961 // Automatically approve parent comment.
962 if ( !empty($_POST['approve_parent']) ) {
963 $parent = get_comment( $comment_parent );
965 if ( $parent && $parent->comment_approved === '0' && $parent->comment_post_ID == $comment_post_ID ) {
966 if ( wp_set_comment_status( $parent->comment_ID, 'approve' ) )
967 $comment_auto_approved = true;
971 $comment_id = wp_new_comment( $commentdata );
972 $comment = get_comment($comment_id);
973 if ( ! $comment ) wp_die( 1 );
975 $position = ( isset($_POST['position']) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1';
978 if ( isset( $_REQUEST['mode'] ) && 'dashboard' == $_REQUEST['mode'] ) {
979 require_once( ABSPATH . 'wp-admin/includes/dashboard.php' );
980 _wp_dashboard_recent_comments_row( $comment );
982 if ( isset( $_REQUEST['mode'] ) && 'single' == $_REQUEST['mode'] ) {
983 $wp_list_table = _get_list_table('WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
985 $wp_list_table = _get_list_table('WP_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
987 $wp_list_table->single_row( $comment );
989 $comment_list_item = ob_get_clean();
993 'id' => $comment->comment_ID,
994 'data' => $comment_list_item,
995 'position' => $position
998 if ( $comment_auto_approved )
999 $response['supplemental'] = array( 'parent_approved' => $parent->comment_ID );
1001 $x = new WP_Ajax_Response();
1002 $x->add( $response );
1007 * Ajax handler for editing a comment.
1011 function wp_ajax_edit_comment() {
1012 global $wp_list_table;
1014 check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' );
1016 $comment_id = (int) $_POST['comment_ID'];
1017 if ( ! current_user_can( 'edit_comment', $comment_id ) )
1020 if ( '' == $_POST['content'] )
1021 wp_die( __( 'ERROR: please type a comment.' ) );
1023 if ( isset( $_POST['status'] ) )
1024 $_POST['comment_status'] = $_POST['status'];
1027 $position = ( isset($_POST['position']) && (int) $_POST['position']) ? (int) $_POST['position'] : '-1';
1028 $checkbox = ( isset($_POST['checkbox']) && true == $_POST['checkbox'] ) ? 1 : 0;
1029 $wp_list_table = _get_list_table( $checkbox ? 'WP_Comments_List_Table' : 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
1031 $comment = get_comment( $comment_id );
1032 if ( empty( $comment->comment_ID ) )
1036 $wp_list_table->single_row( $comment );
1037 $comment_list_item = ob_get_clean();
1039 $x = new WP_Ajax_Response();
1042 'what' => 'edit_comment',
1043 'id' => $comment->comment_ID,
1044 'data' => $comment_list_item,
1045 'position' => $position
1052 * Ajax handler for adding a menu item.
1056 function wp_ajax_add_menu_item() {
1057 check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1059 if ( ! current_user_can( 'edit_theme_options' ) )
1062 require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1064 // For performance reasons, we omit some object properties from the checklist.
1065 // The following is a hacky way to restore them when adding non-custom items.
1067 $menu_items_data = array();
1068 foreach ( (array) $_POST['menu-item'] as $menu_item_data ) {
1070 ! empty( $menu_item_data['menu-item-type'] ) &&
1071 'custom' != $menu_item_data['menu-item-type'] &&
1072 ! empty( $menu_item_data['menu-item-object-id'] )
1074 switch( $menu_item_data['menu-item-type'] ) {
1076 $_object = get_post( $menu_item_data['menu-item-object-id'] );
1080 $_object = get_term( $menu_item_data['menu-item-object-id'], $menu_item_data['menu-item-object'] );
1084 $_menu_items = array_map( 'wp_setup_nav_menu_item', array( $_object ) );
1085 $_menu_item = array_shift( $_menu_items );
1087 // Restore the missing menu item properties
1088 $menu_item_data['menu-item-description'] = $_menu_item->description;
1091 $menu_items_data[] = $menu_item_data;
1094 $item_ids = wp_save_nav_menu_items( 0, $menu_items_data );
1095 if ( is_wp_error( $item_ids ) )
1098 $menu_items = array();
1100 foreach ( (array) $item_ids as $menu_item_id ) {
1101 $menu_obj = get_post( $menu_item_id );
1102 if ( ! empty( $menu_obj->ID ) ) {
1103 $menu_obj = wp_setup_nav_menu_item( $menu_obj );
1104 $menu_obj->label = $menu_obj->title; // don't show "(pending)" in ajax-added items
1105 $menu_items[] = $menu_obj;
1109 /** This filter is documented in wp-admin/includes/nav-menu.php */
1110 $walker_class_name = apply_filters( 'wp_edit_nav_menu_walker', 'Walker_Nav_Menu_Edit', $_POST['menu'] );
1112 if ( ! class_exists( $walker_class_name ) )
1115 if ( ! empty( $menu_items ) ) {
1120 'link_before' => '',
1121 'walker' => new $walker_class_name,
1123 echo walk_nav_menu_tree( $menu_items, 0, (object) $args );
1129 * Ajax handler for adding meta.
1133 function wp_ajax_add_meta() {
1134 check_ajax_referer( 'add-meta', '_ajax_nonce-add-meta' );
1136 $pid = (int) $_POST['post_id'];
1137 $post = get_post( $pid );
1139 if ( isset($_POST['metakeyselect']) || isset($_POST['metakeyinput']) ) {
1140 if ( !current_user_can( 'edit_post', $pid ) )
1142 if ( isset($_POST['metakeyselect']) && '#NONE#' == $_POST['metakeyselect'] && empty($_POST['metakeyinput']) )
1144 if ( $post->post_status == 'auto-draft' ) {
1145 $save_POST = $_POST; // Backup $_POST
1146 $_POST = array(); // Make it empty for edit_post()
1147 $_POST['action'] = 'draft'; // Warning fix
1148 $_POST['post_ID'] = $pid;
1149 $_POST['post_type'] = $post->post_type;
1150 $_POST['post_status'] = 'draft';
1151 $now = current_time('timestamp', 1);
1152 $_POST['post_title'] = sprintf( __( 'Draft created on %1$s at %2$s' ), date( get_option( 'date_format' ), $now ), date( get_option( 'time_format' ), $now ) );
1154 if ( $pid = edit_post() ) {
1155 if ( is_wp_error( $pid ) ) {
1156 $x = new WP_Ajax_Response( array(
1162 $_POST = $save_POST; // Now we can restore original $_POST again
1163 if ( !$mid = add_meta( $pid ) )
1164 wp_die( __( 'Please provide a custom field value.' ) );
1168 } else if ( !$mid = add_meta( $pid ) ) {
1169 wp_die( __( 'Please provide a custom field value.' ) );
1172 $meta = get_metadata_by_mid( 'post', $mid );
1173 $pid = (int) $meta->post_id;
1174 $meta = get_object_vars( $meta );
1175 $x = new WP_Ajax_Response( array(
1178 'data' => _list_meta_row( $meta, $c ),
1180 'supplemental' => array('postid' => $pid)
1183 $mid = (int) key( $_POST['meta'] );
1184 $key = wp_unslash( $_POST['meta'][$mid]['key'] );
1185 $value = wp_unslash( $_POST['meta'][$mid]['value'] );
1186 if ( '' == trim($key) )
1187 wp_die( __( 'Please provide a custom field name.' ) );
1188 if ( '' == trim($value) )
1189 wp_die( __( 'Please provide a custom field value.' ) );
1190 if ( ! $meta = get_metadata_by_mid( 'post', $mid ) )
1191 wp_die( 0 ); // if meta doesn't exist
1192 if ( is_protected_meta( $meta->meta_key, 'post' ) || is_protected_meta( $key, 'post' ) ||
1193 ! current_user_can( 'edit_post_meta', $meta->post_id, $meta->meta_key ) ||
1194 ! current_user_can( 'edit_post_meta', $meta->post_id, $key ) )
1196 if ( $meta->meta_value != $value || $meta->meta_key != $key ) {
1197 if ( !$u = update_metadata_by_mid( 'post', $mid, $value, $key ) )
1198 wp_die( 0 ); // We know meta exists; we also know it's unchanged (or DB error, in which case there are bigger problems).
1201 $x = new WP_Ajax_Response( array(
1203 'id' => $mid, 'old_id' => $mid,
1204 'data' => _list_meta_row( array(
1206 'meta_value' => $value,
1210 'supplemental' => array('postid' => $meta->post_id)
1217 * Ajax handler for adding a user.
1221 * @param string $action Action to perform.
1223 function wp_ajax_add_user( $action ) {
1224 global $wp_list_table;
1225 if ( empty( $action ) )
1226 $action = 'add-user';
1228 check_ajax_referer( $action );
1229 if ( ! current_user_can('create_users') )
1231 if ( ! $user_id = edit_user() ) {
1233 } elseif ( is_wp_error( $user_id ) ) {
1234 $x = new WP_Ajax_Response( array(
1240 $user_object = get_userdata( $user_id );
1242 $wp_list_table = _get_list_table('WP_Users_List_Table');
1244 $role = current( $user_object->roles );
1246 $x = new WP_Ajax_Response( array(
1249 'data' => $wp_list_table->single_row( $user_object, '', $role ),
1250 'supplemental' => array(
1251 'show-link' => sprintf(__( 'User <a href="#%s">%s</a> added' ), "user-$user_id", $user_object->user_login),
1259 * Ajax handler for closed post boxes.
1263 function wp_ajax_closed_postboxes() {
1264 check_ajax_referer( 'closedpostboxes', 'closedpostboxesnonce' );
1265 $closed = isset( $_POST['closed'] ) ? explode( ',', $_POST['closed']) : array();
1266 $closed = array_filter($closed);
1268 $hidden = isset( $_POST['hidden'] ) ? explode( ',', $_POST['hidden']) : array();
1269 $hidden = array_filter($hidden);
1271 $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1273 if ( $page != sanitize_key( $page ) )
1276 if ( ! $user = wp_get_current_user() )
1279 if ( is_array($closed) )
1280 update_user_option($user->ID, "closedpostboxes_$page", $closed, true);
1282 if ( is_array($hidden) ) {
1283 $hidden = array_diff( $hidden, array('submitdiv', 'linksubmitdiv', 'manage-menu', 'create-menu') ); // postboxes that are always shown
1284 update_user_option($user->ID, "metaboxhidden_$page", $hidden, true);
1291 * Ajax handler for hidden columns.
1295 function wp_ajax_hidden_columns() {
1296 check_ajax_referer( 'screen-options-nonce', 'screenoptionnonce' );
1297 $hidden = explode( ',', isset( $_POST['hidden'] ) ? $_POST['hidden'] : '' );
1298 $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1300 if ( $page != sanitize_key( $page ) )
1303 if ( ! $user = wp_get_current_user() )
1306 if ( is_array($hidden) )
1307 update_user_option($user->ID, "manage{$page}columnshidden", $hidden, true);
1313 * Ajax handler for updating whether to display the welcome panel.
1317 function wp_ajax_update_welcome_panel() {
1318 check_ajax_referer( 'welcome-panel-nonce', 'welcomepanelnonce' );
1320 if ( ! current_user_can( 'edit_theme_options' ) )
1323 update_user_meta( get_current_user_id(), 'show_welcome_panel', empty( $_POST['visible'] ) ? 0 : 1 );
1329 * Ajax handler for retrieving menu meta boxes.
1333 function wp_ajax_menu_get_metabox() {
1334 if ( ! current_user_can( 'edit_theme_options' ) )
1337 require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1339 if ( isset( $_POST['item-type'] ) && 'post_type' == $_POST['item-type'] ) {
1341 $callback = 'wp_nav_menu_item_post_type_meta_box';
1342 $items = (array) get_post_types( array( 'show_in_nav_menus' => true ), 'object' );
1343 } elseif ( isset( $_POST['item-type'] ) && 'taxonomy' == $_POST['item-type'] ) {
1345 $callback = 'wp_nav_menu_item_taxonomy_meta_box';
1346 $items = (array) get_taxonomies( array( 'show_ui' => true ), 'object' );
1349 if ( ! empty( $_POST['item-object'] ) && isset( $items[$_POST['item-object']] ) ) {
1350 $menus_meta_box_object = $items[ $_POST['item-object'] ];
1352 /** This filter is documented in wp-admin/includes/nav-menu.php */
1353 $item = apply_filters( 'nav_menu_meta_box_object', $menus_meta_box_object );
1355 call_user_func_array($callback, array(
1358 'id' => 'add-' . $item->name,
1359 'title' => $item->labels->name,
1360 'callback' => $callback,
1365 $markup = ob_get_clean();
1367 echo json_encode(array(
1368 'replace-id' => $type . '-' . $item->name,
1369 'markup' => $markup,
1377 * Ajax handler for internal linking.
1381 function wp_ajax_wp_link_ajax() {
1382 check_ajax_referer( 'internal-linking', '_ajax_linking_nonce' );
1386 if ( isset( $_POST['search'] ) )
1387 $args['s'] = wp_unslash( $_POST['search'] );
1388 $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
1390 require(ABSPATH . WPINC . '/class-wp-editor.php');
1391 $results = _WP_Editors::wp_link_query( $args );
1393 if ( ! isset( $results ) )
1396 echo json_encode( $results );
1403 * Ajax handler for menu locations save.
1407 function wp_ajax_menu_locations_save() {
1408 if ( ! current_user_can( 'edit_theme_options' ) )
1410 check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1411 if ( ! isset( $_POST['menu-locations'] ) )
1413 set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_POST['menu-locations'] ) );
1418 * Ajax handler for saving the meta box order.
1422 function wp_ajax_meta_box_order() {
1423 check_ajax_referer( 'meta-box-order' );
1424 $order = isset( $_POST['order'] ) ? (array) $_POST['order'] : false;
1425 $page_columns = isset( $_POST['page_columns'] ) ? $_POST['page_columns'] : 'auto';
1427 if ( $page_columns != 'auto' )
1428 $page_columns = (int) $page_columns;
1430 $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1432 if ( $page != sanitize_key( $page ) )
1435 if ( ! $user = wp_get_current_user() )
1439 update_user_option($user->ID, "meta-box-order_$page", $order, true);
1441 if ( $page_columns )
1442 update_user_option($user->ID, "screen_layout_$page", $page_columns, true);
1448 * Ajax handler for menu quick searching.
1452 function wp_ajax_menu_quick_search() {
1453 if ( ! current_user_can( 'edit_theme_options' ) )
1456 require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1458 _wp_ajax_menu_quick_search( $_POST );
1464 * Ajax handler to retrieve a permalink.
1468 function wp_ajax_get_permalink() {
1469 check_ajax_referer( 'getpermalink', 'getpermalinknonce' );
1470 $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1471 wp_die( add_query_arg( array( 'preview' => 'true' ), get_permalink( $post_id ) ) );
1475 * Ajax handler to retrieve a sample permalink.
1479 function wp_ajax_sample_permalink() {
1480 check_ajax_referer( 'samplepermalink', 'samplepermalinknonce' );
1481 $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1482 $title = isset($_POST['new_title'])? $_POST['new_title'] : '';
1483 $slug = isset($_POST['new_slug'])? $_POST['new_slug'] : null;
1484 wp_die( get_sample_permalink_html( $post_id, $title, $slug ) );
1488 * Ajax handler for quick edit saving for a post.
1492 function wp_ajax_inline_save() {
1493 global $wp_list_table;
1495 check_ajax_referer( 'inlineeditnonce', '_inline_edit' );
1497 if ( ! isset($_POST['post_ID']) || ! ( $post_ID = (int) $_POST['post_ID'] ) )
1500 if ( 'page' == $_POST['post_type'] ) {
1501 if ( ! current_user_can( 'edit_page', $post_ID ) )
1502 wp_die( __( 'You are not allowed to edit this page.' ) );
1504 if ( ! current_user_can( 'edit_post', $post_ID ) )
1505 wp_die( __( 'You are not allowed to edit this post.' ) );
1508 if ( $last = wp_check_post_lock( $post_ID ) ) {
1509 $last_user = get_userdata( $last );
1510 $last_user_name = $last_user ? $last_user->display_name : __( 'Someone' );
1511 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 ) );
1517 $post = get_post( $post_ID, ARRAY_A );
1519 // Since it's coming from the database.
1520 $post = wp_slash($post);
1522 $data['content'] = $post['post_content'];
1523 $data['excerpt'] = $post['post_excerpt'];
1526 $data['user_ID'] = get_current_user_id();
1528 if ( isset($data['post_parent']) )
1529 $data['parent_id'] = $data['post_parent'];
1532 if ( isset($data['keep_private']) && 'private' == $data['keep_private'] )
1533 $data['post_status'] = 'private';
1535 $data['post_status'] = $data['_status'];
1537 if ( empty($data['comment_status']) )
1538 $data['comment_status'] = 'closed';
1539 if ( empty($data['ping_status']) )
1540 $data['ping_status'] = 'closed';
1542 // Hack: wp_unique_post_slug() doesn't work for drafts, so we will fake that our post is published.
1543 if ( ! empty( $data['post_name'] ) && in_array( $post['post_status'], array( 'draft', 'pending' ) ) ) {
1544 $post['post_status'] = 'publish';
1545 $data['post_name'] = wp_unique_post_slug( $data['post_name'], $post['ID'], $post['post_status'], $post['post_type'], $post['post_parent'] );
1551 $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => $_POST['screen'] ) );
1554 $request_post = array( get_post( $_POST['post_ID'] ) );
1555 $parent = $request_post[0]->post_parent;
1557 while ( $parent > 0 ) {
1558 $parent_post = get_post( $parent );
1559 $parent = $parent_post->post_parent;
1563 $wp_list_table->display_rows( array( get_post( $_POST['post_ID'] ) ), $level );
1569 * Ajax handler for quick edit saving for a term.
1573 function wp_ajax_inline_save_tax() {
1574 global $wp_list_table;
1576 check_ajax_referer( 'taxinlineeditnonce', '_inline_edit' );
1578 $taxonomy = sanitize_key( $_POST['taxonomy'] );
1579 $tax = get_taxonomy( $taxonomy );
1583 if ( ! current_user_can( $tax->cap->edit_terms ) )
1586 $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => 'edit-' . $taxonomy ) );
1588 if ( ! isset($_POST['tax_ID']) || ! ( $id = (int) $_POST['tax_ID'] ) )
1591 $tag = get_term( $id, $taxonomy );
1592 $_POST['description'] = $tag->description;
1594 $updated = wp_update_term($id, $taxonomy, $_POST);
1595 if ( $updated && !is_wp_error($updated) ) {
1596 $tag = get_term( $updated['term_id'], $taxonomy );
1597 if ( !$tag || is_wp_error( $tag ) ) {
1598 if ( is_wp_error($tag) && $tag->get_error_message() )
1599 wp_die( $tag->get_error_message() );
1600 wp_die( __( 'Item not updated.' ) );
1603 if ( is_wp_error($updated) && $updated->get_error_message() )
1604 wp_die( $updated->get_error_message() );
1605 wp_die( __( 'Item not updated.' ) );
1608 $parent = $tag->parent;
1609 while ( $parent > 0 ) {
1610 $parent_tag = get_term( $parent, $taxonomy );
1611 $parent = $parent_tag->parent;
1614 $wp_list_table->single_row( $tag, $level );
1619 * Ajax handler for finding posts.
1623 function wp_ajax_find_posts() {
1624 check_ajax_referer( 'find-posts' );
1626 $post_types = get_post_types( array( 'public' => true ), 'objects' );
1627 unset( $post_types['attachment'] );
1629 $s = wp_unslash( $_POST['ps'] );
1631 'post_type' => array_keys( $post_types ),
1632 'post_status' => 'any',
1633 'posts_per_page' => 50,
1638 $posts = get_posts( $args );
1641 wp_send_json_error( __( 'No items found.' ) );
1644 $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>';
1646 foreach ( $posts as $post ) {
1647 $title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
1648 $alt = ( 'alternate' == $alt ) ? '' : 'alternate';
1650 switch ( $post->post_status ) {
1653 $stat = __('Published');
1656 $stat = __('Scheduled');
1659 $stat = __('Pending Review');
1662 $stat = __('Draft');
1666 if ( '0000-00-00 00:00:00' == $post->post_date ) {
1669 /* translators: date format in table columns, see http://php.net/date */
1670 $time = mysql2date(__('Y/m/d'), $post->post_date);
1673 $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>';
1674 $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";
1677 $html .= '</tbody></table>';
1679 wp_send_json_success( $html );
1683 * Ajax handler for saving the widgets order.
1687 function wp_ajax_widgets_order() {
1688 check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1690 if ( !current_user_can('edit_theme_options') )
1693 unset( $_POST['savewidgets'], $_POST['action'] );
1695 // Save widgets order for all sidebars.
1696 if ( is_array($_POST['sidebars']) ) {
1697 $sidebars = array();
1698 foreach ( $_POST['sidebars'] as $key => $val ) {
1700 if ( !empty($val) ) {
1701 $val = explode(',', $val);
1702 foreach ( $val as $k => $v ) {
1703 if ( strpos($v, 'widget-') === false )
1706 $sb[$k] = substr($v, strpos($v, '_') + 1);
1709 $sidebars[$key] = $sb;
1711 wp_set_sidebars_widgets($sidebars);
1719 * Ajax handler for saving a widget.
1723 function wp_ajax_save_widget() {
1724 global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
1726 check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1728 if ( !current_user_can('edit_theme_options') || !isset($_POST['id_base']) )
1731 unset( $_POST['savewidgets'], $_POST['action'] );
1734 * Fires early when editing the widgets displayed in sidebars.
1738 do_action( 'load-widgets.php' );
1741 * Fires early when editing the widgets displayed in sidebars.
1745 do_action( 'widgets.php' );
1747 /** This action is documented in wp-admin/widgets.php */
1748 do_action( 'sidebar_admin_setup' );
1750 $id_base = $_POST['id_base'];
1751 $widget_id = $_POST['widget-id'];
1752 $sidebar_id = $_POST['sidebar'];
1753 $multi_number = !empty($_POST['multi_number']) ? (int) $_POST['multi_number'] : 0;
1754 $settings = isset($_POST['widget-' . $id_base]) && is_array($_POST['widget-' . $id_base]) ? $_POST['widget-' . $id_base] : false;
1755 $error = '<p>' . __('An error has occurred. Please reload the page and try again.') . '</p>';
1757 $sidebars = wp_get_sidebars_widgets();
1758 $sidebar = isset($sidebars[$sidebar_id]) ? $sidebars[$sidebar_id] : array();
1761 if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1763 if ( !isset($wp_registered_widgets[$widget_id]) )
1766 $sidebar = array_diff( $sidebar, array($widget_id) );
1767 $_POST = array('sidebar' => $sidebar_id, 'widget-' . $id_base => array(), 'the-widget-id' => $widget_id, 'delete_widget' => '1');
1768 } elseif ( $settings && preg_match( '/__i__|%i%/', key($settings) ) ) {
1769 if ( !$multi_number )
1772 $_POST['widget-' . $id_base] = array( $multi_number => array_shift($settings) );
1773 $widget_id = $id_base . '-' . $multi_number;
1774 $sidebar[] = $widget_id;
1776 $_POST['widget-id'] = $sidebar;
1778 foreach ( (array) $wp_registered_widget_updates as $name => $control ) {
1780 if ( $name == $id_base ) {
1781 if ( !is_callable( $control['callback'] ) )
1785 call_user_func_array( $control['callback'], $control['params'] );
1791 if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1792 $sidebars[$sidebar_id] = $sidebar;
1793 wp_set_sidebars_widgets($sidebars);
1794 echo "deleted:$widget_id";
1798 if ( !empty($_POST['add_new']) )
1801 if ( $form = $wp_registered_widget_controls[$widget_id] )
1802 call_user_func_array( $form['callback'], $form['params'] );
1808 * Ajax handler for saving a widget.
1812 function wp_ajax_update_widget() {
1813 global $wp_customize;
1814 $wp_customize->widgets->wp_ajax_update_widget();
1818 * Ajax handler for uploading attachments
1822 function wp_ajax_upload_attachment() {
1823 check_ajax_referer( 'media-form' );
1825 if ( ! current_user_can( 'upload_files' ) )
1828 if ( isset( $_REQUEST['post_id'] ) ) {
1829 $post_id = $_REQUEST['post_id'];
1830 if ( ! current_user_can( 'edit_post', $post_id ) )
1836 $post_data = isset( $_REQUEST['post_data'] ) ? $_REQUEST['post_data'] : array();
1838 // If the context is custom header or background, make sure the uploaded file is an image.
1839 if ( isset( $post_data['context'] ) && in_array( $post_data['context'], array( 'custom-header', 'custom-background' ) ) ) {
1840 $wp_filetype = wp_check_filetype_and_ext( $_FILES['async-upload']['tmp_name'], $_FILES['async-upload']['name'], false );
1841 if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) {
1842 echo json_encode( array(
1845 'message' => __( 'The uploaded file is not a valid image. Please try again.' ),
1846 'filename' => $_FILES['async-upload']['name'],
1854 $attachment_id = media_handle_upload( 'async-upload', $post_id, $post_data );
1856 if ( is_wp_error( $attachment_id ) ) {
1857 echo json_encode( array(
1860 'message' => $attachment_id->get_error_message(),
1861 'filename' => $_FILES['async-upload']['name'],
1868 if ( isset( $post_data['context'] ) && isset( $post_data['theme'] ) ) {
1869 if ( 'custom-background' === $post_data['context'] )
1870 update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', $post_data['theme'] );
1872 if ( 'custom-header' === $post_data['context'] )
1873 update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', $post_data['theme'] );
1876 if ( ! $attachment = wp_prepare_attachment_for_js( $attachment_id ) )
1879 echo json_encode( array(
1881 'data' => $attachment,
1888 * Ajax handler for image editing.
1892 function wp_ajax_image_editor() {
1893 $attachment_id = intval($_POST['postid']);
1894 if ( empty($attachment_id) || !current_user_can('edit_post', $attachment_id) )
1897 check_ajax_referer( "image_editor-$attachment_id" );
1898 include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
1901 switch ( $_POST['do'] ) {
1903 $msg = wp_save_image($attachment_id);
1904 $msg = json_encode($msg);
1908 $msg = wp_save_image($attachment_id);
1911 $msg = wp_restore_image($attachment_id);
1915 wp_image_editor($attachment_id, $msg);
1920 * Ajax handler for setting the featured image.
1924 function wp_ajax_set_post_thumbnail() {
1925 $json = ! empty( $_REQUEST['json'] ); // New-style request
1927 $post_ID = intval( $_POST['post_id'] );
1928 if ( ! current_user_can( 'edit_post', $post_ID ) )
1931 $thumbnail_id = intval( $_POST['thumbnail_id'] );
1934 check_ajax_referer( "update-post_$post_ID" );
1936 check_ajax_referer( "set_post_thumbnail-$post_ID" );
1938 if ( $thumbnail_id == '-1' ) {
1939 if ( delete_post_thumbnail( $post_ID ) ) {
1940 $return = _wp_post_thumbnail_html( null, $post_ID );
1941 $json ? wp_send_json_success( $return ) : wp_die( $return );
1947 if ( set_post_thumbnail( $post_ID, $thumbnail_id ) ) {
1948 $return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID );
1949 $json ? wp_send_json_success( $return ) : wp_die( $return );
1956 * AJAX handler for setting the featured image for an attachment.
1960 * @see set_post_thumbnail()
1962 function wp_ajax_set_attachment_thumbnail() {
1963 if ( empty( $_POST['urls'] ) || ! is_array( $_POST['urls'] ) ) {
1964 wp_send_json_error();
1967 $thumbnail_id = (int) $_POST['thumbnail_id'];
1968 if ( empty( $thumbnail_id ) ) {
1969 wp_send_json_error();
1972 $post_ids = array();
1973 // For each URL, try to find its corresponding post ID.
1974 foreach ( $_POST['urls'] as $url ) {
1975 $post_id = attachment_url_to_postid( $url );
1976 if ( ! empty( $post_id ) ) {
1977 $post_ids[] = $post_id;
1981 if ( empty( $post_ids ) ) {
1982 wp_send_json_error();
1986 // For each found attachment, set its thumbnail.
1987 foreach ( $post_ids as $post_id ) {
1988 if ( ! current_user_can( 'edit_post', $post_id ) ) {
1992 if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) {
1997 if ( 0 === $success ) {
1998 wp_send_json_error();
2000 wp_send_json_success();
2003 wp_send_json_error();
2007 * Ajax handler for date formatting.
2011 function wp_ajax_date_format() {
2012 wp_die( date_i18n( sanitize_option( 'date_format', wp_unslash( $_POST['date'] ) ) ) );
2016 * Ajax handler for time formatting.
2020 function wp_ajax_time_format() {
2021 wp_die( date_i18n( sanitize_option( 'time_format', wp_unslash( $_POST['date'] ) ) ) );
2025 * Ajax handler for saving posts from the fullscreen editor.
2029 function wp_ajax_wp_fullscreen_save_post() {
2030 $post_id = isset( $_POST['post_ID'] ) ? (int) $_POST['post_ID'] : 0;
2035 $post = get_post( $post_id );
2037 check_ajax_referer('update-post_' . $post_id, '_wpnonce');
2039 $post_id = edit_post();
2041 if ( is_wp_error( $post_id ) ) {
2042 wp_send_json_error();
2046 $last_date = mysql2date( get_option('date_format'), $post->post_modified );
2047 $last_time = mysql2date( get_option('time_format'), $post->post_modified );
2049 $last_date = date_i18n( get_option('date_format') );
2050 $last_time = date_i18n( get_option('time_format') );
2053 if ( $last_id = get_post_meta( $post_id, '_edit_last', true ) ) {
2054 $last_user = get_userdata( $last_id );
2055 $last_edited = sprintf( __('Last edited by %1$s on %2$s at %3$s'), esc_html( $last_user->display_name ), $last_date, $last_time );
2057 $last_edited = sprintf( __('Last edited on %1$s at %2$s'), $last_date, $last_time );
2060 wp_send_json_success( array( 'last_edited' => $last_edited ) );
2064 * Ajax handler for removing a post lock.
2068 function wp_ajax_wp_remove_post_lock() {
2069 if ( empty( $_POST['post_ID'] ) || empty( $_POST['active_post_lock'] ) )
2071 $post_id = (int) $_POST['post_ID'];
2072 if ( ! $post = get_post( $post_id ) )
2075 check_ajax_referer( 'update-post_' . $post_id );
2077 if ( ! current_user_can( 'edit_post', $post_id ) )
2080 $active_lock = array_map( 'absint', explode( ':', $_POST['active_post_lock'] ) );
2081 if ( $active_lock[1] != get_current_user_id() )
2085 * Filter the post lock window duration.
2089 * @param int $interval The interval in seconds the post lock duration
2090 * should last, plus 5 seconds. Default 150.
2092 $new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 150 ) + 5 ) . ':' . $active_lock[1];
2093 update_post_meta( $post_id, '_edit_lock', $new_lock, implode( ':', $active_lock ) );
2098 * Ajax handler for dismissing a WordPress pointer.
2102 function wp_ajax_dismiss_wp_pointer() {
2103 $pointer = $_POST['pointer'];
2104 if ( $pointer != sanitize_key( $pointer ) )
2107 // check_ajax_referer( 'dismiss-pointer_' . $pointer );
2109 $dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) );
2111 if ( in_array( $pointer, $dismissed ) )
2114 $dismissed[] = $pointer;
2115 $dismissed = implode( ',', $dismissed );
2117 update_user_meta( get_current_user_id(), 'dismissed_wp_pointers', $dismissed );
2122 * Ajax handler for getting an attachment.
2126 function wp_ajax_get_attachment() {
2127 if ( ! isset( $_REQUEST['id'] ) )
2128 wp_send_json_error();
2130 if ( ! $id = absint( $_REQUEST['id'] ) )
2131 wp_send_json_error();
2133 if ( ! $post = get_post( $id ) )
2134 wp_send_json_error();
2136 if ( 'attachment' != $post->post_type )
2137 wp_send_json_error();
2139 if ( ! current_user_can( 'upload_files' ) )
2140 wp_send_json_error();
2142 if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2143 wp_send_json_error();
2145 wp_send_json_success( $attachment );
2149 * Ajax handler for querying for attachments.
2153 function wp_ajax_query_attachments() {
2154 if ( ! current_user_can( 'upload_files' ) )
2155 wp_send_json_error();
2157 $query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array();
2158 $query = array_intersect_key( $query, array_flip( array(
2159 's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type',
2160 'post_parent', 'post__in', 'post__not_in', 'year', 'monthnum'
2163 $query['post_type'] = 'attachment';
2165 && ! empty( $_REQUEST['query']['post_status'] )
2166 && 'trash' === $_REQUEST['query']['post_status'] ) {
2167 $query['post_status'] = 'trash';
2169 $query['post_status'] = 'inherit';
2172 if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) )
2173 $query['post_status'] .= ',private';
2176 * Filter the arguments passed to WP_Query during an AJAX
2177 * call for querying attachments.
2181 * @see WP_Query::parse_query()
2183 * @param array $query An array of query variables.
2185 $query = apply_filters( 'ajax_query_attachments_args', $query );
2186 $query = new WP_Query( $query );
2188 $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts );
2189 $posts = array_filter( $posts );
2191 wp_send_json_success( $posts );
2195 * Ajax handler for saving attachment attributes.
2199 function wp_ajax_save_attachment() {
2200 if ( ! isset( $_REQUEST['id'] ) || ! isset( $_REQUEST['changes'] ) )
2201 wp_send_json_error();
2203 if ( ! $id = absint( $_REQUEST['id'] ) )
2204 wp_send_json_error();
2206 check_ajax_referer( 'update-post_' . $id, 'nonce' );
2208 if ( ! current_user_can( 'edit_post', $id ) )
2209 wp_send_json_error();
2211 $changes = $_REQUEST['changes'];
2212 $post = get_post( $id, ARRAY_A );
2214 if ( 'attachment' != $post['post_type'] )
2215 wp_send_json_error();
2217 if ( isset( $changes['title'] ) )
2218 $post['post_title'] = $changes['title'];
2220 if ( isset( $changes['caption'] ) )
2221 $post['post_excerpt'] = $changes['caption'];
2223 if ( isset( $changes['description'] ) )
2224 $post['post_content'] = $changes['description'];
2226 if ( MEDIA_TRASH && isset( $changes['status'] ) )
2227 $post['post_status'] = $changes['status'];
2229 if ( isset( $changes['alt'] ) ) {
2230 $alt = wp_unslash( $changes['alt'] );
2231 if ( $alt != get_post_meta( $id, '_wp_attachment_image_alt', true ) ) {
2232 $alt = wp_strip_all_tags( $alt, true );
2233 update_post_meta( $id, '_wp_attachment_image_alt', wp_slash( $alt ) );
2237 if ( 0 === strpos( $post['post_mime_type'], 'audio/' ) ) {
2239 $id3data = wp_get_attachment_metadata( $post['ID'] );
2240 if ( ! is_array( $id3data ) ) {
2244 foreach ( wp_get_attachment_id3_keys( (object) $post, 'edit' ) as $key => $label ) {
2245 if ( isset( $changes[ $key ] ) ) {
2247 $id3data[ $key ] = sanitize_text_field( wp_unslash( $changes[ $key ] ) );
2252 wp_update_attachment_metadata( $id, $id3data );
2256 if ( MEDIA_TRASH && isset( $changes['status'] ) && 'trash' === $changes['status'] ) {
2257 wp_delete_post( $id );
2259 wp_update_post( $post );
2262 wp_send_json_success();
2266 * Ajax handler for saving backwards compatible attachment attributes.
2270 function wp_ajax_save_attachment_compat() {
2271 if ( ! isset( $_REQUEST['id'] ) )
2272 wp_send_json_error();
2274 if ( ! $id = absint( $_REQUEST['id'] ) )
2275 wp_send_json_error();
2277 if ( empty( $_REQUEST['attachments'] ) || empty( $_REQUEST['attachments'][ $id ] ) )
2278 wp_send_json_error();
2279 $attachment_data = $_REQUEST['attachments'][ $id ];
2281 check_ajax_referer( 'update-post_' . $id, 'nonce' );
2283 if ( ! current_user_can( 'edit_post', $id ) )
2284 wp_send_json_error();
2286 $post = get_post( $id, ARRAY_A );
2288 if ( 'attachment' != $post['post_type'] )
2289 wp_send_json_error();
2291 /** This filter is documented in wp-admin/includes/media.php */
2292 $post = apply_filters( 'attachment_fields_to_save', $post, $attachment_data );
2294 if ( isset( $post['errors'] ) ) {
2295 $errors = $post['errors']; // @todo return me and display me!
2296 unset( $post['errors'] );
2299 wp_update_post( $post );
2301 foreach ( get_attachment_taxonomies( $post ) as $taxonomy ) {
2302 if ( isset( $attachment_data[ $taxonomy ] ) )
2303 wp_set_object_terms( $id, array_map( 'trim', preg_split( '/,+/', $attachment_data[ $taxonomy ] ) ), $taxonomy, false );
2306 if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2307 wp_send_json_error();
2309 wp_send_json_success( $attachment );
2313 * Ajax handler for saving the attachment order.
2317 function wp_ajax_save_attachment_order() {
2318 if ( ! isset( $_REQUEST['post_id'] ) )
2319 wp_send_json_error();
2321 if ( ! $post_id = absint( $_REQUEST['post_id'] ) )
2322 wp_send_json_error();
2324 if ( empty( $_REQUEST['attachments'] ) )
2325 wp_send_json_error();
2327 check_ajax_referer( 'update-post_' . $post_id, 'nonce' );
2329 $attachments = $_REQUEST['attachments'];
2331 if ( ! current_user_can( 'edit_post', $post_id ) )
2332 wp_send_json_error();
2334 foreach ( $attachments as $attachment_id => $menu_order ) {
2335 if ( ! current_user_can( 'edit_post', $attachment_id ) )
2337 if ( ! $attachment = get_post( $attachment_id ) )
2339 if ( 'attachment' != $attachment->post_type )
2342 wp_update_post( array( 'ID' => $attachment_id, 'menu_order' => $menu_order ) );
2345 wp_send_json_success();
2349 * Ajax handler for sending an attachment to the editor.
2351 * Generates the HTML to send an attachment to the editor.
2352 * Backwards compatible with the media_send_to_editor filter
2353 * and the chain of filters that follow.
2357 function wp_ajax_send_attachment_to_editor() {
2358 check_ajax_referer( 'media-send-to-editor', 'nonce' );
2360 $attachment = wp_unslash( $_POST['attachment'] );
2362 $id = intval( $attachment['id'] );
2364 if ( ! $post = get_post( $id ) )
2365 wp_send_json_error();
2367 if ( 'attachment' != $post->post_type )
2368 wp_send_json_error();
2370 if ( current_user_can( 'edit_post', $id ) ) {
2371 // If this attachment is unattached, attach it. Primarily a back compat thing.
2372 if ( 0 == $post->post_parent && $insert_into_post_id = intval( $_POST['post_id'] ) ) {
2373 wp_update_post( array( 'ID' => $id, 'post_parent' => $insert_into_post_id ) );
2378 $html = isset( $attachment['post_title'] ) ? $attachment['post_title'] : '';
2379 if ( ! empty( $attachment['url'] ) ) {
2380 $url = $attachment['url'];
2381 if ( strpos( $url, 'attachment_id') || get_attachment_link( $id ) == $url )
2382 $rel = ' rel="attachment wp-att-' . $id . '"';
2383 $html = '<a href="' . esc_url( $url ) . '"' . $rel . '>' . $html . '</a>';
2386 remove_filter( 'media_send_to_editor', 'image_media_send_to_editor' );
2388 if ( 'image' === substr( $post->post_mime_type, 0, 5 ) ) {
2389 $align = isset( $attachment['align'] ) ? $attachment['align'] : 'none';
2390 $size = isset( $attachment['image-size'] ) ? $attachment['image-size'] : 'medium';
2391 $alt = isset( $attachment['image_alt'] ) ? $attachment['image_alt'] : '';
2392 $caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : '';
2393 $title = ''; // We no longer insert title tags into <img> tags, as they are redundant.
2394 $html = get_image_send_to_editor( $id, $caption, $title, $align, $url, (bool) $rel, $size, $alt );
2395 } elseif ( 'video' === substr( $post->post_mime_type, 0, 5 ) || 'audio' === substr( $post->post_mime_type, 0, 5 ) ) {
2396 $html = stripslashes_deep( $_POST['html'] );
2399 /** This filter is documented in wp-admin/includes/media.php */
2400 $html = apply_filters( 'media_send_to_editor', $html, $id, $attachment );
2402 wp_send_json_success( $html );
2406 * Ajax handler for sending a link to the editor.
2408 * Generates the HTML to send a non-image embed link to the editor.
2410 * Backwards compatible with the following filters:
2411 * - file_send_to_editor_url
2412 * - audio_send_to_editor_url
2413 * - video_send_to_editor_url
2417 function wp_ajax_send_link_to_editor() {
2418 global $post, $wp_embed;
2420 check_ajax_referer( 'media-send-to-editor', 'nonce' );
2422 if ( ! $src = wp_unslash( $_POST['src'] ) )
2423 wp_send_json_error();
2425 if ( ! strpos( $src, '://' ) )
2426 $src = 'http://' . $src;
2428 if ( ! $src = esc_url_raw( $src ) )
2429 wp_send_json_error();
2431 if ( ! $title = trim( wp_unslash( $_POST['title'] ) ) )
2432 $title = wp_basename( $src );
2434 $post = get_post( isset( $_POST['post_id'] ) ? $_POST['post_id'] : 0 );
2436 // Ping WordPress for an embed.
2437 $check_embed = $wp_embed->run_shortcode( '[embed]'. $src .'[/embed]' );
2439 // Fallback that WordPress creates when no oEmbed was found.
2440 $fallback = $wp_embed->maybe_make_link( $src );
2442 if ( $check_embed !== $fallback ) {
2443 // TinyMCE view for [embed] will parse this
2444 $html = '[embed]' . $src . '[/embed]';
2445 } elseif ( $title ) {
2446 $html = '<a href="' . esc_url( $src ) . '">' . $title . '</a>';
2451 // Figure out what filter to run:
2453 if ( ( $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $src ) ) && ( $ext_type = wp_ext2type( $ext ) )
2454 && ( 'audio' == $ext_type || 'video' == $ext_type ) )
2457 /** This filter is documented in wp-admin/includes/media.php */
2458 $html = apply_filters( $type . '_send_to_editor_url', $html, $src, $title );
2460 wp_send_json_success( $html );
2464 * Ajax handler for the Heartbeat API.
2466 * Runs when the user is logged in.
2470 function wp_ajax_heartbeat() {
2471 if ( empty( $_POST['_nonce'] ) )
2472 wp_send_json_error();
2474 $response = array();
2476 if ( false === wp_verify_nonce( $_POST['_nonce'], 'heartbeat-nonce' ) ) {
2477 // User is logged in but nonces have expired.
2478 $response['nonces_expired'] = true;
2479 wp_send_json($response);
2482 // screen_id is the same as $current_screen->id and the JS global 'pagenow'.
2483 if ( ! empty($_POST['screen_id']) )
2484 $screen_id = sanitize_key($_POST['screen_id']);
2486 $screen_id = 'front';
2488 if ( ! empty($_POST['data']) ) {
2489 $data = wp_unslash( (array) $_POST['data'] );
2492 * Filter the Heartbeat response received.
2496 * @param array|object $response The Heartbeat response object or array.
2497 * @param array $data The $_POST data sent.
2498 * @param string $screen_id The screen id.
2500 $response = apply_filters( 'heartbeat_received', $response, $data, $screen_id );
2504 * Filter the Heartbeat response sent.
2508 * @param array|object $response The Heartbeat response object or array.
2509 * @param string $screen_id The screen id.
2511 $response = apply_filters( 'heartbeat_send', $response, $screen_id );
2514 * Fires when Heartbeat ticks in logged-in environments.
2516 * Allows the transport to be easily replaced with long-polling.
2520 * @param array|object $response The Heartbeat response object or array.
2521 * @param string $screen_id The screen id.
2523 do_action( 'heartbeat_tick', $response, $screen_id );
2525 // Send the current time according to the server
2526 $response['server_time'] = time();
2528 wp_send_json($response);
2532 * Ajax handler for getting revision diffs.
2536 function wp_ajax_get_revision_diffs() {
2537 require ABSPATH . 'wp-admin/includes/revision.php';
2539 if ( ! $post = get_post( (int) $_REQUEST['post_id'] ) )
2540 wp_send_json_error();
2542 if ( ! current_user_can( 'read_post', $post->ID ) )
2543 wp_send_json_error();
2545 // Really just pre-loading the cache here.
2546 if ( ! $revisions = wp_get_post_revisions( $post->ID, array( 'check_enabled' => false ) ) )
2547 wp_send_json_error();
2550 @set_time_limit( 0 );
2552 foreach ( $_REQUEST['compare'] as $compare_key ) {
2553 list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to
2556 'id' => $compare_key,
2557 'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ),
2560 wp_send_json_success( $return );
2564 * Ajax handler for auto-saving the selected color scheme for
2565 * a user's own profile.
2569 function wp_ajax_save_user_color_scheme() {
2570 global $_wp_admin_css_colors;
2572 check_ajax_referer( 'save-color-scheme', 'nonce' );
2574 $color_scheme = sanitize_key( $_POST['color_scheme'] );
2576 if ( ! isset( $_wp_admin_css_colors[ $color_scheme ] ) ) {
2577 wp_send_json_error();
2580 update_user_meta( get_current_user_id(), 'admin_color', $color_scheme );
2581 wp_send_json_success();
2585 * Ajax handler for getting themes from themes_api().
2589 function wp_ajax_query_themes() {
2590 global $themes_allowedtags, $theme_field_defaults;
2592 if ( ! current_user_can( 'install_themes' ) ) {
2593 wp_send_json_error();
2596 $args = wp_parse_args( wp_unslash( $_REQUEST['request'] ), array(
2598 'fields' => $theme_field_defaults
2601 $old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search';
2603 /** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */
2604 $args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args );
2606 $api = themes_api( 'query_themes', $args );
2608 if ( is_wp_error( $api ) ) {
2609 wp_send_json_error();
2612 $update_php = network_admin_url( 'update.php?action=install-theme' );
2613 foreach ( $api->themes as &$theme ) {
2614 $theme->install_url = add_query_arg( array(
2615 'theme' => $theme->slug,
2616 '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug )
2619 $theme->name = wp_kses( $theme->name, $themes_allowedtags );
2620 $theme->author = wp_kses( $theme->author, $themes_allowedtags );
2621 $theme->version = wp_kses( $theme->version, $themes_allowedtags );
2622 $theme->description = wp_kses( $theme->description, $themes_allowedtags );
2623 $theme->num_ratings = sprintf( _n( '(based on %s rating)', '(based on %s ratings)', $theme->num_ratings ), number_format_i18n( $theme->num_ratings ) );
2624 $theme->preview_url = set_url_scheme( $theme->preview_url );
2627 wp_send_json_success( $api );
2631 * Apply [embed] AJAX handlers to a string.
2635 * @global WP_Post $post Global $post.
2636 * @global WP_Embed $wp_embed Embed API instance.
2638 function wp_ajax_parse_embed() {
2639 global $post, $wp_embed;
2641 if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) {
2642 wp_send_json_error();
2645 if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) {
2646 wp_send_json_error();
2649 $shortcode = wp_unslash( $_POST['shortcode'] );
2650 $url = str_replace( '[embed]', '', str_replace( '[/embed]', '', $shortcode ) );
2652 setup_postdata( $post );
2654 $wp_embed->return_false_on_fail = true;
2656 if ( is_ssl() && preg_match( '%^\\[embed[^\\]]*\\]http://%i', $shortcode ) ) {
2657 // Admin is ssl and the user pasted non-ssl URL.
2658 // Check if the provider supports ssl embeds and use that for the preview.
2659 $ssl_shortcode = preg_replace( '%^(\\[embed[^\\]]*\\])http://%i', '$1https://', $shortcode );
2660 $parsed = $wp_embed->run_shortcode( $ssl_shortcode );
2663 $no_ssl_support = true;
2668 $parsed = $wp_embed->run_shortcode( $shortcode );
2672 wp_send_json_error( array(
2673 'type' => 'not-embeddable',
2674 'message' => sprintf( __( '%s failed to embed.' ), '<code>' . esc_html( $url ) . '</code>' ),
2678 if ( has_shortcode( $parsed, 'audio' ) || has_shortcode( $parsed, 'video' ) ) {
2680 $mce_styles = wpview_media_sandbox_styles();
2681 foreach ( $mce_styles as $style ) {
2682 $styles .= sprintf( '<link rel="stylesheet" href="%s"/>', $style );
2685 $html = do_shortcode( $parsed );
2688 if ( ! empty( $wp_scripts ) ) {
2689 $wp_scripts->done = array();
2692 wp_print_scripts( 'wp-mediaelement' );
2693 $scripts = ob_get_clean();
2695 $parsed = $styles . $html . $scripts;
2699 if ( ! empty( $no_ssl_support ) || ( is_ssl() && ( preg_match( '%<(iframe|script|embed) [^>]*src="http://%', $parsed ) ||
2700 preg_match( '%<link [^>]*href="http://%', $parsed ) ) ) ) {
2701 // Admin is ssl and the embed is not. Iframes, scripts, and other "active content" will be blocked.
2702 wp_send_json_error( array(
2703 'type' => 'not-ssl',
2704 'message' => sprintf( __( 'Preview not available. %s cannot be embedded securely.' ), '<code>' . esc_html( $url ) . '</code>' ),
2708 wp_send_json_success( array(
2713 function wp_ajax_parse_media_shortcode() {
2714 global $post, $wp_scripts;
2716 if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) {
2717 wp_send_json_error();
2720 if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) {
2721 wp_send_json_error();
2724 setup_postdata( $post );
2725 $shortcode = do_shortcode( wp_unslash( $_POST['shortcode'] ) );
2727 if ( empty( $shortcode ) ) {
2728 wp_send_json_error( array(
2729 'type' => 'no-items',
2730 'message' => __( 'No items found.' ),
2735 $styles = wpview_media_sandbox_styles();
2737 foreach ( $styles as $style ) {
2738 $head .= '<link type="text/css" rel="stylesheet" href="' . $style . '">';
2741 if ( ! empty( $wp_scripts ) ) {
2742 $wp_scripts->done = array();
2749 if ( 'playlist' === $_REQUEST['type'] ) {
2750 wp_underscore_playlist_templates();
2752 wp_print_scripts( 'wp-playlist' );
2754 wp_print_scripts( 'wp-mediaelement' );
2757 wp_send_json_success( array(
2759 'body' => ob_get_clean()