]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-admin/includes/ajax-actions.php
WordPress 4.1
[autoinstalls/wordpress.git] / wp-admin / includes / ajax-actions.php
1 <?php
2 /**
3  * WordPress Core Ajax Handlers.
4  *
5  * @package WordPress
6  * @subpackage Administration
7  */
8
9 //
10 // No-privilege Ajax handlers.
11 //
12
13 /**
14  * Ajax handler for the Heartbeat API in
15  * the no-privilege context.
16  *
17  * Runs when the user is not logged in.
18  *
19  * @since 3.6.0
20  */
21 function wp_ajax_nopriv_heartbeat() {
22         $response = array();
23
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']);
27         else
28                 $screen_id = 'front';
29
30         if ( ! empty($_POST['data']) ) {
31                 $data = wp_unslash( (array) $_POST['data'] );
32
33                 /**
34                  * Filter Heartbeat AJAX response in no-privilege environments.
35                  *
36                  * @since 3.6.0
37                  *
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.
41                  */
42                 $response = apply_filters( 'heartbeat_nopriv_received', $response, $data, $screen_id );
43         }
44
45         /**
46          * Filter Heartbeat AJAX response when no data is passed.
47          *
48          * @since 3.6.0
49          *
50          * @param array|object $response  The Heartbeat response object or array.
51          * @param string       $screen_id The screen id.
52          */
53         $response = apply_filters( 'heartbeat_nopriv_send', $response, $screen_id );
54
55         /**
56          * Fires when Heartbeat ticks in no-privilege environments.
57          *
58          * Allows the transport to be easily replaced with long-polling.
59          *
60          * @since 3.6.0
61          *
62          * @param array|object $response  The no-priv Heartbeat response.
63          * @param string       $screen_id The screen id.
64          */
65         do_action( 'heartbeat_nopriv_tick', $response, $screen_id );
66
67         // Send the current time according to the server.
68         $response['server_time'] = time();
69
70         wp_send_json($response);
71 }
72
73 //
74 // GET-based Ajax handlers.
75 //
76
77 /**
78  * Ajax handler for fetching a list table.
79  *
80  * @since 3.1.0
81  */
82 function wp_ajax_fetch_list() {
83         global $wp_list_table;
84
85         $list_class = $_GET['list_args']['class'];
86         check_ajax_referer( "fetch-list-$list_class", '_ajax_fetch_list_nonce' );
87
88         $wp_list_table = _get_list_table( $list_class, array( 'screen' => $_GET['list_args']['screen']['id'] ) );
89         if ( ! $wp_list_table )
90                 wp_die( 0 );
91
92         if ( ! $wp_list_table->ajax_user_can() )
93                 wp_die( -1 );
94
95         $wp_list_table->ajax_response();
96
97         wp_die( 0 );
98 }
99
100 /**
101  * Ajax handler for tag search.
102  *
103  * @since 3.1.0
104  */
105 function wp_ajax_ajax_tag_search() {
106         if ( ! isset( $_GET['tax'] ) ) {
107                 wp_die( 0 );
108         }
109
110         $taxonomy = sanitize_key( $_GET['tax'] );
111         $tax = get_taxonomy( $taxonomy );
112         if ( ! $tax ) {
113                 wp_die( 0 );
114         }
115
116         if ( ! current_user_can( $tax->cap->assign_terms ) ) {
117                 wp_die( -1 );
118         }
119
120         $s = wp_unslash( $_GET['q'] );
121
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];
128         }
129         $s = trim( $s );
130
131         /**
132          * Filter the minimum number of characters required to fire a tag search via AJAX.
133          *
134          * @since 4.0.0
135          *
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.
139          */
140         $term_search_min_chars = (int) apply_filters( 'term_search_min_chars', 2, $tax, $s );
141
142         /*
143          * Require $term_search_min_chars chars for matching (default: 2)
144          * ensure it's a non-negative, non-zero integer.
145          */
146         if ( ( $term_search_min_chars == 0 ) || ( strlen( $s ) < $term_search_min_chars ) ){
147                 wp_die();
148         }
149
150         $results = get_terms( $taxonomy, array( 'name__like' => $s, 'fields' => 'names', 'hide_empty' => false ) );
151
152         echo join( $results, "\n" );
153         wp_die();
154 }
155
156 /**
157  * Ajax handler for compression testing.
158  *
159  * @since 3.1.0
160  */
161 function wp_ajax_wp_compression_test() {
162         if ( !current_user_can( 'manage_options' ) )
163                 wp_die( -1 );
164
165         if ( ini_get('zlib.output_compression') || 'ob_gzhandler' == ini_get('output_handler') ) {
166                 update_site_option('can_compress_scripts', 0);
167                 wp_die( 0 );
168         }
169
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."';
178
179                  if ( 1 == $_GET['test'] ) {
180                         echo $test_str;
181                         wp_die();
182                  } elseif ( 2 == $_GET['test'] ) {
183                         if ( !isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
184                                 wp_die( -1 );
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 );
191                         } else {
192                                 wp_die( -1 );
193                         }
194                         echo $out;
195                         wp_die();
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);
200                 }
201         }
202
203         wp_die( 0 );
204 }
205
206 /**
207  * Ajax handler for image editor previews.
208  *
209  * @since 3.1.0
210  */
211 function wp_ajax_imgedit_preview() {
212         $post_id = intval($_GET['postid']);
213         if ( empty($post_id) || !current_user_can('edit_post', $post_id) )
214                 wp_die( -1 );
215
216         check_ajax_referer( "image_editor-$post_id" );
217
218         include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
219         if ( ! stream_preview_image($post_id) )
220                 wp_die( -1 );
221
222         wp_die();
223 }
224
225 /**
226  * Ajax handler for oEmbed caching.
227  *
228  * @since 3.1.0
229  */
230 function wp_ajax_oembed_cache() {
231         $GLOBALS['wp_embed']->cache_oembed( $_GET['post'] );
232         wp_die( 0 );
233 }
234
235 /**
236  * Ajax handler for user autocomplete.
237  *
238  * @since 3.4.0
239  */
240 function wp_ajax_autocomplete_user() {
241         if ( ! is_multisite() || ! current_user_can( 'promote_users' ) || wp_is_large_network( 'users' ) )
242                 wp_die( -1 );
243
244         /** This filter is documented in wp-admin/user-new.php */
245         if ( ! is_super_admin() && ! apply_filters( 'autocomplete_users_for_site_admins', false ) )
246                 wp_die( -1 );
247
248         $return = array();
249
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'];
254         } else {
255                 $type = 'add';
256         }
257
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'];
262         } else {
263                 $field = 'user_login';
264         }
265
266         // Exclude current users of this blog
267         if ( isset( $_REQUEST['site_id'] ) ) {
268                 $id = absint( $_REQUEST['site_id'] );
269         } else {
270                 $id = get_current_blog_id();
271         }
272
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() );
275
276         $users = get_users( array(
277                 'blog_id' => false,
278                 'search'  => '*' . $_REQUEST['term'] . '*',
279                 'include' => $include_blog_users,
280                 'exclude' => $exclude_blog_users,
281                 'search_columns' => array( 'user_login', 'user_nicename', 'user_email' ),
282         ) );
283
284         foreach ( $users as $user ) {
285                 $return[] = array(
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,
289                 );
290         }
291
292         wp_die( wp_json_encode( $return ) );
293 }
294
295 /**
296  * Ajax handler for dashboard widgets.
297  *
298  * @since 3.4.0
299  */
300 function wp_ajax_dashboard_widgets() {
301         require_once ABSPATH . 'wp-admin/includes/dashboard.php';
302
303         $pagenow = $_GET['pagenow'];
304         if ( $pagenow === 'dashboard-user' || $pagenow === 'dashboard-network' || $pagenow === 'dashboard' ) {
305                 set_current_screen( $pagenow );
306         }
307
308         switch ( $_GET['widget'] ) {
309                 case 'dashboard_primary' :
310                         wp_dashboard_primary();
311                         break;
312         }
313         wp_die();
314 }
315
316 /**
317  * Ajax handler for Customizer preview logged-in status.
318  *
319  * @since 3.4.0
320  */
321 function wp_ajax_logged_in() {
322         wp_die( 1 );
323 }
324
325 //
326 // Ajax helpers.
327 //
328
329 /**
330  * Sends back current comment total and new page links if they need to be updated.
331  *
332  * Contrary to normal success AJAX response ("1"), die with time() on success.
333  *
334  * @since 2.7.0
335  *
336  * @param int $comment_id
337  * @return die
338  */
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'] ) : '';
344
345         // JS didn't send us everything we need to know. Just die with success message
346         if ( !$total || !$per_page || !$page || !$url )
347                 wp_die( time() );
348
349         $total += $delta;
350         if ( $total < 0 )
351                 $total = 0;
352
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 ) ) {
355                 $post_id = 0;
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'];
364                 }
365
366                 $comment_count = wp_count_comments($post_id);
367
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.
372         }
373
374         // The time since the last comment count.
375         $time = time();
376
377         $x = new WP_Ajax_Response( array(
378                 'what' => 'comment',
379                 // Here for completeness - not used.
380                 'id' => $comment_id,
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 ) ),
385                         'total' => $total,
386                         'time' => $time
387                 )
388         ) );
389         $x->send();
390 }
391
392 //
393 // POST-based Ajax handlers.
394 //
395
396 /**
397  * Ajax handler for adding a hierarchical term.
398  *
399  * @since 3.1.0
400  */
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 ) )
406                 wp_die( -1 );
407         $names = explode(',', $_POST['new'.$taxonomy->name]);
408         $parent = isset($_POST['new'.$taxonomy->name.'_parent']) ? (int) $_POST['new'.$taxonomy->name.'_parent'] : 0;
409         if ( 0 > $parent )
410                 $parent = 0;
411         if ( $taxonomy->name == 'category' )
412                 $post_category = isset($_POST['post_category']) ? (array) $_POST['post_category'] : array();
413         else
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);
417
418         foreach ( $names as $cat_name ) {
419                 $cat_name = trim($cat_name);
420                 $category_nicename = sanitize_title($cat_name);
421                 if ( '' === $category_nicename )
422                         continue;
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 ) )
426                         continue;
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
431                         continue;
432                 ob_start();
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();
435                 ob_end_clean();
436                 $add = array(
437                         'what' => $taxonomy->name,
438                         'id' => $cat_id,
439                         'data' => str_replace( array("\n", "\t"), '', $data),
440                         'position' => -1
441                 );
442         }
443
444         if ( $parent ) { // Foncy - replace the parent and all its children
445                 $parent = get_term( $parent, $taxonomy->name );
446                 $term_id = $parent->term_id;
447
448                 while ( $parent->parent ) { // get the top parent
449                         $parent = get_term( $parent->parent, $taxonomy->name );
450                         if ( is_wp_error( $parent ) )
451                                 break;
452                         $term_id = $parent->term_id;
453                 }
454
455                 ob_start();
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();
458                 ob_end_clean();
459                 $add = array(
460                         'what' => $taxonomy->name,
461                         'id' => $term_id,
462                         'data' => str_replace( array("\n", "\t"), '', $data),
463                         'position' => -1
464                 );
465         }
466
467         ob_start();
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' => '&mdash; '.$taxonomy->labels->parent_item.' &mdash;'
471                 ) );
472         $sup = ob_get_contents();
473         ob_end_clean();
474         $add['supplemental'] = array( 'newcat_parent' => $sup );
475
476         $x = new WP_Ajax_Response( $add );
477         $x->send();
478 }
479
480 /**
481  * Ajax handler for deleting a comment.
482  *
483  * @since 3.1.0
484  */
485 function wp_ajax_delete_comment() {
486         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
487
488         if ( !$comment = get_comment( $id ) )
489                 wp_die( time() );
490         if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
491                 wp_die( -1 );
492
493         check_ajax_referer( "delete-comment_$id" );
494         $status = wp_get_comment_status( $comment->comment_ID );
495
496         $delta = -1;
497         if ( isset($_POST['trash']) && 1 == $_POST['trash'] ) {
498                 if ( 'trash' == $status )
499                         wp_die( time() );
500                 $r = wp_trash_comment( $comment->comment_ID );
501         } elseif ( isset($_POST['untrash']) && 1 == $_POST['untrash'] ) {
502                 if ( 'trash' != $status )
503                         wp_die( time() );
504                 $r = wp_untrash_comment( $comment->comment_ID );
505                 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'trash' ) // undo trash, not in trash
506                         $delta = 1;
507         } elseif ( isset($_POST['spam']) && 1 == $_POST['spam'] ) {
508                 if ( 'spam' == $status )
509                         wp_die( time() );
510                 $r = wp_spam_comment( $comment->comment_ID );
511         } elseif ( isset($_POST['unspam']) && 1 == $_POST['unspam'] ) {
512                 if ( 'spam' != $status )
513                         wp_die( time() );
514                 $r = wp_unspam_comment( $comment->comment_ID );
515                 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'spam' ) // undo spam, not in spam
516                         $delta = 1;
517         } elseif ( isset($_POST['delete']) && 1 == $_POST['delete'] ) {
518                 $r = wp_delete_comment( $comment->comment_ID );
519         } else {
520                 wp_die( -1 );
521         }
522
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 );
525         wp_die( 0 );
526 }
527
528 /**
529  * Ajax handler for deleting a tag.
530  *
531  * @since 3.1.0
532  */
533 function wp_ajax_delete_tag() {
534         $tag_id = (int) $_POST['tag_ID'];
535         check_ajax_referer( "delete-tag_$tag_id" );
536
537         $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
538         $tax = get_taxonomy($taxonomy);
539
540         if ( !current_user_can( $tax->cap->delete_terms ) )
541                 wp_die( -1 );
542
543         $tag = get_term( $tag_id, $taxonomy );
544         if ( !$tag || is_wp_error( $tag ) )
545                 wp_die( 1 );
546
547         if ( wp_delete_term($tag_id, $taxonomy))
548                 wp_die( 1 );
549         else
550                 wp_die( 0 );
551 }
552
553 /**
554  * Ajax handler for deleting a link.
555  *
556  * @since 3.1.0
557  */
558 function wp_ajax_delete_link() {
559         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
560
561         check_ajax_referer( "delete-bookmark_$id" );
562         if ( !current_user_can( 'manage_links' ) )
563                 wp_die( -1 );
564
565         $link = get_bookmark( $id );
566         if ( !$link || is_wp_error( $link ) )
567                 wp_die( 1 );
568
569         if ( wp_delete_link( $id ) )
570                 wp_die( 1 );
571         else
572                 wp_die( 0 );
573 }
574
575 /**
576  * Ajax handler for deleting meta.
577  *
578  * @since 3.1.0
579  */
580 function wp_ajax_delete_meta() {
581         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
582
583         check_ajax_referer( "delete-meta_$id" );
584         if ( !$meta = get_metadata_by_mid( 'post', $id ) )
585                 wp_die( 1 );
586
587         if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta',  $meta->post_id, $meta->meta_key ) )
588                 wp_die( -1 );
589         if ( delete_meta( $meta->meta_id ) )
590                 wp_die( 1 );
591         wp_die( 0 );
592 }
593
594 /**
595  * Ajax handler for deleting a post.
596  *
597  * @since 3.1.0
598  *
599  * @param string $action Action to perform.
600  */
601 function wp_ajax_delete_post( $action ) {
602         if ( empty( $action ) )
603                 $action = 'delete-post';
604         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
605
606         check_ajax_referer( "{$action}_$id" );
607         if ( !current_user_can( 'delete_post', $id ) )
608                 wp_die( -1 );
609
610         if ( !get_post( $id ) )
611                 wp_die( 1 );
612
613         if ( wp_delete_post( $id ) )
614                 wp_die( 1 );
615         else
616                 wp_die( 0 );
617 }
618
619 /**
620  * Ajax handler for sending a post to the trash.
621  *
622  * @since 3.1.0
623  *
624  * @param string $action Action to perform.
625  */
626 function wp_ajax_trash_post( $action ) {
627         if ( empty( $action ) )
628                 $action = 'trash-post';
629         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
630
631         check_ajax_referer( "{$action}_$id" );
632         if ( !current_user_can( 'delete_post', $id ) )
633                 wp_die( -1 );
634
635         if ( !get_post( $id ) )
636                 wp_die( 1 );
637
638         if ( 'trash-post' == $action )
639                 $done = wp_trash_post( $id );
640         else
641                 $done = wp_untrash_post( $id );
642
643         if ( $done )
644                 wp_die( 1 );
645
646         wp_die( 0 );
647 }
648
649 /**
650  * Ajax handler to restore a post from the trash.
651  *
652  * @since 3.1.0
653  *
654  * @param string $action Action to perform.
655  */
656 function wp_ajax_untrash_post( $action ) {
657         if ( empty( $action ) )
658                 $action = 'untrash-post';
659         wp_ajax_trash_post( $action );
660 }
661
662 function wp_ajax_delete_page( $action ) {
663         if ( empty( $action ) )
664                 $action = 'delete-page';
665         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
666
667         check_ajax_referer( "{$action}_$id" );
668         if ( !current_user_can( 'delete_page', $id ) )
669                 wp_die( -1 );
670
671         if ( ! get_post( $id ) )
672                 wp_die( 1 );
673
674         if ( wp_delete_post( $id ) )
675                 wp_die( 1 );
676         else
677                 wp_die( 0 );
678 }
679
680 /**
681  * Ajax handler to dim a comment.
682  *
683  * @since 3.1.0
684  */
685 function wp_ajax_dim_comment() {
686         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
687
688         if ( !$comment = get_comment( $id ) ) {
689                 $x = new WP_Ajax_Response( array(
690                         'what' => 'comment',
691                         'id' => new WP_Error('invalid_comment', sprintf(__('Comment %d does not exist'), $id))
692                 ) );
693                 $x->send();
694         }
695
696         if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) && ! current_user_can( 'moderate_comments' ) )
697                 wp_die( -1 );
698
699         $current = wp_get_comment_status( $comment->comment_ID );
700         if ( isset( $_POST['new'] ) && $_POST['new'] == $current )
701                 wp_die( time() );
702
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 );
706         else
707                 $result = wp_set_comment_status( $comment->comment_ID, 'hold', true );
708
709         if ( is_wp_error($result) ) {
710                 $x = new WP_Ajax_Response( array(
711                         'what' => 'comment',
712                         'id' => $result
713                 ) );
714                 $x->send();
715         }
716
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 );
719         wp_die( 0 );
720 }
721
722 /**
723  * Ajax handler for deleting a link category.
724  *
725  * @since 3.1.0
726  *
727  * @param string $action Action to perform.
728  */
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' ) )
734                 wp_die( -1 );
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);
740                 if ( '' === $slug )
741                         continue;
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 ) )
745                         continue;
746                 else if ( is_array( $cat_id ) )
747                         $cat_id = $cat_id['term_id'];
748                 $cat_name = esc_html( $cat_name );
749                 $x->add( array(
750                         'what' => 'link-category',
751                         'id' => $cat_id,
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>",
753                         'position' => -1
754                 ) );
755         }
756         $x->send();
757 }
758
759 /**
760  * Ajax handler to add a tag.
761  *
762  * @since 3.1.0
763  */
764 function wp_ajax_add_tag() {
765         global $wp_list_table;
766
767         check_ajax_referer( 'add-tag', '_wpnonce_add-tag' );
768         $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
769         $tax = get_taxonomy($taxonomy);
770
771         if ( !current_user_can( $tax->cap->edit_terms ) )
772                 wp_die( -1 );
773
774         $x = new WP_Ajax_Response();
775
776         $tag = wp_insert_term($_POST['tag-name'], $taxonomy, $_POST );
777
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();
782
783                 $x->add( array(
784                         'what' => 'taxonomy',
785                         'data' => new WP_Error('error', $message )
786                 ) );
787                 $x->send();
788         }
789
790         $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => $_POST['screen'] ) );
791
792         $level = 0;
793         if ( is_taxonomy_hierarchical($taxonomy) ) {
794                 $level = count( get_ancestors( $tag->term_id, $taxonomy, 'taxonomy' ) );
795                 ob_start();
796                 $wp_list_table->single_row( $tag, $level );
797                 $noparents = ob_get_clean();
798         }
799
800         ob_start();
801         $wp_list_table->single_row( $tag );
802         $parents = ob_get_clean();
803
804         $x->add( array(
805                 'what' => 'taxonomy',
806                 'supplemental' => compact('parents', 'noparents')
807                 ) );
808         $x->add( array(
809                 'what' => 'term',
810                 'position' => $level,
811                 'supplemental' => (array) $tag
812                 ) );
813         $x->send();
814 }
815
816 /**
817  * Ajax handler for getting a tagcloud.
818  *
819  * @since 3.1.0
820  */
821 function wp_ajax_get_tagcloud() {
822         if ( ! isset( $_POST['tax'] ) ) {
823                 wp_die( 0 );
824         }
825
826         $taxonomy = sanitize_key( $_POST['tax'] );
827         $tax = get_taxonomy( $taxonomy );
828         if ( ! $tax ) {
829                 wp_die( 0 );
830         }
831         
832         if ( ! current_user_can( $tax->cap->assign_terms ) ) {
833                 wp_die( -1 );
834         }
835
836         $tags = get_terms( $taxonomy, array( 'number' => 45, 'orderby' => 'count', 'order' => 'DESC' ) );
837
838         if ( empty( $tags ) )
839                 wp_die( $tax->labels->not_found );
840
841         if ( is_wp_error( $tags ) )
842                 wp_die( $tags->get_error_message() );
843
844         foreach ( $tags as $key => $tag ) {
845                 $tags[ $key ]->link = '#';
846                 $tags[ $key ]->id = $tag->term_id;
847         }
848
849         // We need raw tag names here, so don't filter the output
850         $return = wp_generate_tag_cloud( $tags, array('filter' => 0) );
851
852         if ( empty($return) )
853                 wp_die( 0 );
854
855         echo $return;
856
857         wp_die();
858 }
859
860 /**
861  * Ajax handler for getting comments.
862  *
863  * @since 3.1.0
864  *
865  * @param string $action Action to perform.
866  */
867 function wp_ajax_get_comments( $action ) {
868         global $wp_list_table, $post_id;
869         if ( empty( $action ) )
870                 $action = 'get-comments';
871
872         check_ajax_referer( $action );
873
874         if ( empty( $post_id ) && ! empty( $_REQUEST['p'] ) ) {
875                 $id = absint( $_REQUEST['p'] );
876                 if ( ! empty( $id ) )
877                         $post_id = $id;
878         }
879
880         if ( empty( $post_id ) )
881                 wp_die( -1 );
882
883         $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
884
885         if ( ! current_user_can( 'edit_post', $post_id ) )
886                 wp_die( -1 );
887
888         $wp_list_table->prepare_items();
889
890         if ( !$wp_list_table->has_items() )
891                 wp_die( 1 );
892
893         $x = new WP_Ajax_Response();
894         ob_start();
895         foreach ( $wp_list_table->items as $comment ) {
896                 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
897                         continue;
898                 get_comment( $comment );
899                 $wp_list_table->single_row( $comment );
900         }
901         $comment_list_item = ob_get_contents();
902         ob_end_clean();
903
904         $x->add( array(
905                 'what' => 'comments',
906                 'data' => $comment_list_item
907         ) );
908         $x->send();
909 }
910
911 /**
912  * Ajax handler for replying to a comment.
913  *
914  * @since 3.1.0
915  *
916  * @param string $action Action to perform.
917  */
918 function wp_ajax_replyto_comment( $action ) {
919         global $wp_list_table;
920         if ( empty( $action ) )
921                 $action = 'replyto-comment';
922
923         check_ajax_referer( $action, '_ajax_nonce-replyto-comment' );
924
925         $comment_post_ID = (int) $_POST['comment_post_ID'];
926         $post = get_post( $comment_post_ID );
927         if ( ! $post )
928                 wp_die( -1 );
929
930         if ( !current_user_can( 'edit_post', $comment_post_ID ) )
931                 wp_die( -1 );
932
933         if ( empty( $post->post_status ) )
934                 wp_die( 1 );
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.') );
937
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'] = '';
949
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
953                         }
954                 }
955         } else {
956                 wp_die( __( 'Sorry, you must be logged in to reply to a comment.' ) );
957         }
958
959         if ( '' == $comment_content )
960                 wp_die( __( 'ERROR: please type a comment.' ) );
961
962         $comment_parent = 0;
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');
967
968         // Automatically approve parent comment.
969         if ( !empty($_POST['approve_parent']) ) {
970                 $parent = get_comment( $comment_parent );
971
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;
975                 }
976         }
977
978         $comment_id = wp_new_comment( $commentdata );
979         $comment = get_comment($comment_id);
980         if ( ! $comment ) wp_die( 1 );
981
982         $position = ( isset($_POST['position']) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1';
983
984         ob_start();
985         if ( isset( $_REQUEST['mode'] ) && 'dashboard' == $_REQUEST['mode'] ) {
986                 require_once( ABSPATH . 'wp-admin/includes/dashboard.php' );
987                 _wp_dashboard_recent_comments_row( $comment );
988         } else {
989                 if ( isset( $_REQUEST['mode'] ) && 'single' == $_REQUEST['mode'] ) {
990                         $wp_list_table = _get_list_table('WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
991                 } else {
992                         $wp_list_table = _get_list_table('WP_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
993                 }
994                 $wp_list_table->single_row( $comment );
995         }
996         $comment_list_item = ob_get_clean();
997
998         $response =  array(
999                 'what' => 'comment',
1000                 'id' => $comment->comment_ID,
1001                 'data' => $comment_list_item,
1002                 'position' => $position
1003         );
1004
1005         if ( $comment_auto_approved )
1006                 $response['supplemental'] = array( 'parent_approved' => $parent->comment_ID );
1007
1008         $x = new WP_Ajax_Response();
1009         $x->add( $response );
1010         $x->send();
1011 }
1012
1013 /**
1014  * Ajax handler for editing a comment.
1015  *
1016  * @since 3.1.0
1017  */
1018 function wp_ajax_edit_comment() {
1019         global $wp_list_table;
1020
1021         check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' );
1022
1023         $comment_id = (int) $_POST['comment_ID'];
1024         if ( ! current_user_can( 'edit_comment', $comment_id ) )
1025                 wp_die( -1 );
1026
1027         if ( '' == $_POST['content'] )
1028                 wp_die( __( 'ERROR: please type a comment.' ) );
1029
1030         if ( isset( $_POST['status'] ) )
1031                 $_POST['comment_status'] = $_POST['status'];
1032         edit_comment();
1033
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' ) );
1037
1038         $comment = get_comment( $comment_id );
1039         if ( empty( $comment->comment_ID ) )
1040                 wp_die( -1 );
1041
1042         ob_start();
1043         $wp_list_table->single_row( $comment );
1044         $comment_list_item = ob_get_clean();
1045
1046         $x = new WP_Ajax_Response();
1047
1048         $x->add( array(
1049                 'what' => 'edit_comment',
1050                 'id' => $comment->comment_ID,
1051                 'data' => $comment_list_item,
1052                 'position' => $position
1053         ));
1054
1055         $x->send();
1056 }
1057
1058 /**
1059  * Ajax handler for adding a menu item.
1060  *
1061  * @since 3.1.0
1062  */
1063 function wp_ajax_add_menu_item() {
1064         check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1065
1066         if ( ! current_user_can( 'edit_theme_options' ) )
1067                 wp_die( -1 );
1068
1069         require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1070
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.
1073
1074         $menu_items_data = array();
1075         foreach ( (array) $_POST['menu-item'] as $menu_item_data ) {
1076                 if (
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'] )
1080                 ) {
1081                         switch( $menu_item_data['menu-item-type'] ) {
1082                                 case 'post_type' :
1083                                         $_object = get_post( $menu_item_data['menu-item-object-id'] );
1084                                 break;
1085
1086                                 case 'taxonomy' :
1087                                         $_object = get_term( $menu_item_data['menu-item-object-id'], $menu_item_data['menu-item-object'] );
1088                                 break;
1089                         }
1090
1091                         $_menu_items = array_map( 'wp_setup_nav_menu_item', array( $_object ) );
1092                         $_menu_item = array_shift( $_menu_items );
1093
1094                         // Restore the missing menu item properties
1095                         $menu_item_data['menu-item-description'] = $_menu_item->description;
1096                 }
1097
1098                 $menu_items_data[] = $menu_item_data;
1099         }
1100
1101         $item_ids = wp_save_nav_menu_items( 0, $menu_items_data );
1102         if ( is_wp_error( $item_ids ) )
1103                 wp_die( 0 );
1104
1105         $menu_items = array();
1106
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;
1113                 }
1114         }
1115
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'] );
1118
1119         if ( ! class_exists( $walker_class_name ) )
1120                 wp_die( 0 );
1121
1122         if ( ! empty( $menu_items ) ) {
1123                 $args = array(
1124                         'after' => '',
1125                         'before' => '',
1126                         'link_after' => '',
1127                         'link_before' => '',
1128                         'walker' => new $walker_class_name,
1129                 );
1130                 echo walk_nav_menu_tree( $menu_items, 0, (object) $args );
1131         }
1132         wp_die();
1133 }
1134
1135 /**
1136  * Ajax handler for adding meta.
1137  *
1138  * @since 3.1.0
1139  */
1140 function wp_ajax_add_meta() {
1141         check_ajax_referer( 'add-meta', '_ajax_nonce-add-meta' );
1142         $c = 0;
1143         $pid = (int) $_POST['post_id'];
1144         $post = get_post( $pid );
1145
1146         if ( isset($_POST['metakeyselect']) || isset($_POST['metakeyinput']) ) {
1147                 if ( !current_user_can( 'edit_post', $pid ) )
1148                         wp_die( -1 );
1149                 if ( isset($_POST['metakeyselect']) && '#NONE#' == $_POST['metakeyselect'] && empty($_POST['metakeyinput']) )
1150                         wp_die( 1 );
1151
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 ) );
1162
1163                         if ( $pid = edit_post() ) {
1164                                 if ( is_wp_error( $pid ) ) {
1165                                         $x = new WP_Ajax_Response( array(
1166                                                 'what' => 'meta',
1167                                                 'data' => $pid
1168                                         ) );
1169                                         $x->send();
1170                                 }
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.' ) );
1174                         } else {
1175                                 wp_die( 0 );
1176                         }
1177                 } else if ( !$mid = add_meta( $pid ) ) {
1178                         wp_die( __( 'Please provide a custom field value.' ) );
1179                 }
1180
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(
1185                         'what' => 'meta',
1186                         'id' => $mid,
1187                         'data' => _list_meta_row( $meta, $c ),
1188                         'position' => 1,
1189                         'supplemental' => array('postid' => $pid)
1190                 ) );
1191         } else { // Update?
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 ) )
1204                         wp_die( -1 );
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).
1208                 }
1209
1210                 $x = new WP_Ajax_Response( array(
1211                         'what' => 'meta',
1212                         'id' => $mid, 'old_id' => $mid,
1213                         'data' => _list_meta_row( array(
1214                                 'meta_key' => $key,
1215                                 'meta_value' => $value,
1216                                 'meta_id' => $mid
1217                         ), $c ),
1218                         'position' => 0,
1219                         'supplemental' => array('postid' => $meta->post_id)
1220                 ) );
1221         }
1222         $x->send();
1223 }
1224
1225 /**
1226  * Ajax handler for adding a user.
1227  *
1228  * @since 3.1.0
1229  *
1230  * @param string $action Action to perform.
1231  */
1232 function wp_ajax_add_user( $action ) {
1233         global $wp_list_table;
1234         if ( empty( $action ) )
1235                 $action = 'add-user';
1236
1237         check_ajax_referer( $action );
1238         if ( ! current_user_can('create_users') )
1239                 wp_die( -1 );
1240         if ( ! $user_id = edit_user() ) {
1241                 wp_die( 0 );
1242         } elseif ( is_wp_error( $user_id ) ) {
1243                 $x = new WP_Ajax_Response( array(
1244                         'what' => 'user',
1245                         'id' => $user_id
1246                 ) );
1247                 $x->send();
1248         }
1249         $user_object = get_userdata( $user_id );
1250
1251         $wp_list_table = _get_list_table('WP_Users_List_Table');
1252
1253         $role = current( $user_object->roles );
1254
1255         $x = new WP_Ajax_Response( array(
1256                 'what' => 'user',
1257                 'id' => $user_id,
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),
1261                         'role' => $role,
1262                 )
1263         ) );
1264         $x->send();
1265 }
1266
1267 /**
1268  * Ajax handler for closed post boxes.
1269  *
1270  * @since 3.1.0
1271  */
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);
1276
1277         $hidden = isset( $_POST['hidden'] ) ? explode( ',', $_POST['hidden']) : array();
1278         $hidden = array_filter($hidden);
1279
1280         $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1281
1282         if ( $page != sanitize_key( $page ) )
1283                 wp_die( 0 );
1284
1285         if ( ! $user = wp_get_current_user() )
1286                 wp_die( -1 );
1287
1288         if ( is_array($closed) )
1289                 update_user_option($user->ID, "closedpostboxes_$page", $closed, true);
1290
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);
1294         }
1295
1296         wp_die( 1 );
1297 }
1298
1299 /**
1300  * Ajax handler for hidden columns.
1301  *
1302  * @since 3.1.0
1303  */
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'] : '';
1308
1309         if ( $page != sanitize_key( $page ) )
1310                 wp_die( 0 );
1311
1312         if ( ! $user = wp_get_current_user() )
1313                 wp_die( -1 );
1314
1315         if ( is_array($hidden) )
1316                 update_user_option($user->ID, "manage{$page}columnshidden", $hidden, true);
1317
1318         wp_die( 1 );
1319 }
1320
1321 /**
1322  * Ajax handler for updating whether to display the welcome panel.
1323  *
1324  * @since 3.1.0
1325  */
1326 function wp_ajax_update_welcome_panel() {
1327         check_ajax_referer( 'welcome-panel-nonce', 'welcomepanelnonce' );
1328
1329         if ( ! current_user_can( 'edit_theme_options' ) )
1330                 wp_die( -1 );
1331
1332         update_user_meta( get_current_user_id(), 'show_welcome_panel', empty( $_POST['visible'] ) ? 0 : 1 );
1333
1334         wp_die( 1 );
1335 }
1336
1337 /**
1338  * Ajax handler for retrieving menu meta boxes.
1339  *
1340  * @since 3.1.0
1341  */
1342 function wp_ajax_menu_get_metabox() {
1343         if ( ! current_user_can( 'edit_theme_options' ) )
1344                 wp_die( -1 );
1345
1346         require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1347
1348         if ( isset( $_POST['item-type'] ) && 'post_type' == $_POST['item-type'] ) {
1349                 $type = 'posttype';
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'] ) {
1353                 $type = 'taxonomy';
1354                 $callback = 'wp_nav_menu_item_taxonomy_meta_box';
1355                 $items = (array) get_taxonomies( array( 'show_ui' => true ), 'object' );
1356         }
1357
1358         if ( ! empty( $_POST['item-object'] ) && isset( $items[$_POST['item-object']] ) ) {
1359                 $menus_meta_box_object = $items[ $_POST['item-object'] ];
1360
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 );
1363                 ob_start();
1364                 call_user_func_array($callback, array(
1365                         null,
1366                         array(
1367                                 'id' => 'add-' . $item->name,
1368                                 'title' => $item->labels->name,
1369                                 'callback' => $callback,
1370                                 'args' => $item,
1371                         )
1372                 ));
1373
1374                 $markup = ob_get_clean();
1375
1376                 echo wp_json_encode(array(
1377                         'replace-id' => $type . '-' . $item->name,
1378                         'markup' => $markup,
1379                 ));
1380         }
1381
1382         wp_die();
1383 }
1384
1385 /**
1386  * Ajax handler for internal linking.
1387  *
1388  * @since 3.1.0
1389  */
1390 function wp_ajax_wp_link_ajax() {
1391         check_ajax_referer( 'internal-linking', '_ajax_linking_nonce' );
1392
1393         $args = array();
1394
1395         if ( isset( $_POST['search'] ) )
1396                 $args['s'] = wp_unslash( $_POST['search'] );
1397         $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
1398
1399         require(ABSPATH . WPINC . '/class-wp-editor.php');
1400         $results = _WP_Editors::wp_link_query( $args );
1401
1402         if ( ! isset( $results ) )
1403                 wp_die( 0 );
1404
1405         echo wp_json_encode( $results );
1406         echo "\n";
1407
1408         wp_die();
1409 }
1410
1411 /**
1412  * Ajax handler for menu locations save.
1413  *
1414  * @since 3.1.0
1415  */
1416 function wp_ajax_menu_locations_save() {
1417         if ( ! current_user_can( 'edit_theme_options' ) )
1418                 wp_die( -1 );
1419         check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1420         if ( ! isset( $_POST['menu-locations'] ) )
1421                 wp_die( 0 );
1422         set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_POST['menu-locations'] ) );
1423         wp_die( 1 );
1424 }
1425
1426 /**
1427  * Ajax handler for saving the meta box order.
1428  *
1429  * @since 3.1.0
1430  */
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';
1435
1436         if ( $page_columns != 'auto' )
1437                 $page_columns = (int) $page_columns;
1438
1439         $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1440
1441         if ( $page != sanitize_key( $page ) )
1442                 wp_die( 0 );
1443
1444         if ( ! $user = wp_get_current_user() )
1445                 wp_die( -1 );
1446
1447         if ( $order )
1448                 update_user_option($user->ID, "meta-box-order_$page", $order, true);
1449
1450         if ( $page_columns )
1451                 update_user_option($user->ID, "screen_layout_$page", $page_columns, true);
1452
1453         wp_die( 1 );
1454 }
1455
1456 /**
1457  * Ajax handler for menu quick searching.
1458  *
1459  * @since 3.1.0
1460  */
1461 function wp_ajax_menu_quick_search() {
1462         if ( ! current_user_can( 'edit_theme_options' ) )
1463                 wp_die( -1 );
1464
1465         require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1466
1467         _wp_ajax_menu_quick_search( $_POST );
1468
1469         wp_die();
1470 }
1471
1472 /**
1473  * Ajax handler to retrieve a permalink.
1474  *
1475  * @since 3.1.0
1476  */
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 ) ) );
1481 }
1482
1483 /**
1484  * Ajax handler to retrieve a sample permalink.
1485  *
1486  * @since 3.1.0
1487  */
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 ) );
1494 }
1495
1496 /**
1497  * Ajax handler for Quick Edit saving a post from a list table.
1498  *
1499  * @since 3.1.0
1500  */
1501 function wp_ajax_inline_save() {
1502         global $wp_list_table;
1503
1504         check_ajax_referer( 'inlineeditnonce', '_inline_edit' );
1505
1506         if ( ! isset($_POST['post_ID']) || ! ( $post_ID = (int) $_POST['post_ID'] ) )
1507                 wp_die();
1508
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.' ) );
1512         } else {
1513                 if ( ! current_user_can( 'edit_post', $post_ID ) )
1514                         wp_die( __( 'You are not allowed to edit this post.' ) );
1515         }
1516
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 ) );
1521                 wp_die();
1522         }
1523
1524         $data = &$_POST;
1525
1526         $post = get_post( $post_ID, ARRAY_A );
1527
1528         // Since it's coming from the database.
1529         $post = wp_slash($post);
1530
1531         $data['content'] = $post['post_content'];
1532         $data['excerpt'] = $post['post_excerpt'];
1533
1534         // Rename.
1535         $data['user_ID'] = get_current_user_id();
1536
1537         if ( isset($data['post_parent']) )
1538                 $data['parent_id'] = $data['post_parent'];
1539
1540         // Status.
1541         if ( isset($data['keep_private']) && 'private' == $data['keep_private'] )
1542                 $data['post_status'] = 'private';
1543         else
1544                 $data['post_status'] = $data['_status'];
1545
1546         if ( empty($data['comment_status']) )
1547                 $data['comment_status'] = 'closed';
1548         if ( empty($data['ping_status']) )
1549                 $data['ping_status'] = 'closed';
1550
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'] );
1555         }
1556
1557         // Update the post.
1558         edit_post();
1559
1560         $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => $_POST['screen'] ) );
1561
1562         $level = 0;
1563         $request_post = array( get_post( $_POST['post_ID'] ) );
1564         $parent = $request_post[0]->post_parent;
1565
1566         while ( $parent > 0 ) {
1567                 $parent_post = get_post( $parent );
1568                 $parent = $parent_post->post_parent;
1569                 $level++;
1570         }
1571
1572         $wp_list_table->display_rows( array( get_post( $_POST['post_ID'] ) ), $level );
1573
1574         wp_die();
1575 }
1576
1577 /**
1578  * Ajax handler for quick edit saving for a term.
1579  *
1580  * @since 3.1.0
1581  */
1582 function wp_ajax_inline_save_tax() {
1583         global $wp_list_table;
1584
1585         check_ajax_referer( 'taxinlineeditnonce', '_inline_edit' );
1586
1587         $taxonomy = sanitize_key( $_POST['taxonomy'] );
1588         $tax = get_taxonomy( $taxonomy );
1589         if ( ! $tax )
1590                 wp_die( 0 );
1591
1592         if ( ! current_user_can( $tax->cap->edit_terms ) )
1593                 wp_die( -1 );
1594
1595         $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => 'edit-' . $taxonomy ) );
1596
1597         if ( ! isset($_POST['tax_ID']) || ! ( $id = (int) $_POST['tax_ID'] ) )
1598                 wp_die( -1 );
1599
1600         $tag = get_term( $id, $taxonomy );
1601         $_POST['description'] = $tag->description;
1602
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.' ) );
1610                 }
1611         } else {
1612                 if ( is_wp_error($updated) && $updated->get_error_message() )
1613                         wp_die( $updated->get_error_message() );
1614                 wp_die( __( 'Item not updated.' ) );
1615         }
1616         $level = 0;
1617         $parent = $tag->parent;
1618         while ( $parent > 0 ) {
1619                 $parent_tag = get_term( $parent, $taxonomy );
1620                 $parent = $parent_tag->parent;
1621                 $level++;
1622         }
1623         $wp_list_table->single_row( $tag, $level );
1624         wp_die();
1625 }
1626
1627 /**
1628  * Ajax handler for querying posts for the Find Posts modal.
1629  *
1630  * @see window.findPosts
1631  *
1632  * @since 3.1.0
1633  */
1634 function wp_ajax_find_posts() {
1635         check_ajax_referer( 'find-posts' );
1636
1637         $post_types = get_post_types( array( 'public' => true ), 'objects' );
1638         unset( $post_types['attachment'] );
1639
1640         $s = wp_unslash( $_POST['ps'] );
1641         $args = array(
1642                 'post_type' => array_keys( $post_types ),
1643                 'post_status' => 'any',
1644                 'posts_per_page' => 50,
1645         );
1646         if ( '' !== $s )
1647                 $args['s'] = $s;
1648
1649         $posts = get_posts( $args );
1650
1651         if ( ! $posts ) {
1652                 wp_send_json_error( __( 'No items found.' ) );
1653         }
1654
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>';
1656         $alt = '';
1657         foreach ( $posts as $post ) {
1658                 $title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
1659                 $alt = ( 'alternate' == $alt ) ? '' : 'alternate';
1660
1661                 switch ( $post->post_status ) {
1662                         case 'publish' :
1663                         case 'private' :
1664                                 $stat = __('Published');
1665                                 break;
1666                         case 'future' :
1667                                 $stat = __('Scheduled');
1668                                 break;
1669                         case 'pending' :
1670                                 $stat = __('Pending Review');
1671                                 break;
1672                         case 'draft' :
1673                                 $stat = __('Draft');
1674                                 break;
1675                 }
1676
1677                 if ( '0000-00-00 00:00:00' == $post->post_date ) {
1678                         $time = '';
1679                 } else {
1680                         /* translators: date format in table columns, see http://php.net/date */
1681                         $time = mysql2date(__('Y/m/d'), $post->post_date);
1682                 }
1683
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";
1686         }
1687
1688         $html .= '</tbody></table>';
1689
1690         wp_send_json_success( $html );
1691 }
1692
1693 /**
1694  * Ajax handler for saving the widgets order.
1695  *
1696  * @since 3.1.0
1697  */
1698 function wp_ajax_widgets_order() {
1699         check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1700
1701         if ( !current_user_can('edit_theme_options') )
1702                 wp_die( -1 );
1703
1704         unset( $_POST['savewidgets'], $_POST['action'] );
1705
1706         // Save widgets order for all sidebars.
1707         if ( is_array($_POST['sidebars']) ) {
1708                 $sidebars = array();
1709                 foreach ( $_POST['sidebars'] as $key => $val ) {
1710                         $sb = array();
1711                         if ( !empty($val) ) {
1712                                 $val = explode(',', $val);
1713                                 foreach ( $val as $k => $v ) {
1714                                         if ( strpos($v, 'widget-') === false )
1715                                                 continue;
1716
1717                                         $sb[$k] = substr($v, strpos($v, '_') + 1);
1718                                 }
1719                         }
1720                         $sidebars[$key] = $sb;
1721                 }
1722                 wp_set_sidebars_widgets($sidebars);
1723                 wp_die( 1 );
1724         }
1725
1726         wp_die( -1 );
1727 }
1728
1729 /**
1730  * Ajax handler for saving a widget.
1731  *
1732  * @since 3.1.0
1733  */
1734 function wp_ajax_save_widget() {
1735         global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
1736
1737         check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1738
1739         if ( !current_user_can('edit_theme_options') || !isset($_POST['id_base']) )
1740                 wp_die( -1 );
1741
1742         unset( $_POST['savewidgets'], $_POST['action'] );
1743
1744         /**
1745          * Fires early when editing the widgets displayed in sidebars.
1746          *
1747          * @since 2.8.0
1748          */
1749         do_action( 'load-widgets.php' );
1750
1751         /**
1752          * Fires early when editing the widgets displayed in sidebars.
1753          *
1754          * @since 2.8.0
1755          */
1756         do_action( 'widgets.php' );
1757
1758         /** This action is documented in wp-admin/widgets.php */
1759         do_action( 'sidebar_admin_setup' );
1760
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>';
1767
1768         $sidebars = wp_get_sidebars_widgets();
1769         $sidebar = isset($sidebars[$sidebar_id]) ? $sidebars[$sidebar_id] : array();
1770
1771         // Delete.
1772         if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1773
1774                 if ( !isset($wp_registered_widgets[$widget_id]) )
1775                         wp_die( $error );
1776
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 )
1781                         wp_die( $error );
1782
1783                 $_POST['widget-' . $id_base] = array( $multi_number => array_shift($settings) );
1784                 $widget_id = $id_base . '-' . $multi_number;
1785                 $sidebar[] = $widget_id;
1786         }
1787         $_POST['widget-id'] = $sidebar;
1788
1789         foreach ( (array) $wp_registered_widget_updates as $name => $control ) {
1790
1791                 if ( $name == $id_base ) {
1792                         if ( !is_callable( $control['callback'] ) )
1793                                 continue;
1794
1795                         ob_start();
1796                                 call_user_func_array( $control['callback'], $control['params'] );
1797                         ob_end_clean();
1798                         break;
1799                 }
1800         }
1801
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";
1806                 wp_die();
1807         }
1808
1809         if ( !empty($_POST['add_new']) )
1810                 wp_die();
1811
1812         if ( $form = $wp_registered_widget_controls[$widget_id] )
1813                 call_user_func_array( $form['callback'], $form['params'] );
1814
1815         wp_die();
1816 }
1817
1818 /**
1819  * Ajax handler for saving a widget.
1820  *
1821  * @since 3.9.0
1822  */
1823 function wp_ajax_update_widget() {
1824         global $wp_customize;
1825         $wp_customize->widgets->wp_ajax_update_widget();
1826 }
1827
1828 /**
1829  * Ajax handler for uploading attachments
1830  *
1831  * @since 3.3.0
1832  */
1833 function wp_ajax_upload_attachment() {
1834         check_ajax_referer( 'media-form' );
1835
1836         if ( ! current_user_can( 'upload_files' ) ) {
1837                 wp_send_json_error( array(
1838                         'message'  => __( "You don't have permission to upload files." ),
1839                         'filename' => $_FILES['async-upload']['name'],
1840                 ) );
1841         }
1842
1843         if ( isset( $_REQUEST['post_id'] ) ) {
1844                 $post_id = $_REQUEST['post_id'];
1845                 if ( ! current_user_can( 'edit_post', $post_id ) ) {
1846                         wp_send_json_error( array(
1847                                 'message'  => __( "You don't have permission to attach files to this post." ),
1848                                 'filename' => $_FILES['async-upload']['name'],
1849                         ) );
1850                 }
1851         } else {
1852                 $post_id = null;
1853         }
1854
1855         $post_data = isset( $_REQUEST['post_data'] ) ? $_REQUEST['post_data'] : array();
1856
1857         // If the context is custom header or background, make sure the uploaded file is an image.
1858         if ( isset( $post_data['context'] ) && in_array( $post_data['context'], array( 'custom-header', 'custom-background' ) ) ) {
1859                 $wp_filetype = wp_check_filetype_and_ext( $_FILES['async-upload']['tmp_name'], $_FILES['async-upload']['name'], false );
1860                 if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) {
1861                         wp_send_json_error( array(
1862                                 'message'  => __( 'The uploaded file is not a valid image. Please try again.' ),
1863                                 'filename' => $_FILES['async-upload']['name'],
1864                         ) );
1865                 }
1866         }
1867
1868         $attachment_id = media_handle_upload( 'async-upload', $post_id, $post_data );
1869
1870         if ( is_wp_error( $attachment_id ) ) {
1871                 wp_send_json_error( array(
1872                         'message'  => $attachment_id->get_error_message(),
1873                         'filename' => $_FILES['async-upload']['name'],
1874                 ) );
1875         }
1876
1877         if ( isset( $post_data['context'] ) && isset( $post_data['theme'] ) ) {
1878                 if ( 'custom-background' === $post_data['context'] )
1879                         update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', $post_data['theme'] );
1880
1881                 if ( 'custom-header' === $post_data['context'] )
1882                         update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', $post_data['theme'] );
1883         }
1884
1885         if ( ! $attachment = wp_prepare_attachment_for_js( $attachment_id ) )
1886                 wp_die();
1887
1888         wp_send_json_success( $attachment );
1889 }
1890
1891 /**
1892  * Ajax handler for image editing.
1893  *
1894  * @since 3.1.0
1895  */
1896 function wp_ajax_image_editor() {
1897         $attachment_id = intval($_POST['postid']);
1898         if ( empty($attachment_id) || !current_user_can('edit_post', $attachment_id) )
1899                 wp_die( -1 );
1900
1901         check_ajax_referer( "image_editor-$attachment_id" );
1902         include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
1903
1904         $msg = false;
1905         switch ( $_POST['do'] ) {
1906                 case 'save' :
1907                         $msg = wp_save_image($attachment_id);
1908                         $msg = wp_json_encode($msg);
1909                         wp_die( $msg );
1910                         break;
1911                 case 'scale' :
1912                         $msg = wp_save_image($attachment_id);
1913                         break;
1914                 case 'restore' :
1915                         $msg = wp_restore_image($attachment_id);
1916                         break;
1917         }
1918
1919         wp_image_editor($attachment_id, $msg);
1920         wp_die();
1921 }
1922
1923 /**
1924  * Ajax handler for setting the featured image.
1925  *
1926  * @since 3.1.0
1927  */
1928 function wp_ajax_set_post_thumbnail() {
1929         $json = ! empty( $_REQUEST['json'] ); // New-style request
1930
1931         $post_ID = intval( $_POST['post_id'] );
1932         if ( ! current_user_can( 'edit_post', $post_ID ) )
1933                 wp_die( -1 );
1934
1935         $thumbnail_id = intval( $_POST['thumbnail_id'] );
1936
1937         if ( $json )
1938                 check_ajax_referer( "update-post_$post_ID" );
1939         else
1940                 check_ajax_referer( "set_post_thumbnail-$post_ID" );
1941
1942         if ( $thumbnail_id == '-1' ) {
1943                 if ( delete_post_thumbnail( $post_ID ) ) {
1944                         $return = _wp_post_thumbnail_html( null, $post_ID );
1945                         $json ? wp_send_json_success( $return ) : wp_die( $return );
1946                 } else {
1947                         wp_die( 0 );
1948                 }
1949         }
1950
1951         if ( set_post_thumbnail( $post_ID, $thumbnail_id ) ) {
1952                 $return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID );
1953                 $json ? wp_send_json_success( $return ) : wp_die( $return );
1954         }
1955
1956         wp_die( 0 );
1957 }
1958
1959 /**
1960  * AJAX handler for setting the featured image for an attachment.
1961  *
1962  * @since 4.0.0
1963  *
1964  * @see set_post_thumbnail()
1965  */
1966 function wp_ajax_set_attachment_thumbnail() {
1967         if ( empty( $_POST['urls'] ) || ! is_array( $_POST['urls'] ) ) {
1968                 wp_send_json_error();
1969         }
1970
1971         $thumbnail_id = (int) $_POST['thumbnail_id'];
1972         if ( empty( $thumbnail_id ) ) {
1973                 wp_send_json_error();
1974         }
1975
1976         $post_ids = array();
1977         // For each URL, try to find its corresponding post ID.
1978         foreach ( $_POST['urls'] as $url ) {
1979                 $post_id = attachment_url_to_postid( $url );
1980                 if ( ! empty( $post_id ) ) {
1981                         $post_ids[] = $post_id;
1982                 }
1983         }
1984
1985         if ( empty( $post_ids ) ) {
1986                 wp_send_json_error();
1987         }
1988
1989         $success = 0;
1990         // For each found attachment, set its thumbnail.
1991         foreach ( $post_ids as $post_id ) {
1992                 if ( ! current_user_can( 'edit_post', $post_id ) ) {
1993                         continue;
1994                 }
1995
1996                 if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) {
1997                         $success++;
1998                 }
1999         }
2000
2001         if ( 0 === $success ) {
2002                 wp_send_json_error();
2003         } else {
2004                 wp_send_json_success();
2005         }
2006
2007         wp_send_json_error();
2008 }
2009
2010 /**
2011  * Ajax handler for date formatting.
2012  *
2013  * @since 3.1.0
2014  */
2015 function wp_ajax_date_format() {
2016         wp_die( date_i18n( sanitize_option( 'date_format', wp_unslash( $_POST['date'] ) ) ) );
2017 }
2018
2019 /**
2020  * Ajax handler for time formatting.
2021  *
2022  * @since 3.1.0
2023  */
2024 function wp_ajax_time_format() {
2025         wp_die( date_i18n( sanitize_option( 'time_format', wp_unslash( $_POST['date'] ) ) ) );
2026 }
2027
2028 /**
2029  * Ajax handler for saving posts from the fullscreen editor.
2030  *
2031  * @since 3.1.0
2032  */
2033 function wp_ajax_wp_fullscreen_save_post() {
2034         $post_id = isset( $_POST['post_ID'] ) ? (int) $_POST['post_ID'] : 0;
2035
2036         $post = null;
2037
2038         if ( $post_id )
2039                 $post = get_post( $post_id );
2040
2041         check_ajax_referer('update-post_' . $post_id, '_wpnonce');
2042
2043         $post_id = edit_post();
2044
2045         if ( is_wp_error( $post_id ) ) {
2046                 wp_send_json_error();
2047         }
2048
2049         if ( $post ) {
2050                 $last_date = mysql2date( get_option('date_format'), $post->post_modified );
2051                 $last_time = mysql2date( get_option('time_format'), $post->post_modified );
2052         } else {
2053                 $last_date = date_i18n( get_option('date_format') );
2054                 $last_time = date_i18n( get_option('time_format') );
2055         }
2056
2057         if ( $last_id = get_post_meta( $post_id, '_edit_last', true ) ) {
2058                 $last_user = get_userdata( $last_id );
2059                 $last_edited = sprintf( __('Last edited by %1$s on %2$s at %3$s'), esc_html( $last_user->display_name ), $last_date, $last_time );
2060         } else {
2061                 $last_edited = sprintf( __('Last edited on %1$s at %2$s'), $last_date, $last_time );
2062         }
2063
2064         wp_send_json_success( array( 'last_edited' => $last_edited ) );
2065 }
2066
2067 /**
2068  * Ajax handler for removing a post lock.
2069  *
2070  * @since 3.1.0
2071  */
2072 function wp_ajax_wp_remove_post_lock() {
2073         if ( empty( $_POST['post_ID'] ) || empty( $_POST['active_post_lock'] ) )
2074                 wp_die( 0 );
2075         $post_id = (int) $_POST['post_ID'];
2076         if ( ! $post = get_post( $post_id ) )
2077                 wp_die( 0 );
2078
2079         check_ajax_referer( 'update-post_' . $post_id );
2080
2081         if ( ! current_user_can( 'edit_post', $post_id ) )
2082                 wp_die( -1 );
2083
2084         $active_lock = array_map( 'absint', explode( ':', $_POST['active_post_lock'] ) );
2085         if ( $active_lock[1] != get_current_user_id() )
2086                 wp_die( 0 );
2087
2088         /**
2089          * Filter the post lock window duration.
2090          *
2091          * @since 3.3.0
2092          *
2093          * @param int $interval The interval in seconds the post lock duration
2094          *                      should last, plus 5 seconds. Default 150.
2095          */
2096         $new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 150 ) + 5 ) . ':' . $active_lock[1];
2097         update_post_meta( $post_id, '_edit_lock', $new_lock, implode( ':', $active_lock ) );
2098         wp_die( 1 );
2099 }
2100
2101 /**
2102  * Ajax handler for dismissing a WordPress pointer.
2103  *
2104  * @since 3.1.0
2105  */
2106 function wp_ajax_dismiss_wp_pointer() {
2107         $pointer = $_POST['pointer'];
2108         if ( $pointer != sanitize_key( $pointer ) )
2109                 wp_die( 0 );
2110
2111 //      check_ajax_referer( 'dismiss-pointer_' . $pointer );
2112
2113         $dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) );
2114
2115         if ( in_array( $pointer, $dismissed ) )
2116                 wp_die( 0 );
2117
2118         $dismissed[] = $pointer;
2119         $dismissed = implode( ',', $dismissed );
2120
2121         update_user_meta( get_current_user_id(), 'dismissed_wp_pointers', $dismissed );
2122         wp_die( 1 );
2123 }
2124
2125 /**
2126  * Ajax handler for getting an attachment.
2127  *
2128  * @since 3.5.0
2129  */
2130 function wp_ajax_get_attachment() {
2131         if ( ! isset( $_REQUEST['id'] ) )
2132                 wp_send_json_error();
2133
2134         if ( ! $id = absint( $_REQUEST['id'] ) )
2135                 wp_send_json_error();
2136
2137         if ( ! $post = get_post( $id ) )
2138                 wp_send_json_error();
2139
2140         if ( 'attachment' != $post->post_type )
2141                 wp_send_json_error();
2142
2143         if ( ! current_user_can( 'upload_files' ) )
2144                 wp_send_json_error();
2145
2146         if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2147                 wp_send_json_error();
2148
2149         wp_send_json_success( $attachment );
2150 }
2151
2152 /**
2153  * Ajax handler for querying attachments.
2154  *
2155  * @since 3.5.0
2156  */
2157 function wp_ajax_query_attachments() {
2158         if ( ! current_user_can( 'upload_files' ) )
2159                 wp_send_json_error();
2160
2161         $query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array();
2162         $query = array_intersect_key( $query, array_flip( array(
2163                 's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type',
2164                 'post_parent', 'post__in', 'post__not_in', 'year', 'monthnum'
2165         ) ) );
2166
2167         $query['post_type'] = 'attachment';
2168         if ( MEDIA_TRASH
2169                 && ! empty( $_REQUEST['query']['post_status'] )
2170                 && 'trash' === $_REQUEST['query']['post_status'] ) {
2171                 $query['post_status'] = 'trash';
2172         } else {
2173                 $query['post_status'] = 'inherit';
2174         }
2175
2176         if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) )
2177                 $query['post_status'] .= ',private';
2178
2179         /**
2180          * Filter the arguments passed to WP_Query during an AJAX
2181          * call for querying attachments.
2182          *
2183          * @since 3.7.0
2184          *
2185          * @see WP_Query::parse_query()
2186          *
2187          * @param array $query An array of query variables.
2188          */
2189         $query = apply_filters( 'ajax_query_attachments_args', $query );
2190         $query = new WP_Query( $query );
2191
2192         $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts );
2193         $posts = array_filter( $posts );
2194
2195         wp_send_json_success( $posts );
2196 }
2197
2198 /**
2199  * Ajax handler for updating attachment attributes.
2200  *
2201  * @since 3.5.0
2202  */
2203 function wp_ajax_save_attachment() {
2204         if ( ! isset( $_REQUEST['id'] ) || ! isset( $_REQUEST['changes'] ) )
2205                 wp_send_json_error();
2206
2207         if ( ! $id = absint( $_REQUEST['id'] ) )
2208                 wp_send_json_error();
2209
2210         check_ajax_referer( 'update-post_' . $id, 'nonce' );
2211
2212         if ( ! current_user_can( 'edit_post', $id ) )
2213                 wp_send_json_error();
2214
2215         $changes = $_REQUEST['changes'];
2216         $post    = get_post( $id, ARRAY_A );
2217
2218         if ( 'attachment' != $post['post_type'] )
2219                 wp_send_json_error();
2220
2221         if ( isset( $changes['title'] ) )
2222                 $post['post_title'] = $changes['title'];
2223
2224         if ( isset( $changes['caption'] ) )
2225                 $post['post_excerpt'] = $changes['caption'];
2226
2227         if ( isset( $changes['description'] ) )
2228                 $post['post_content'] = $changes['description'];
2229
2230         if ( MEDIA_TRASH && isset( $changes['status'] ) )
2231                 $post['post_status'] = $changes['status'];
2232
2233         if ( isset( $changes['alt'] ) ) {
2234                 $alt = wp_unslash( $changes['alt'] );
2235                 if ( $alt != get_post_meta( $id, '_wp_attachment_image_alt', true ) ) {
2236                         $alt = wp_strip_all_tags( $alt, true );
2237                         update_post_meta( $id, '_wp_attachment_image_alt', wp_slash( $alt ) );
2238                 }
2239         }
2240
2241         if ( 0 === strpos( $post['post_mime_type'], 'audio/' ) ) {
2242                 $changed = false;
2243                 $id3data = wp_get_attachment_metadata( $post['ID'] );
2244                 if ( ! is_array( $id3data ) ) {
2245                         $changed = true;
2246                         $id3data = array();
2247                 }
2248                 foreach ( wp_get_attachment_id3_keys( (object) $post, 'edit' ) as $key => $label ) {
2249                         if ( isset( $changes[ $key ] ) ) {
2250                                 $changed = true;
2251                                 $id3data[ $key ] = sanitize_text_field( wp_unslash( $changes[ $key ] ) );
2252                         }
2253                 }
2254
2255                 if ( $changed ) {
2256                         wp_update_attachment_metadata( $id, $id3data );
2257                 }
2258         }
2259
2260         if ( MEDIA_TRASH && isset( $changes['status'] ) && 'trash' === $changes['status'] ) {
2261                 wp_delete_post( $id );
2262         } else {
2263                 wp_update_post( $post );
2264         }
2265
2266         wp_send_json_success();
2267 }
2268
2269 /**
2270  * Ajax handler for saving backwards compatible attachment attributes.
2271  *
2272  * @since 3.5.0
2273  */
2274 function wp_ajax_save_attachment_compat() {
2275         if ( ! isset( $_REQUEST['id'] ) )
2276                 wp_send_json_error();
2277
2278         if ( ! $id = absint( $_REQUEST['id'] ) )
2279                 wp_send_json_error();
2280
2281         if ( empty( $_REQUEST['attachments'] ) || empty( $_REQUEST['attachments'][ $id ] ) )
2282                 wp_send_json_error();
2283         $attachment_data = $_REQUEST['attachments'][ $id ];
2284
2285         check_ajax_referer( 'update-post_' . $id, 'nonce' );
2286
2287         if ( ! current_user_can( 'edit_post', $id ) )
2288                 wp_send_json_error();
2289
2290         $post = get_post( $id, ARRAY_A );
2291
2292         if ( 'attachment' != $post['post_type'] )
2293                 wp_send_json_error();
2294
2295         /** This filter is documented in wp-admin/includes/media.php */
2296         $post = apply_filters( 'attachment_fields_to_save', $post, $attachment_data );
2297
2298         if ( isset( $post['errors'] ) ) {
2299                 $errors = $post['errors']; // @todo return me and display me!
2300                 unset( $post['errors'] );
2301         }
2302
2303         wp_update_post( $post );
2304
2305         foreach ( get_attachment_taxonomies( $post ) as $taxonomy ) {
2306                 if ( isset( $attachment_data[ $taxonomy ] ) )
2307                         wp_set_object_terms( $id, array_map( 'trim', preg_split( '/,+/', $attachment_data[ $taxonomy ] ) ), $taxonomy, false );
2308         }
2309
2310         if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2311                 wp_send_json_error();
2312
2313         wp_send_json_success( $attachment );
2314 }
2315
2316 /**
2317  * Ajax handler for saving the attachment order.
2318  *
2319  * @since 3.5.0
2320  */
2321 function wp_ajax_save_attachment_order() {
2322         if ( ! isset( $_REQUEST['post_id'] ) )
2323                 wp_send_json_error();
2324
2325         if ( ! $post_id = absint( $_REQUEST['post_id'] ) )
2326                 wp_send_json_error();
2327
2328         if ( empty( $_REQUEST['attachments'] ) )
2329                 wp_send_json_error();
2330
2331         check_ajax_referer( 'update-post_' . $post_id, 'nonce' );
2332
2333         $attachments = $_REQUEST['attachments'];
2334
2335         if ( ! current_user_can( 'edit_post', $post_id ) )
2336                 wp_send_json_error();
2337
2338         foreach ( $attachments as $attachment_id => $menu_order ) {
2339                 if ( ! current_user_can( 'edit_post', $attachment_id ) )
2340                         continue;
2341                 if ( ! $attachment = get_post( $attachment_id ) )
2342                         continue;
2343                 if ( 'attachment' != $attachment->post_type )
2344                         continue;
2345
2346                 wp_update_post( array( 'ID' => $attachment_id, 'menu_order' => $menu_order ) );
2347         }
2348
2349         wp_send_json_success();
2350 }
2351
2352 /**
2353  * Ajax handler for sending an attachment to the editor.
2354  *
2355  * Generates the HTML to send an attachment to the editor.
2356  * Backwards compatible with the media_send_to_editor filter
2357  * and the chain of filters that follow.
2358  *
2359  * @since 3.5.0
2360  */
2361 function wp_ajax_send_attachment_to_editor() {
2362         check_ajax_referer( 'media-send-to-editor', 'nonce' );
2363
2364         $attachment = wp_unslash( $_POST['attachment'] );
2365
2366         $id = intval( $attachment['id'] );
2367
2368         if ( ! $post = get_post( $id ) )
2369                 wp_send_json_error();
2370
2371         if ( 'attachment' != $post->post_type )
2372                 wp_send_json_error();
2373
2374         if ( current_user_can( 'edit_post', $id ) ) {
2375                 // If this attachment is unattached, attach it. Primarily a back compat thing.
2376                 if ( 0 == $post->post_parent && $insert_into_post_id = intval( $_POST['post_id'] ) ) {
2377                         wp_update_post( array( 'ID' => $id, 'post_parent' => $insert_into_post_id ) );
2378                 }
2379         }
2380
2381         $rel = $url = '';
2382         $html = isset( $attachment['post_title'] ) ? $attachment['post_title'] : '';
2383         if ( ! empty( $attachment['url'] ) ) {
2384                 $url = $attachment['url'];
2385                 if ( strpos( $url, 'attachment_id') || get_attachment_link( $id ) == $url )
2386                         $rel = ' rel="attachment wp-att-' . $id . '"';
2387                 $html = '<a href="' . esc_url( $url ) . '"' . $rel . '>' . $html . '</a>';
2388         }
2389
2390         remove_filter( 'media_send_to_editor', 'image_media_send_to_editor' );
2391
2392         if ( 'image' === substr( $post->post_mime_type, 0, 5 ) ) {
2393                 $align = isset( $attachment['align'] ) ? $attachment['align'] : 'none';
2394                 $size = isset( $attachment['image-size'] ) ? $attachment['image-size'] : 'medium';
2395                 $alt = isset( $attachment['image_alt'] ) ? $attachment['image_alt'] : '';
2396                 $caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : '';
2397                 $title = ''; // We no longer insert title tags into <img> tags, as they are redundant.
2398                 $html = get_image_send_to_editor( $id, $caption, $title, $align, $url, (bool) $rel, $size, $alt );
2399         } elseif ( 'video' === substr( $post->post_mime_type, 0, 5 ) || 'audio' === substr( $post->post_mime_type, 0, 5 )  ) {
2400                 $html = stripslashes_deep( $_POST['html'] );
2401         }
2402
2403         /** This filter is documented in wp-admin/includes/media.php */
2404         $html = apply_filters( 'media_send_to_editor', $html, $id, $attachment );
2405
2406         wp_send_json_success( $html );
2407 }
2408
2409 /**
2410  * Ajax handler for sending a link to the editor.
2411  *
2412  * Generates the HTML to send a non-image embed link to the editor.
2413  *
2414  * Backwards compatible with the following filters:
2415  * - file_send_to_editor_url
2416  * - audio_send_to_editor_url
2417  * - video_send_to_editor_url
2418  *
2419  * @since 3.5.0
2420  */
2421 function wp_ajax_send_link_to_editor() {
2422         global $post, $wp_embed;
2423
2424         check_ajax_referer( 'media-send-to-editor', 'nonce' );
2425
2426         if ( ! $src = wp_unslash( $_POST['src'] ) )
2427                 wp_send_json_error();
2428
2429         if ( ! strpos( $src, '://' ) )
2430                 $src = 'http://' . $src;
2431
2432         if ( ! $src = esc_url_raw( $src ) )
2433                 wp_send_json_error();
2434
2435         if ( ! $title = trim( wp_unslash( $_POST['title'] ) ) )
2436                 $title = wp_basename( $src );
2437
2438         $post = get_post( isset( $_POST['post_id'] ) ? $_POST['post_id'] : 0 );
2439
2440         // Ping WordPress for an embed.
2441         $check_embed = $wp_embed->run_shortcode( '[embed]'. $src .'[/embed]' );
2442
2443         // Fallback that WordPress creates when no oEmbed was found.
2444         $fallback = $wp_embed->maybe_make_link( $src );
2445
2446         if ( $check_embed !== $fallback ) {
2447                 // TinyMCE view for [embed] will parse this
2448                 $html = '[embed]' . $src . '[/embed]';
2449         } elseif ( $title ) {
2450                 $html = '<a href="' . esc_url( $src ) . '">' . $title . '</a>';
2451         } else {
2452                 $html = '';
2453         }
2454
2455         // Figure out what filter to run:
2456         $type = 'file';
2457         if ( ( $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $src ) ) && ( $ext_type = wp_ext2type( $ext ) )
2458                 && ( 'audio' == $ext_type || 'video' == $ext_type ) )
2459                         $type = $ext_type;
2460
2461         /** This filter is documented in wp-admin/includes/media.php */
2462         $html = apply_filters( $type . '_send_to_editor_url', $html, $src, $title );
2463
2464         wp_send_json_success( $html );
2465 }
2466
2467 /**
2468  * Ajax handler for the Heartbeat API.
2469  *
2470  * Runs when the user is logged in.
2471  *
2472  * @since 3.6.0
2473  */
2474 function wp_ajax_heartbeat() {
2475         if ( empty( $_POST['_nonce'] ) )
2476                 wp_send_json_error();
2477
2478         $response = array();
2479
2480         if ( false === wp_verify_nonce( $_POST['_nonce'], 'heartbeat-nonce' ) ) {
2481                 // User is logged in but nonces have expired.
2482                 $response['nonces_expired'] = true;
2483                 wp_send_json($response);
2484         }
2485
2486         // screen_id is the same as $current_screen->id and the JS global 'pagenow'.
2487         if ( ! empty($_POST['screen_id']) )
2488                 $screen_id = sanitize_key($_POST['screen_id']);
2489         else
2490                 $screen_id = 'front';
2491
2492         if ( ! empty($_POST['data']) ) {
2493                 $data = wp_unslash( (array) $_POST['data'] );
2494
2495                 /**
2496                  * Filter the Heartbeat response received.
2497                  *
2498                  * @since 3.6.0
2499                  *
2500                  * @param array|object $response  The Heartbeat response object or array.
2501                  * @param array        $data      The $_POST data sent.
2502                  * @param string       $screen_id The screen id.
2503                  */
2504                 $response = apply_filters( 'heartbeat_received', $response, $data, $screen_id );
2505         }
2506
2507         /**
2508          * Filter the Heartbeat response sent.
2509          *
2510          * @since 3.6.0
2511          *
2512          * @param array|object $response  The Heartbeat response object or array.
2513          * @param string       $screen_id The screen id.
2514          */
2515         $response = apply_filters( 'heartbeat_send', $response, $screen_id );
2516
2517         /**
2518          * Fires when Heartbeat ticks in logged-in environments.
2519          *
2520          * Allows the transport to be easily replaced with long-polling.
2521          *
2522          * @since 3.6.0
2523          *
2524          * @param array|object $response  The Heartbeat response object or array.
2525          * @param string       $screen_id The screen id.
2526          */
2527         do_action( 'heartbeat_tick', $response, $screen_id );
2528
2529         // Send the current time according to the server
2530         $response['server_time'] = time();
2531
2532         wp_send_json($response);
2533 }
2534
2535 /**
2536  * Ajax handler for getting revision diffs.
2537  *
2538  * @since 3.6.0
2539  */
2540 function wp_ajax_get_revision_diffs() {
2541         require ABSPATH . 'wp-admin/includes/revision.php';
2542
2543         if ( ! $post = get_post( (int) $_REQUEST['post_id'] ) )
2544                 wp_send_json_error();
2545
2546         if ( ! current_user_can( 'read_post', $post->ID ) )
2547                 wp_send_json_error();
2548
2549         // Really just pre-loading the cache here.
2550         if ( ! $revisions = wp_get_post_revisions( $post->ID, array( 'check_enabled' => false ) ) )
2551                 wp_send_json_error();
2552
2553         $return = array();
2554         @set_time_limit( 0 );
2555
2556         foreach ( $_REQUEST['compare'] as $compare_key ) {
2557                 list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to
2558
2559                 $return[] = array(
2560                         'id' => $compare_key,
2561                         'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ),
2562                 );
2563         }
2564         wp_send_json_success( $return );
2565 }
2566
2567 /**
2568  * Ajax handler for auto-saving the selected color scheme for
2569  * a user's own profile.
2570  *
2571  * @since 3.8.0
2572  */
2573 function wp_ajax_save_user_color_scheme() {
2574         global $_wp_admin_css_colors;
2575
2576         check_ajax_referer( 'save-color-scheme', 'nonce' );
2577
2578         $color_scheme = sanitize_key( $_POST['color_scheme'] );
2579
2580         if ( ! isset( $_wp_admin_css_colors[ $color_scheme ] ) ) {
2581                 wp_send_json_error();
2582         }
2583
2584         update_user_meta( get_current_user_id(), 'admin_color', $color_scheme );
2585         wp_send_json_success();
2586 }
2587
2588 /**
2589  * Ajax handler for getting themes from themes_api().
2590  *
2591  * @since 3.9.0
2592  */
2593 function wp_ajax_query_themes() {
2594         global $themes_allowedtags, $theme_field_defaults;
2595
2596         if ( ! current_user_can( 'install_themes' ) ) {
2597                 wp_send_json_error();
2598         }
2599
2600         $args = wp_parse_args( wp_unslash( $_REQUEST['request'] ), array(
2601                 'per_page' => 20,
2602                 'fields'   => $theme_field_defaults
2603         ) );
2604
2605         $old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search';
2606
2607         /** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */
2608         $args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args );
2609
2610         $api = themes_api( 'query_themes', $args );
2611
2612         if ( is_wp_error( $api ) ) {
2613                 wp_send_json_error();
2614         }
2615
2616         $update_php = network_admin_url( 'update.php?action=install-theme' );
2617         foreach ( $api->themes as &$theme ) {
2618                 $theme->install_url = add_query_arg( array(
2619                         'theme'    => $theme->slug,
2620                         '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug )
2621                 ), $update_php );
2622
2623                 $theme->name        = wp_kses( $theme->name, $themes_allowedtags );
2624                 $theme->author      = wp_kses( $theme->author, $themes_allowedtags );
2625                 $theme->version     = wp_kses( $theme->version, $themes_allowedtags );
2626                 $theme->description = wp_kses( $theme->description, $themes_allowedtags );
2627                 $theme->num_ratings = sprintf( _n( '(based on %s rating)', '(based on %s ratings)', $theme->num_ratings ), number_format_i18n( $theme->num_ratings ) );
2628                 $theme->preview_url = set_url_scheme( $theme->preview_url );
2629         }
2630
2631         wp_send_json_success( $api );
2632 }
2633
2634 /**
2635  * Apply [embed] AJAX handlers to a string.
2636  *
2637  * @since 4.0.0
2638  *
2639  * @global WP_Post  $post     Global $post.
2640  * @global WP_Embed $wp_embed Embed API instance.
2641  */
2642 function wp_ajax_parse_embed() {
2643         global $post, $wp_embed;
2644
2645         if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) {
2646                 wp_send_json_error();
2647         }
2648
2649         if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) {
2650                 wp_send_json_error();
2651         }
2652
2653         $shortcode = wp_unslash( $_POST['shortcode'] );
2654         $url = str_replace( '[embed]', '', str_replace( '[/embed]', '', $shortcode ) );
2655         $parsed = false;
2656         setup_postdata( $post );
2657
2658         $wp_embed->return_false_on_fail = true;
2659
2660         if ( is_ssl() && preg_match( '%^\\[embed[^\\]]*\\]http://%i', $shortcode ) ) {
2661                 // Admin is ssl and the user pasted non-ssl URL.
2662                 // Check if the provider supports ssl embeds and use that for the preview.
2663                 $ssl_shortcode = preg_replace( '%^(\\[embed[^\\]]*\\])http://%i', '$1https://', $shortcode );
2664                 $parsed = $wp_embed->run_shortcode( $ssl_shortcode );
2665
2666                 if ( ! $parsed ) {
2667                         $no_ssl_support = true;
2668                 }
2669         }
2670
2671         if ( ! $parsed ) {
2672                 $parsed = $wp_embed->run_shortcode( $shortcode );
2673         }
2674
2675         if ( ! $parsed ) {
2676                 wp_send_json_error( array(
2677                         'type' => 'not-embeddable',
2678                         'message' => sprintf( __( '%s failed to embed.' ), '<code>' . esc_html( $url ) . '</code>' ),
2679                 ) );
2680         }
2681
2682         if ( has_shortcode( $parsed, 'audio' ) || has_shortcode( $parsed, 'video' ) ) {
2683                 $styles = '';
2684                 $mce_styles = wpview_media_sandbox_styles();
2685                 foreach ( $mce_styles as $style ) {
2686                         $styles .= sprintf( '<link rel="stylesheet" href="%s"/>', $style );
2687                 }
2688
2689                 $html = do_shortcode( $parsed );
2690
2691                 global $wp_scripts;
2692                 if ( ! empty( $wp_scripts ) ) {
2693                         $wp_scripts->done = array();
2694                 }
2695                 ob_start();
2696                 wp_print_scripts( 'wp-mediaelement' );
2697                 $scripts = ob_get_clean();
2698
2699                 $parsed = $styles . $html . $scripts;
2700         }
2701
2702
2703         if ( ! empty( $no_ssl_support ) || ( is_ssl() && ( preg_match( '%<(iframe|script|embed) [^>]*src="http://%', $parsed ) ||
2704                 preg_match( '%<link [^>]*href="http://%', $parsed ) ) ) ) {
2705                 // Admin is ssl and the embed is not. Iframes, scripts, and other "active content" will be blocked.
2706                 wp_send_json_error( array(
2707                         'type' => 'not-ssl',
2708                         'message' => __( 'This preview is unavailable in the editor.' ),
2709                 ) );
2710         }
2711
2712         wp_send_json_success( array(
2713                 'body' => $parsed
2714         ) );
2715 }
2716
2717 function wp_ajax_parse_media_shortcode() {
2718         global $post, $wp_scripts;
2719
2720         if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) {
2721                 wp_send_json_error();
2722         }
2723
2724         if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) {
2725                 wp_send_json_error();
2726         }
2727
2728         setup_postdata( $post );
2729         $shortcode = do_shortcode( wp_unslash( $_POST['shortcode'] ) );
2730
2731         if ( empty( $shortcode ) ) {
2732                 wp_send_json_error( array(
2733                         'type' => 'no-items',
2734                         'message' => __( 'No items found.' ),
2735                 ) );
2736         }
2737
2738         $head = '';
2739         $styles = wpview_media_sandbox_styles();
2740
2741         foreach ( $styles as $style ) {
2742                 $head .= '<link type="text/css" rel="stylesheet" href="' . $style . '">';
2743         }
2744
2745         if ( ! empty( $wp_scripts ) ) {
2746                 $wp_scripts->done = array();
2747         }
2748
2749         ob_start();
2750
2751         echo $shortcode;
2752
2753         if ( 'playlist' === $_REQUEST['type'] ) {
2754                 wp_underscore_playlist_templates();
2755
2756                 wp_print_scripts( 'wp-playlist' );
2757         } else {
2758                 wp_print_scripts( 'wp-mediaelement' );
2759         }
2760
2761         wp_send_json_success( array(
2762                 'head' => $head,
2763                 'body' => ob_get_clean()
2764         ) );
2765 }
2766
2767 /**
2768  * AJAX handler for destroying multiple open sessions for a user.
2769  *
2770  * @since 4.1.0
2771  */
2772 function wp_ajax_destroy_sessions() {
2773
2774         $user = get_userdata( (int) $_POST['user_id'] );
2775         if ( $user ) {
2776                 if ( ! current_user_can( 'edit_user', $user->ID ) ) {
2777                         $user = false;
2778                 } elseif ( ! wp_verify_nonce( $_POST['nonce'], 'update-user_' . $user->ID ) ) {
2779                         $user = false;
2780                 }
2781         }
2782
2783         if ( ! $user ) {
2784                 wp_send_json_error( array(
2785                         'message' => __( 'Could not log out user sessions. Please try again.' ),
2786                 ) );
2787         }
2788
2789         $sessions = WP_Session_Tokens::get_instance( $user->ID );
2790
2791         if ( $user->ID === get_current_user_id() ) {
2792                 $sessions->destroy_others( wp_get_session_token() );
2793                 $message = __( 'You are now logged out everywhere else.' );
2794         } else {
2795                 $sessions->destroy_all();
2796                 /* translators: 1: User's display name. */ 
2797                 $message = sprintf( __( '%s has been logged out.' ), $user->display_name );
2798         }
2799
2800         wp_send_json_success( array( 'message' => $message ) );
2801 }