]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-admin/includes/ajax-actions.php
WordPress 4.5.1
[autoinstalls/wordpress.git] / wp-admin / includes / ajax-actions.php
1 <?php
2 /**
3  * Administration API: Core Ajax handlers
4  *
5  * @package WordPress
6  * @subpackage Administration
7  * @since 2.1.0
8  */
9
10 //
11 // No-privilege Ajax handlers.
12 //
13
14 /**
15  * Ajax handler for the Heartbeat API in
16  * the no-privilege context.
17  *
18  * Runs when the user is not logged in.
19  *
20  * @since 3.6.0
21  */
22 function wp_ajax_nopriv_heartbeat() {
23         $response = array();
24
25         // screen_id is the same as $current_screen->id and the JS global 'pagenow'.
26         if ( ! empty($_POST['screen_id']) )
27                 $screen_id = sanitize_key($_POST['screen_id']);
28         else
29                 $screen_id = 'front';
30
31         if ( ! empty($_POST['data']) ) {
32                 $data = wp_unslash( (array) $_POST['data'] );
33
34                 /**
35                  * Filter Heartbeat AJAX response in no-privilege environments.
36                  *
37                  * @since 3.6.0
38                  *
39                  * @param array|object $response  The no-priv Heartbeat response object or array.
40                  * @param array        $data      An array of data passed via $_POST.
41                  * @param string       $screen_id The screen id.
42                  */
43                 $response = apply_filters( 'heartbeat_nopriv_received', $response, $data, $screen_id );
44         }
45
46         /**
47          * Filter Heartbeat AJAX response when no data is passed.
48          *
49          * @since 3.6.0
50          *
51          * @param array|object $response  The Heartbeat response object or array.
52          * @param string       $screen_id The screen id.
53          */
54         $response = apply_filters( 'heartbeat_nopriv_send', $response, $screen_id );
55
56         /**
57          * Fires when Heartbeat ticks in no-privilege environments.
58          *
59          * Allows the transport to be easily replaced with long-polling.
60          *
61          * @since 3.6.0
62          *
63          * @param array|object $response  The no-priv Heartbeat response.
64          * @param string       $screen_id The screen id.
65          */
66         do_action( 'heartbeat_nopriv_tick', $response, $screen_id );
67
68         // Send the current time according to the server.
69         $response['server_time'] = time();
70
71         wp_send_json($response);
72 }
73
74 //
75 // GET-based Ajax handlers.
76 //
77
78 /**
79  * Ajax handler for fetching a list table.
80  *
81  * @since 3.1.0
82  *
83  * @global WP_List_Table $wp_list_table
84  */
85 function wp_ajax_fetch_list() {
86         global $wp_list_table;
87
88         $list_class = $_GET['list_args']['class'];
89         check_ajax_referer( "fetch-list-$list_class", '_ajax_fetch_list_nonce' );
90
91         $wp_list_table = _get_list_table( $list_class, array( 'screen' => $_GET['list_args']['screen']['id'] ) );
92         if ( ! $wp_list_table )
93                 wp_die( 0 );
94
95         if ( ! $wp_list_table->ajax_user_can() )
96                 wp_die( -1 );
97
98         $wp_list_table->ajax_response();
99
100         wp_die( 0 );
101 }
102
103 /**
104  * Ajax handler for tag search.
105  *
106  * @since 3.1.0
107  */
108 function wp_ajax_ajax_tag_search() {
109         if ( ! isset( $_GET['tax'] ) ) {
110                 wp_die( 0 );
111         }
112
113         $taxonomy = sanitize_key( $_GET['tax'] );
114         $tax = get_taxonomy( $taxonomy );
115         if ( ! $tax ) {
116                 wp_die( 0 );
117         }
118
119         if ( ! current_user_can( $tax->cap->assign_terms ) ) {
120                 wp_die( -1 );
121         }
122
123         $s = wp_unslash( $_GET['q'] );
124
125         $comma = _x( ',', 'tag delimiter' );
126         if ( ',' !== $comma )
127                 $s = str_replace( $comma, ',', $s );
128         if ( false !== strpos( $s, ',' ) ) {
129                 $s = explode( ',', $s );
130                 $s = $s[count( $s ) - 1];
131         }
132         $s = trim( $s );
133
134         /**
135          * Filter the minimum number of characters required to fire a tag search via AJAX.
136          *
137          * @since 4.0.0
138          *
139          * @param int    $characters The minimum number of characters required. Default 2.
140          * @param object $tax        The taxonomy object.
141          * @param string $s          The search term.
142          */
143         $term_search_min_chars = (int) apply_filters( 'term_search_min_chars', 2, $tax, $s );
144
145         /*
146          * Require $term_search_min_chars chars for matching (default: 2)
147          * ensure it's a non-negative, non-zero integer.
148          */
149         if ( ( $term_search_min_chars == 0 ) || ( strlen( $s ) < $term_search_min_chars ) ){
150                 wp_die();
151         }
152
153         $results = get_terms( $taxonomy, array( 'name__like' => $s, 'fields' => 'names', 'hide_empty' => false ) );
154
155         echo join( $results, "\n" );
156         wp_die();
157 }
158
159 /**
160  * Ajax handler for compression testing.
161  *
162  * @since 3.1.0
163  */
164 function wp_ajax_wp_compression_test() {
165         if ( !current_user_can( 'manage_options' ) )
166                 wp_die( -1 );
167
168         if ( ini_get('zlib.output_compression') || 'ob_gzhandler' == ini_get('output_handler') ) {
169                 update_site_option('can_compress_scripts', 0);
170                 wp_die( 0 );
171         }
172
173         if ( isset($_GET['test']) ) {
174                 header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' );
175                 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
176                 header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
177                 header( 'Pragma: no-cache' );
178                 header('Content-Type: application/javascript; charset=UTF-8');
179                 $force_gzip = ( defined('ENFORCE_GZIP') && ENFORCE_GZIP );
180                 $test_str = '"wpCompressionTest Lorem ipsum dolor sit amet consectetuer mollis sapien urna ut a. Eu nonummy condimentum fringilla tempor pretium platea vel nibh netus Maecenas. Hac molestie amet justo quis pellentesque est ultrices interdum nibh Morbi. Cras mattis pretium Phasellus ante ipsum ipsum ut sociis Suspendisse Lorem. Ante et non molestie. Porta urna Vestibulum egestas id congue nibh eu risus gravida sit. Ac augue auctor Ut et non a elit massa id sodales. Elit eu Nulla at nibh adipiscing mattis lacus mauris at tempus. Netus nibh quis suscipit nec feugiat eget sed lorem et urna. Pellentesque lacus at ut massa consectetuer ligula ut auctor semper Pellentesque. Ut metus massa nibh quam Curabitur molestie nec mauris congue. Volutpat molestie elit justo facilisis neque ac risus Ut nascetur tristique. Vitae sit lorem tellus et quis Phasellus lacus tincidunt nunc Fusce. Pharetra wisi Suspendisse mus sagittis libero lacinia Integer consequat ac Phasellus. Et urna ac cursus tortor aliquam Aliquam amet tellus volutpat Vestibulum. Justo interdum condimentum In augue congue tellus sollicitudin Quisque quis nibh."';
181
182                  if ( 1 == $_GET['test'] ) {
183                         echo $test_str;
184                         wp_die();
185                  } elseif ( 2 == $_GET['test'] ) {
186                         if ( !isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
187                                 wp_die( -1 );
188                         if ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') && function_exists('gzdeflate') && ! $force_gzip ) {
189                                 header('Content-Encoding: deflate');
190                                 $out = gzdeflate( $test_str, 1 );
191                         } elseif ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') && function_exists('gzencode') ) {
192                                 header('Content-Encoding: gzip');
193                                 $out = gzencode( $test_str, 1 );
194                         } else {
195                                 wp_die( -1 );
196                         }
197                         echo $out;
198                         wp_die();
199                 } elseif ( 'no' == $_GET['test'] ) {
200                         check_ajax_referer( 'update_can_compress_scripts' );
201                         update_site_option('can_compress_scripts', 0);
202                 } elseif ( 'yes' == $_GET['test'] ) {
203                         check_ajax_referer( 'update_can_compress_scripts' );
204                         update_site_option('can_compress_scripts', 1);
205                 }
206         }
207
208         wp_die( 0 );
209 }
210
211 /**
212  * Ajax handler for image editor previews.
213  *
214  * @since 3.1.0
215  */
216 function wp_ajax_imgedit_preview() {
217         $post_id = intval($_GET['postid']);
218         if ( empty($post_id) || !current_user_can('edit_post', $post_id) )
219                 wp_die( -1 );
220
221         check_ajax_referer( "image_editor-$post_id" );
222
223         include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
224         if ( ! stream_preview_image($post_id) )
225                 wp_die( -1 );
226
227         wp_die();
228 }
229
230 /**
231  * Ajax handler for oEmbed caching.
232  *
233  * @since 3.1.0
234  *
235  * @global WP_Embed $wp_embed
236  */
237 function wp_ajax_oembed_cache() {
238         $GLOBALS['wp_embed']->cache_oembed( $_GET['post'] );
239         wp_die( 0 );
240 }
241
242 /**
243  * Ajax handler for user autocomplete.
244  *
245  * @since 3.4.0
246  */
247 function wp_ajax_autocomplete_user() {
248         if ( ! is_multisite() || ! current_user_can( 'promote_users' ) || wp_is_large_network( 'users' ) )
249                 wp_die( -1 );
250
251         /** This filter is documented in wp-admin/user-new.php */
252         if ( ! is_super_admin() && ! apply_filters( 'autocomplete_users_for_site_admins', false ) )
253                 wp_die( -1 );
254
255         $return = array();
256
257         // Check the type of request
258         // Current allowed values are `add` and `search`
259         if ( isset( $_REQUEST['autocomplete_type'] ) && 'search' === $_REQUEST['autocomplete_type'] ) {
260                 $type = $_REQUEST['autocomplete_type'];
261         } else {
262                 $type = 'add';
263         }
264
265         // Check the desired field for value
266         // Current allowed values are `user_email` and `user_login`
267         if ( isset( $_REQUEST['autocomplete_field'] ) && 'user_email' === $_REQUEST['autocomplete_field'] ) {
268                 $field = $_REQUEST['autocomplete_field'];
269         } else {
270                 $field = 'user_login';
271         }
272
273         // Exclude current users of this blog
274         if ( isset( $_REQUEST['site_id'] ) ) {
275                 $id = absint( $_REQUEST['site_id'] );
276         } else {
277                 $id = get_current_blog_id();
278         }
279
280         $include_blog_users = ( $type == 'search' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
281         $exclude_blog_users = ( $type == 'add' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
282
283         $users = get_users( array(
284                 'blog_id' => false,
285                 'search'  => '*' . $_REQUEST['term'] . '*',
286                 'include' => $include_blog_users,
287                 'exclude' => $exclude_blog_users,
288                 'search_columns' => array( 'user_login', 'user_nicename', 'user_email' ),
289         ) );
290
291         foreach ( $users as $user ) {
292                 $return[] = array(
293                         /* translators: 1: user_login, 2: user_email */
294                         'label' => sprintf( _x( '%1$s (%2$s)', 'user autocomplete result' ), $user->user_login, $user->user_email ),
295                         'value' => $user->$field,
296                 );
297         }
298
299         wp_die( wp_json_encode( $return ) );
300 }
301
302 /**
303  * Ajax handler for dashboard widgets.
304  *
305  * @since 3.4.0
306  */
307 function wp_ajax_dashboard_widgets() {
308         require_once ABSPATH . 'wp-admin/includes/dashboard.php';
309
310         $pagenow = $_GET['pagenow'];
311         if ( $pagenow === 'dashboard-user' || $pagenow === 'dashboard-network' || $pagenow === 'dashboard' ) {
312                 set_current_screen( $pagenow );
313         }
314
315         switch ( $_GET['widget'] ) {
316                 case 'dashboard_primary' :
317                         wp_dashboard_primary();
318                         break;
319         }
320         wp_die();
321 }
322
323 /**
324  * Ajax handler for Customizer preview logged-in status.
325  *
326  * @since 3.4.0
327  */
328 function wp_ajax_logged_in() {
329         wp_die( 1 );
330 }
331
332 //
333 // Ajax helpers.
334 //
335
336 /**
337  * Sends back current comment total and new page links if they need to be updated.
338  *
339  * Contrary to normal success AJAX response ("1"), die with time() on success.
340  *
341  * @access private
342  * @since 2.7.0
343  *
344  * @param int $comment_id
345  * @param int $delta
346  */
347 function _wp_ajax_delete_comment_response( $comment_id, $delta = -1 ) {
348         $total    = isset( $_POST['_total'] )    ? (int) $_POST['_total']    : 0;
349         $per_page = isset( $_POST['_per_page'] ) ? (int) $_POST['_per_page'] : 0;
350         $page     = isset( $_POST['_page'] )     ? (int) $_POST['_page']     : 0;
351         $url      = isset( $_POST['_url'] )      ? esc_url_raw( $_POST['_url'] ) : '';
352
353         // JS didn't send us everything we need to know. Just die with success message
354         if ( ! $total || ! $per_page || ! $page || ! $url ) {
355                 $time = time();
356                 $comment = get_comment( $comment_id );
357
358                 $counts = wp_count_comments();
359
360                 $x = new WP_Ajax_Response( array(
361                         'what' => 'comment',
362                         // Here for completeness - not used.
363                         'id' => $comment_id,
364                         'supplemental' => array(
365                                 'status' => $comment ? $comment->comment_approved : '',
366                                 'postId' => $comment ? $comment->comment_post_ID : '',
367                                 'time' => $time,
368                                 'in_moderation' => $counts->moderated,
369                                 'i18n_comments_text' => sprintf(
370                                         _n( '%s Comment', '%s Comments', $counts->approved ),
371                                         number_format_i18n( $counts->approved )
372                                 ),
373                                 'i18n_moderation_text' => sprintf(
374                                         _nx( '%s in moderation', '%s in moderation', $counts->moderated, 'comments' ),
375                                         number_format_i18n( $counts->moderated )
376                                 )
377                         )
378                 ) );
379                 $x->send();
380         }
381
382         $total += $delta;
383         if ( $total < 0 )
384                 $total = 0;
385
386         // Only do the expensive stuff on a page-break, and about 1 other time per page
387         if ( 0 == $total % $per_page || 1 == mt_rand( 1, $per_page ) ) {
388                 $post_id = 0;
389                 // What type of comment count are we looking for?
390                 $status = 'all';
391                 $parsed = parse_url( $url );
392                 if ( isset( $parsed['query'] ) ) {
393                         parse_str( $parsed['query'], $query_vars );
394                         if ( !empty( $query_vars['comment_status'] ) )
395                                 $status = $query_vars['comment_status'];
396                         if ( !empty( $query_vars['p'] ) )
397                                 $post_id = (int) $query_vars['p'];
398                 }
399
400                 $comment_count = wp_count_comments($post_id);
401
402                 // We're looking for a known type of comment count.
403                 if ( isset( $comment_count->$status ) )
404                         $total = $comment_count->$status;
405                         // Else use the decremented value from above.
406         }
407
408         // The time since the last comment count.
409         $time = time();
410         $comment = get_comment( $comment_id );
411
412         $x = new WP_Ajax_Response( array(
413                 'what' => 'comment',
414                 // Here for completeness - not used.
415                 'id' => $comment_id,
416                 'supplemental' => array(
417                         'status' => $comment ? $comment->comment_approved : '',
418                         'postId' => $comment ? $comment->comment_post_ID : '',
419                         'total_items_i18n' => sprintf( _n( '%s item', '%s items', $total ), number_format_i18n( $total ) ),
420                         'total_pages' => ceil( $total / $per_page ),
421                         'total_pages_i18n' => number_format_i18n( ceil( $total / $per_page ) ),
422                         'total' => $total,
423                         'time' => $time
424                 )
425         ) );
426         $x->send();
427 }
428
429 //
430 // POST-based Ajax handlers.
431 //
432
433 /**
434  * Ajax handler for adding a hierarchical term.
435  *
436  * @access private
437  * @since 3.1.0
438  */
439 function _wp_ajax_add_hierarchical_term() {
440         $action = $_POST['action'];
441         $taxonomy = get_taxonomy(substr($action, 4));
442         check_ajax_referer( $action, '_ajax_nonce-add-' . $taxonomy->name );
443         if ( !current_user_can( $taxonomy->cap->edit_terms ) )
444                 wp_die( -1 );
445         $names = explode(',', $_POST['new'.$taxonomy->name]);
446         $parent = isset($_POST['new'.$taxonomy->name.'_parent']) ? (int) $_POST['new'.$taxonomy->name.'_parent'] : 0;
447         if ( 0 > $parent )
448                 $parent = 0;
449         if ( $taxonomy->name == 'category' )
450                 $post_category = isset($_POST['post_category']) ? (array) $_POST['post_category'] : array();
451         else
452                 $post_category = ( isset($_POST['tax_input']) && isset($_POST['tax_input'][$taxonomy->name]) ) ? (array) $_POST['tax_input'][$taxonomy->name] : array();
453         $checked_categories = array_map( 'absint', (array) $post_category );
454         $popular_ids = wp_popular_terms_checklist($taxonomy->name, 0, 10, false);
455
456         foreach ( $names as $cat_name ) {
457                 $cat_name = trim($cat_name);
458                 $category_nicename = sanitize_title($cat_name);
459                 if ( '' === $category_nicename )
460                         continue;
461                 if ( !$cat_id = term_exists( $cat_name, $taxonomy->name, $parent ) )
462                         $cat_id = wp_insert_term( $cat_name, $taxonomy->name, array( 'parent' => $parent ) );
463                 if ( is_wp_error( $cat_id ) ) {
464                         continue;
465                 } elseif ( is_array( $cat_id ) ) {
466                         $cat_id = $cat_id['term_id'];
467                 }
468                 $checked_categories[] = $cat_id;
469                 if ( $parent ) // Do these all at once in a second
470                         continue;
471
472                 ob_start();
473
474                 wp_terms_checklist( 0, array( 'taxonomy' => $taxonomy->name, 'descendants_and_self' => $cat_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids ));
475
476                 $data = ob_get_clean();
477
478                 $add = array(
479                         'what' => $taxonomy->name,
480                         'id' => $cat_id,
481                         'data' => str_replace( array("\n", "\t"), '', $data),
482                         'position' => -1
483                 );
484         }
485
486         if ( $parent ) { // Foncy - replace the parent and all its children
487                 $parent = get_term( $parent, $taxonomy->name );
488                 $term_id = $parent->term_id;
489
490                 while ( $parent->parent ) { // get the top parent
491                         $parent = get_term( $parent->parent, $taxonomy->name );
492                         if ( is_wp_error( $parent ) )
493                                 break;
494                         $term_id = $parent->term_id;
495                 }
496
497                 ob_start();
498
499                 wp_terms_checklist( 0, array('taxonomy' => $taxonomy->name, 'descendants_and_self' => $term_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids));
500
501                 $data = ob_get_clean();
502
503                 $add = array(
504                         'what' => $taxonomy->name,
505                         'id' => $term_id,
506                         'data' => str_replace( array("\n", "\t"), '', $data),
507                         'position' => -1
508                 );
509         }
510
511         ob_start();
512
513         wp_dropdown_categories( array(
514                 'taxonomy' => $taxonomy->name, 'hide_empty' => 0, 'name' => 'new'.$taxonomy->name.'_parent', 'orderby' => 'name',
515                 'hierarchical' => 1, 'show_option_none' => '&mdash; '.$taxonomy->labels->parent_item.' &mdash;'
516         ) );
517
518         $sup = ob_get_clean();
519
520         $add['supplemental'] = array( 'newcat_parent' => $sup );
521
522         $x = new WP_Ajax_Response( $add );
523         $x->send();
524 }
525
526 /**
527  * Ajax handler for deleting a comment.
528  *
529  * @since 3.1.0
530  */
531 function wp_ajax_delete_comment() {
532         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
533
534         if ( !$comment = get_comment( $id ) )
535                 wp_die( time() );
536         if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
537                 wp_die( -1 );
538
539         check_ajax_referer( "delete-comment_$id" );
540         $status = wp_get_comment_status( $comment );
541
542         $delta = -1;
543         if ( isset($_POST['trash']) && 1 == $_POST['trash'] ) {
544                 if ( 'trash' == $status )
545                         wp_die( time() );
546                 $r = wp_trash_comment( $comment );
547         } elseif ( isset($_POST['untrash']) && 1 == $_POST['untrash'] ) {
548                 if ( 'trash' != $status )
549                         wp_die( time() );
550                 $r = wp_untrash_comment( $comment );
551                 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'trash' ) // undo trash, not in trash
552                         $delta = 1;
553         } elseif ( isset($_POST['spam']) && 1 == $_POST['spam'] ) {
554                 if ( 'spam' == $status )
555                         wp_die( time() );
556                 $r = wp_spam_comment( $comment );
557         } elseif ( isset($_POST['unspam']) && 1 == $_POST['unspam'] ) {
558                 if ( 'spam' != $status )
559                         wp_die( time() );
560                 $r = wp_unspam_comment( $comment );
561                 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'spam' ) // undo spam, not in spam
562                         $delta = 1;
563         } elseif ( isset($_POST['delete']) && 1 == $_POST['delete'] ) {
564                 $r = wp_delete_comment( $comment );
565         } else {
566                 wp_die( -1 );
567         }
568
569         if ( $r ) // Decide if we need to send back '1' or a more complicated response including page links and comment counts
570                 _wp_ajax_delete_comment_response( $comment->comment_ID, $delta );
571         wp_die( 0 );
572 }
573
574 /**
575  * Ajax handler for deleting a tag.
576  *
577  * @since 3.1.0
578  */
579 function wp_ajax_delete_tag() {
580         $tag_id = (int) $_POST['tag_ID'];
581         check_ajax_referer( "delete-tag_$tag_id" );
582
583         $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
584         $tax = get_taxonomy($taxonomy);
585
586         if ( !current_user_can( $tax->cap->delete_terms ) )
587                 wp_die( -1 );
588
589         $tag = get_term( $tag_id, $taxonomy );
590         if ( !$tag || is_wp_error( $tag ) )
591                 wp_die( 1 );
592
593         if ( wp_delete_term($tag_id, $taxonomy))
594                 wp_die( 1 );
595         else
596                 wp_die( 0 );
597 }
598
599 /**
600  * Ajax handler for deleting a link.
601  *
602  * @since 3.1.0
603  */
604 function wp_ajax_delete_link() {
605         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
606
607         check_ajax_referer( "delete-bookmark_$id" );
608         if ( !current_user_can( 'manage_links' ) )
609                 wp_die( -1 );
610
611         $link = get_bookmark( $id );
612         if ( !$link || is_wp_error( $link ) )
613                 wp_die( 1 );
614
615         if ( wp_delete_link( $id ) )
616                 wp_die( 1 );
617         else
618                 wp_die( 0 );
619 }
620
621 /**
622  * Ajax handler for deleting meta.
623  *
624  * @since 3.1.0
625  */
626 function wp_ajax_delete_meta() {
627         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
628
629         check_ajax_referer( "delete-meta_$id" );
630         if ( !$meta = get_metadata_by_mid( 'post', $id ) )
631                 wp_die( 1 );
632
633         if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta',  $meta->post_id, $meta->meta_key ) )
634                 wp_die( -1 );
635         if ( delete_meta( $meta->meta_id ) )
636                 wp_die( 1 );
637         wp_die( 0 );
638 }
639
640 /**
641  * Ajax handler for deleting a post.
642  *
643  * @since 3.1.0
644  *
645  * @param string $action Action to perform.
646  */
647 function wp_ajax_delete_post( $action ) {
648         if ( empty( $action ) )
649                 $action = 'delete-post';
650         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
651
652         check_ajax_referer( "{$action}_$id" );
653         if ( !current_user_can( 'delete_post', $id ) )
654                 wp_die( -1 );
655
656         if ( !get_post( $id ) )
657                 wp_die( 1 );
658
659         if ( wp_delete_post( $id ) )
660                 wp_die( 1 );
661         else
662                 wp_die( 0 );
663 }
664
665 /**
666  * Ajax handler for sending a post to the trash.
667  *
668  * @since 3.1.0
669  *
670  * @param string $action Action to perform.
671  */
672 function wp_ajax_trash_post( $action ) {
673         if ( empty( $action ) )
674                 $action = 'trash-post';
675         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
676
677         check_ajax_referer( "{$action}_$id" );
678         if ( !current_user_can( 'delete_post', $id ) )
679                 wp_die( -1 );
680
681         if ( !get_post( $id ) )
682                 wp_die( 1 );
683
684         if ( 'trash-post' == $action )
685                 $done = wp_trash_post( $id );
686         else
687                 $done = wp_untrash_post( $id );
688
689         if ( $done )
690                 wp_die( 1 );
691
692         wp_die( 0 );
693 }
694
695 /**
696  * Ajax handler to restore a post from the trash.
697  *
698  * @since 3.1.0
699  *
700  * @param string $action Action to perform.
701  */
702 function wp_ajax_untrash_post( $action ) {
703         if ( empty( $action ) )
704                 $action = 'untrash-post';
705         wp_ajax_trash_post( $action );
706 }
707
708 /**
709  * @since 3.1.0
710  *
711  * @param string $action
712  */
713 function wp_ajax_delete_page( $action ) {
714         if ( empty( $action ) )
715                 $action = 'delete-page';
716         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
717
718         check_ajax_referer( "{$action}_$id" );
719         if ( !current_user_can( 'delete_page', $id ) )
720                 wp_die( -1 );
721
722         if ( ! get_post( $id ) )
723                 wp_die( 1 );
724
725         if ( wp_delete_post( $id ) )
726                 wp_die( 1 );
727         else
728                 wp_die( 0 );
729 }
730
731 /**
732  * Ajax handler to dim a comment.
733  *
734  * @since 3.1.0
735  */
736 function wp_ajax_dim_comment() {
737         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
738
739         if ( !$comment = get_comment( $id ) ) {
740                 $x = new WP_Ajax_Response( array(
741                         'what' => 'comment',
742                         'id' => new WP_Error('invalid_comment', sprintf(__('Comment %d does not exist'), $id))
743                 ) );
744                 $x->send();
745         }
746
747         if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) && ! current_user_can( 'moderate_comments' ) )
748                 wp_die( -1 );
749
750         $current = wp_get_comment_status( $comment );
751         if ( isset( $_POST['new'] ) && $_POST['new'] == $current )
752                 wp_die( time() );
753
754         check_ajax_referer( "approve-comment_$id" );
755         if ( in_array( $current, array( 'unapproved', 'spam' ) ) ) {
756                 $result = wp_set_comment_status( $comment, 'approve', true );
757         } else {
758                 $result = wp_set_comment_status( $comment, 'hold', true );
759         }
760
761         if ( is_wp_error($result) ) {
762                 $x = new WP_Ajax_Response( array(
763                         'what' => 'comment',
764                         'id' => $result
765                 ) );
766                 $x->send();
767         }
768
769         // Decide if we need to send back '1' or a more complicated response including page links and comment counts
770         _wp_ajax_delete_comment_response( $comment->comment_ID );
771         wp_die( 0 );
772 }
773
774 /**
775  * Ajax handler for deleting a link category.
776  *
777  * @since 3.1.0
778  *
779  * @param string $action Action to perform.
780  */
781 function wp_ajax_add_link_category( $action ) {
782         if ( empty( $action ) )
783                 $action = 'add-link-category';
784         check_ajax_referer( $action );
785         if ( !current_user_can( 'manage_categories' ) )
786                 wp_die( -1 );
787         $names = explode(',', wp_unslash( $_POST['newcat'] ) );
788         $x = new WP_Ajax_Response();
789         foreach ( $names as $cat_name ) {
790                 $cat_name = trim($cat_name);
791                 $slug = sanitize_title($cat_name);
792                 if ( '' === $slug )
793                         continue;
794                 if ( !$cat_id = term_exists( $cat_name, 'link_category' ) )
795                         $cat_id = wp_insert_term( $cat_name, 'link_category' );
796                 if ( is_wp_error( $cat_id ) ) {
797                         continue;
798                 } elseif ( is_array( $cat_id ) ) {
799                         $cat_id = $cat_id['term_id'];
800                 }
801                 $cat_name = esc_html( $cat_name );
802                 $x->add( array(
803                         'what' => 'link-category',
804                         'id' => $cat_id,
805                         'data' => "<li id='link-category-$cat_id'><label for='in-link-category-$cat_id' class='selectit'><input value='" . esc_attr($cat_id) . "' type='checkbox' checked='checked' name='link_category[]' id='in-link-category-$cat_id'/> $cat_name</label></li>",
806                         'position' => -1
807                 ) );
808         }
809         $x->send();
810 }
811
812 /**
813  * Ajax handler to add a tag.
814  *
815  * @since 3.1.0
816  *
817  * @global WP_List_Table $wp_list_table
818  */
819 function wp_ajax_add_tag() {
820         global $wp_list_table;
821
822         check_ajax_referer( 'add-tag', '_wpnonce_add-tag' );
823         $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
824         $tax = get_taxonomy($taxonomy);
825
826         if ( !current_user_can( $tax->cap->edit_terms ) )
827                 wp_die( -1 );
828
829         $x = new WP_Ajax_Response();
830
831         $tag = wp_insert_term($_POST['tag-name'], $taxonomy, $_POST );
832
833         if ( !$tag || is_wp_error($tag) || (!$tag = get_term( $tag['term_id'], $taxonomy )) ) {
834                 $message = __('An error has occurred. Please reload the page and try again.');
835                 if ( is_wp_error($tag) && $tag->get_error_message() )
836                         $message = $tag->get_error_message();
837
838                 $x->add( array(
839                         'what' => 'taxonomy',
840                         'data' => new WP_Error('error', $message )
841                 ) );
842                 $x->send();
843         }
844
845         $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => $_POST['screen'] ) );
846
847         $level = 0;
848         if ( is_taxonomy_hierarchical($taxonomy) ) {
849                 $level = count( get_ancestors( $tag->term_id, $taxonomy, 'taxonomy' ) );
850                 ob_start();
851                 $wp_list_table->single_row( $tag, $level );
852                 $noparents = ob_get_clean();
853         }
854
855         ob_start();
856         $wp_list_table->single_row( $tag );
857         $parents = ob_get_clean();
858
859         $x->add( array(
860                 'what' => 'taxonomy',
861                 'supplemental' => compact('parents', 'noparents')
862         ) );
863         $x->add( array(
864                 'what' => 'term',
865                 'position' => $level,
866                 'supplemental' => (array) $tag
867         ) );
868         $x->send();
869 }
870
871 /**
872  * Ajax handler for getting a tagcloud.
873  *
874  * @since 3.1.0
875  */
876 function wp_ajax_get_tagcloud() {
877         if ( ! isset( $_POST['tax'] ) ) {
878                 wp_die( 0 );
879         }
880
881         $taxonomy = sanitize_key( $_POST['tax'] );
882         $tax = get_taxonomy( $taxonomy );
883         if ( ! $tax ) {
884                 wp_die( 0 );
885         }
886
887         if ( ! current_user_can( $tax->cap->assign_terms ) ) {
888                 wp_die( -1 );
889         }
890
891         $tags = get_terms( $taxonomy, array( 'number' => 45, 'orderby' => 'count', 'order' => 'DESC' ) );
892
893         if ( empty( $tags ) )
894                 wp_die( $tax->labels->not_found );
895
896         if ( is_wp_error( $tags ) )
897                 wp_die( $tags->get_error_message() );
898
899         foreach ( $tags as $key => $tag ) {
900                 $tags[ $key ]->link = '#';
901                 $tags[ $key ]->id = $tag->term_id;
902         }
903
904         // We need raw tag names here, so don't filter the output
905         $return = wp_generate_tag_cloud( $tags, array('filter' => 0) );
906
907         if ( empty($return) )
908                 wp_die( 0 );
909
910         echo $return;
911
912         wp_die();
913 }
914
915 /**
916  * Ajax handler for getting comments.
917  *
918  * @since 3.1.0
919  *
920  * @global WP_List_Table $wp_list_table
921  * @global int           $post_id
922  *
923  * @param string $action Action to perform.
924  */
925 function wp_ajax_get_comments( $action ) {
926         global $wp_list_table, $post_id;
927         if ( empty( $action ) )
928                 $action = 'get-comments';
929
930         check_ajax_referer( $action );
931
932         if ( empty( $post_id ) && ! empty( $_REQUEST['p'] ) ) {
933                 $id = absint( $_REQUEST['p'] );
934                 if ( ! empty( $id ) )
935                         $post_id = $id;
936         }
937
938         if ( empty( $post_id ) )
939                 wp_die( -1 );
940
941         $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
942
943         if ( ! current_user_can( 'edit_post', $post_id ) )
944                 wp_die( -1 );
945
946         $wp_list_table->prepare_items();
947
948         if ( !$wp_list_table->has_items() )
949                 wp_die( 1 );
950
951         $x = new WP_Ajax_Response();
952         ob_start();
953         foreach ( $wp_list_table->items as $comment ) {
954                 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
955                         continue;
956                 get_comment( $comment );
957                 $wp_list_table->single_row( $comment );
958         }
959         $comment_list_item = ob_get_clean();
960
961         $x->add( array(
962                 'what' => 'comments',
963                 'data' => $comment_list_item
964         ) );
965         $x->send();
966 }
967
968 /**
969  * Ajax handler for replying to a comment.
970  *
971  * @since 3.1.0
972  *
973  * @global WP_List_Table $wp_list_table
974  *
975  * @param string $action Action to perform.
976  */
977 function wp_ajax_replyto_comment( $action ) {
978         global $wp_list_table;
979         if ( empty( $action ) )
980                 $action = 'replyto-comment';
981
982         check_ajax_referer( $action, '_ajax_nonce-replyto-comment' );
983
984         $comment_post_ID = (int) $_POST['comment_post_ID'];
985         $post = get_post( $comment_post_ID );
986         if ( ! $post )
987                 wp_die( -1 );
988
989         if ( !current_user_can( 'edit_post', $comment_post_ID ) )
990                 wp_die( -1 );
991
992         if ( empty( $post->post_status ) )
993                 wp_die( 1 );
994         elseif ( in_array($post->post_status, array('draft', 'pending', 'trash') ) )
995                 wp_die( __('ERROR: you are replying to a comment on a draft post.') );
996
997         $user = wp_get_current_user();
998         if ( $user->exists() ) {
999                 $user_ID = $user->ID;
1000                 $comment_author       = wp_slash( $user->display_name );
1001                 $comment_author_email = wp_slash( $user->user_email );
1002                 $comment_author_url   = wp_slash( $user->user_url );
1003                 $comment_content      = trim( $_POST['content'] );
1004                 $comment_type         = isset( $_POST['comment_type'] ) ? trim( $_POST['comment_type'] ) : '';
1005                 if ( current_user_can( 'unfiltered_html' ) ) {
1006                         if ( ! isset( $_POST['_wp_unfiltered_html_comment'] ) )
1007                                 $_POST['_wp_unfiltered_html_comment'] = '';
1008
1009                         if ( wp_create_nonce( 'unfiltered-html-comment' ) != $_POST['_wp_unfiltered_html_comment'] ) {
1010                                 kses_remove_filters(); // start with a clean slate
1011                                 kses_init_filters(); // set up the filters
1012                         }
1013                 }
1014         } else {
1015                 wp_die( __( 'Sorry, you must be logged in to reply to a comment.' ) );
1016         }
1017
1018         if ( '' == $comment_content )
1019                 wp_die( __( 'ERROR: please type a comment.' ) );
1020
1021         $comment_parent = 0;
1022         if ( isset( $_POST['comment_ID'] ) )
1023                 $comment_parent = absint( $_POST['comment_ID'] );
1024         $comment_auto_approved = false;
1025         $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID');
1026
1027         // Automatically approve parent comment.
1028         if ( !empty($_POST['approve_parent']) ) {
1029                 $parent = get_comment( $comment_parent );
1030
1031                 if ( $parent && $parent->comment_approved === '0' && $parent->comment_post_ID == $comment_post_ID ) {
1032                         if ( ! current_user_can( 'edit_comment', $parent->comment_ID ) ) {
1033                                 wp_die( -1 );
1034                         }
1035
1036                         if ( wp_set_comment_status( $parent, 'approve' ) )
1037                                 $comment_auto_approved = true;
1038                 }
1039         }
1040
1041         $comment_id = wp_new_comment( $commentdata );
1042         $comment = get_comment($comment_id);
1043         if ( ! $comment ) wp_die( 1 );
1044
1045         $position = ( isset($_POST['position']) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1';
1046
1047         ob_start();
1048         if ( isset( $_REQUEST['mode'] ) && 'dashboard' == $_REQUEST['mode'] ) {
1049                 require_once( ABSPATH . 'wp-admin/includes/dashboard.php' );
1050                 _wp_dashboard_recent_comments_row( $comment );
1051         } else {
1052                 if ( isset( $_REQUEST['mode'] ) && 'single' == $_REQUEST['mode'] ) {
1053                         $wp_list_table = _get_list_table('WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
1054                 } else {
1055                         $wp_list_table = _get_list_table('WP_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
1056                 }
1057                 $wp_list_table->single_row( $comment );
1058         }
1059         $comment_list_item = ob_get_clean();
1060
1061         $response =  array(
1062                 'what' => 'comment',
1063                 'id' => $comment->comment_ID,
1064                 'data' => $comment_list_item,
1065                 'position' => $position
1066         );
1067
1068         $counts = wp_count_comments();
1069         $response['supplemental'] = array(
1070                 'in_moderation' => $counts->moderated,
1071                 'i18n_comments_text' => sprintf(
1072                         _n( '%s Comment', '%s Comments', $counts->approved ),
1073                         number_format_i18n( $counts->approved )
1074                 ),
1075                 'i18n_moderation_text' => sprintf(
1076                         _nx( '%s in moderation', '%s in moderation', $counts->moderated, 'comments' ),
1077                         number_format_i18n( $counts->moderated )
1078                 )
1079         );
1080
1081         if ( $comment_auto_approved ) {
1082                 $response['supplemental']['parent_approved'] = $parent->comment_ID;
1083                 $response['supplemental']['parent_post_id'] = $parent->comment_post_ID;
1084         }
1085
1086         $x = new WP_Ajax_Response();
1087         $x->add( $response );
1088         $x->send();
1089 }
1090
1091 /**
1092  * Ajax handler for editing a comment.
1093  *
1094  * @since 3.1.0
1095  *
1096  * @global WP_List_Table $wp_list_table
1097  */
1098 function wp_ajax_edit_comment() {
1099         global $wp_list_table;
1100
1101         check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' );
1102
1103         $comment_id = (int) $_POST['comment_ID'];
1104         if ( ! current_user_can( 'edit_comment', $comment_id ) )
1105                 wp_die( -1 );
1106
1107         if ( '' == $_POST['content'] )
1108                 wp_die( __( 'ERROR: please type a comment.' ) );
1109
1110         if ( isset( $_POST['status'] ) )
1111                 $_POST['comment_status'] = $_POST['status'];
1112         edit_comment();
1113
1114         $position = ( isset($_POST['position']) && (int) $_POST['position']) ? (int) $_POST['position'] : '-1';
1115         $checkbox = ( isset($_POST['checkbox']) && true == $_POST['checkbox'] ) ? 1 : 0;
1116         $wp_list_table = _get_list_table( $checkbox ? 'WP_Comments_List_Table' : 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
1117
1118         $comment = get_comment( $comment_id );
1119         if ( empty( $comment->comment_ID ) )
1120                 wp_die( -1 );
1121
1122         ob_start();
1123         $wp_list_table->single_row( $comment );
1124         $comment_list_item = ob_get_clean();
1125
1126         $x = new WP_Ajax_Response();
1127
1128         $x->add( array(
1129                 'what' => 'edit_comment',
1130                 'id' => $comment->comment_ID,
1131                 'data' => $comment_list_item,
1132                 'position' => $position
1133         ));
1134
1135         $x->send();
1136 }
1137
1138 /**
1139  * Ajax handler for adding a menu item.
1140  *
1141  * @since 3.1.0
1142  */
1143 function wp_ajax_add_menu_item() {
1144         check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1145
1146         if ( ! current_user_can( 'edit_theme_options' ) )
1147                 wp_die( -1 );
1148
1149         require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1150
1151         // For performance reasons, we omit some object properties from the checklist.
1152         // The following is a hacky way to restore them when adding non-custom items.
1153
1154         $menu_items_data = array();
1155         foreach ( (array) $_POST['menu-item'] as $menu_item_data ) {
1156                 if (
1157                         ! empty( $menu_item_data['menu-item-type'] ) &&
1158                         'custom' != $menu_item_data['menu-item-type'] &&
1159                         ! empty( $menu_item_data['menu-item-object-id'] )
1160                 ) {
1161                         switch( $menu_item_data['menu-item-type'] ) {
1162                                 case 'post_type' :
1163                                         $_object = get_post( $menu_item_data['menu-item-object-id'] );
1164                                 break;
1165
1166                                 case 'post_type_archive' :
1167                                         $_object = get_post_type_object( $menu_item_data['menu-item-object'] );
1168                                 break;
1169
1170                                 case 'taxonomy' :
1171                                         $_object = get_term( $menu_item_data['menu-item-object-id'], $menu_item_data['menu-item-object'] );
1172                                 break;
1173                         }
1174
1175                         $_menu_items = array_map( 'wp_setup_nav_menu_item', array( $_object ) );
1176                         $_menu_item = reset( $_menu_items );
1177
1178                         // Restore the missing menu item properties
1179                         $menu_item_data['menu-item-description'] = $_menu_item->description;
1180                 }
1181
1182                 $menu_items_data[] = $menu_item_data;
1183         }
1184
1185         $item_ids = wp_save_nav_menu_items( 0, $menu_items_data );
1186         if ( is_wp_error( $item_ids ) )
1187                 wp_die( 0 );
1188
1189         $menu_items = array();
1190
1191         foreach ( (array) $item_ids as $menu_item_id ) {
1192                 $menu_obj = get_post( $menu_item_id );
1193                 if ( ! empty( $menu_obj->ID ) ) {
1194                         $menu_obj = wp_setup_nav_menu_item( $menu_obj );
1195                         $menu_obj->label = $menu_obj->title; // don't show "(pending)" in ajax-added items
1196                         $menu_items[] = $menu_obj;
1197                 }
1198         }
1199
1200         /** This filter is documented in wp-admin/includes/nav-menu.php */
1201         $walker_class_name = apply_filters( 'wp_edit_nav_menu_walker', 'Walker_Nav_Menu_Edit', $_POST['menu'] );
1202
1203         if ( ! class_exists( $walker_class_name ) )
1204                 wp_die( 0 );
1205
1206         if ( ! empty( $menu_items ) ) {
1207                 $args = array(
1208                         'after' => '',
1209                         'before' => '',
1210                         'link_after' => '',
1211                         'link_before' => '',
1212                         'walker' => new $walker_class_name,
1213                 );
1214                 echo walk_nav_menu_tree( $menu_items, 0, (object) $args );
1215         }
1216         wp_die();
1217 }
1218
1219 /**
1220  * Ajax handler for adding meta.
1221  *
1222  * @since 3.1.0
1223  */
1224 function wp_ajax_add_meta() {
1225         check_ajax_referer( 'add-meta', '_ajax_nonce-add-meta' );
1226         $c = 0;
1227         $pid = (int) $_POST['post_id'];
1228         $post = get_post( $pid );
1229
1230         if ( isset($_POST['metakeyselect']) || isset($_POST['metakeyinput']) ) {
1231                 if ( !current_user_can( 'edit_post', $pid ) )
1232                         wp_die( -1 );
1233                 if ( isset($_POST['metakeyselect']) && '#NONE#' == $_POST['metakeyselect'] && empty($_POST['metakeyinput']) )
1234                         wp_die( 1 );
1235
1236                 // If the post is an autodraft, save the post as a draft and then attempt to save the meta.
1237                 if ( $post->post_status == 'auto-draft' ) {
1238                         $post_data = array();
1239                         $post_data['action'] = 'draft'; // Warning fix
1240                         $post_data['post_ID'] = $pid;
1241                         $post_data['post_type'] = $post->post_type;
1242                         $post_data['post_status'] = 'draft';
1243                         $now = current_time('timestamp', 1);
1244                         $post_data['post_title'] = sprintf( __( 'Draft created on %1$s at %2$s' ), date( __( 'F j, Y' ), $now ), date( __( 'g:i a' ), $now ) );
1245
1246                         $pid = edit_post( $post_data );
1247                         if ( $pid ) {
1248                                 if ( is_wp_error( $pid ) ) {
1249                                         $x = new WP_Ajax_Response( array(
1250                                                 'what' => 'meta',
1251                                                 'data' => $pid
1252                                         ) );
1253                                         $x->send();
1254                                 }
1255
1256                                 if ( !$mid = add_meta( $pid ) )
1257                                         wp_die( __( 'Please provide a custom field value.' ) );
1258                         } else {
1259                                 wp_die( 0 );
1260                         }
1261                 } elseif ( ! $mid = add_meta( $pid ) ) {
1262                         wp_die( __( 'Please provide a custom field value.' ) );
1263                 }
1264
1265                 $meta = get_metadata_by_mid( 'post', $mid );
1266                 $pid = (int) $meta->post_id;
1267                 $meta = get_object_vars( $meta );
1268                 $x = new WP_Ajax_Response( array(
1269                         'what' => 'meta',
1270                         'id' => $mid,
1271                         'data' => _list_meta_row( $meta, $c ),
1272                         'position' => 1,
1273                         'supplemental' => array('postid' => $pid)
1274                 ) );
1275         } else { // Update?
1276                 $mid = (int) key( $_POST['meta'] );
1277                 $key = wp_unslash( $_POST['meta'][$mid]['key'] );
1278                 $value = wp_unslash( $_POST['meta'][$mid]['value'] );
1279                 if ( '' == trim($key) )
1280                         wp_die( __( 'Please provide a custom field name.' ) );
1281                 if ( '' == trim($value) )
1282                         wp_die( __( 'Please provide a custom field value.' ) );
1283                 if ( ! $meta = get_metadata_by_mid( 'post', $mid ) )
1284                         wp_die( 0 ); // if meta doesn't exist
1285                 if ( is_protected_meta( $meta->meta_key, 'post' ) || is_protected_meta( $key, 'post' ) ||
1286                         ! current_user_can( 'edit_post_meta', $meta->post_id, $meta->meta_key ) ||
1287                         ! current_user_can( 'edit_post_meta', $meta->post_id, $key ) )
1288                         wp_die( -1 );
1289                 if ( $meta->meta_value != $value || $meta->meta_key != $key ) {
1290                         if ( !$u = update_metadata_by_mid( 'post', $mid, $value, $key ) )
1291                                 wp_die( 0 ); // We know meta exists; we also know it's unchanged (or DB error, in which case there are bigger problems).
1292                 }
1293
1294                 $x = new WP_Ajax_Response( array(
1295                         'what' => 'meta',
1296                         'id' => $mid, 'old_id' => $mid,
1297                         'data' => _list_meta_row( array(
1298                                 'meta_key' => $key,
1299                                 'meta_value' => $value,
1300                                 'meta_id' => $mid
1301                         ), $c ),
1302                         'position' => 0,
1303                         'supplemental' => array('postid' => $meta->post_id)
1304                 ) );
1305         }
1306         $x->send();
1307 }
1308
1309 /**
1310  * Ajax handler for adding a user.
1311  *
1312  * @since 3.1.0
1313  *
1314  * @global WP_List_Table $wp_list_table
1315  *
1316  * @param string $action Action to perform.
1317  */
1318 function wp_ajax_add_user( $action ) {
1319         global $wp_list_table;
1320         if ( empty( $action ) )
1321                 $action = 'add-user';
1322
1323         check_ajax_referer( $action );
1324         if ( ! current_user_can('create_users') )
1325                 wp_die( -1 );
1326         if ( ! $user_id = edit_user() ) {
1327                 wp_die( 0 );
1328         } elseif ( is_wp_error( $user_id ) ) {
1329                 $x = new WP_Ajax_Response( array(
1330                         'what' => 'user',
1331                         'id' => $user_id
1332                 ) );
1333                 $x->send();
1334         }
1335         $user_object = get_userdata( $user_id );
1336
1337         $wp_list_table = _get_list_table('WP_Users_List_Table');
1338
1339         $role = current( $user_object->roles );
1340
1341         $x = new WP_Ajax_Response( array(
1342                 'what' => 'user',
1343                 'id' => $user_id,
1344                 'data' => $wp_list_table->single_row( $user_object, '', $role ),
1345                 'supplemental' => array(
1346                         'show-link' => sprintf(
1347                                 /* translators: %s: the new user */
1348                                 __( 'User %s added' ),
1349                                 '<a href="#user-' . $user_id . '">' . $user_object->user_login . '</a>'
1350                         ),
1351                         'role' => $role,
1352                 )
1353         ) );
1354         $x->send();
1355 }
1356
1357 /**
1358  * Ajax handler for closed post boxes.
1359  *
1360  * @since 3.1.0
1361  */
1362 function wp_ajax_closed_postboxes() {
1363         check_ajax_referer( 'closedpostboxes', 'closedpostboxesnonce' );
1364         $closed = isset( $_POST['closed'] ) ? explode( ',', $_POST['closed']) : array();
1365         $closed = array_filter($closed);
1366
1367         $hidden = isset( $_POST['hidden'] ) ? explode( ',', $_POST['hidden']) : array();
1368         $hidden = array_filter($hidden);
1369
1370         $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1371
1372         if ( $page != sanitize_key( $page ) )
1373                 wp_die( 0 );
1374
1375         if ( ! $user = wp_get_current_user() )
1376                 wp_die( -1 );
1377
1378         if ( is_array($closed) )
1379                 update_user_option($user->ID, "closedpostboxes_$page", $closed, true);
1380
1381         if ( is_array($hidden) ) {
1382                 $hidden = array_diff( $hidden, array('submitdiv', 'linksubmitdiv', 'manage-menu', 'create-menu') ); // postboxes that are always shown
1383                 update_user_option($user->ID, "metaboxhidden_$page", $hidden, true);
1384         }
1385
1386         wp_die( 1 );
1387 }
1388
1389 /**
1390  * Ajax handler for hidden columns.
1391  *
1392  * @since 3.1.0
1393  */
1394 function wp_ajax_hidden_columns() {
1395         check_ajax_referer( 'screen-options-nonce', 'screenoptionnonce' );
1396         $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1397
1398         if ( $page != sanitize_key( $page ) )
1399                 wp_die( 0 );
1400
1401         if ( ! $user = wp_get_current_user() )
1402                 wp_die( -1 );
1403
1404         $hidden = ! empty( $_POST['hidden'] ) ? explode( ',', $_POST['hidden'] ) : array();
1405         update_user_option( $user->ID, "manage{$page}columnshidden", $hidden, true );
1406
1407         wp_die( 1 );
1408 }
1409
1410 /**
1411  * Ajax handler for updating whether to display the welcome panel.
1412  *
1413  * @since 3.1.0
1414  */
1415 function wp_ajax_update_welcome_panel() {
1416         check_ajax_referer( 'welcome-panel-nonce', 'welcomepanelnonce' );
1417
1418         if ( ! current_user_can( 'edit_theme_options' ) )
1419                 wp_die( -1 );
1420
1421         update_user_meta( get_current_user_id(), 'show_welcome_panel', empty( $_POST['visible'] ) ? 0 : 1 );
1422
1423         wp_die( 1 );
1424 }
1425
1426 /**
1427  * Ajax handler for retrieving menu meta boxes.
1428  *
1429  * @since 3.1.0
1430  */
1431 function wp_ajax_menu_get_metabox() {
1432         if ( ! current_user_can( 'edit_theme_options' ) )
1433                 wp_die( -1 );
1434
1435         require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1436
1437         if ( isset( $_POST['item-type'] ) && 'post_type' == $_POST['item-type'] ) {
1438                 $type = 'posttype';
1439                 $callback = 'wp_nav_menu_item_post_type_meta_box';
1440                 $items = (array) get_post_types( array( 'show_in_nav_menus' => true ), 'object' );
1441         } elseif ( isset( $_POST['item-type'] ) && 'taxonomy' == $_POST['item-type'] ) {
1442                 $type = 'taxonomy';
1443                 $callback = 'wp_nav_menu_item_taxonomy_meta_box';
1444                 $items = (array) get_taxonomies( array( 'show_ui' => true ), 'object' );
1445         }
1446
1447         if ( ! empty( $_POST['item-object'] ) && isset( $items[$_POST['item-object']] ) ) {
1448                 $menus_meta_box_object = $items[ $_POST['item-object'] ];
1449
1450                 /** This filter is documented in wp-admin/includes/nav-menu.php */
1451                 $item = apply_filters( 'nav_menu_meta_box_object', $menus_meta_box_object );
1452                 ob_start();
1453                 call_user_func_array($callback, array(
1454                         null,
1455                         array(
1456                                 'id' => 'add-' . $item->name,
1457                                 'title' => $item->labels->name,
1458                                 'callback' => $callback,
1459                                 'args' => $item,
1460                         )
1461                 ));
1462
1463                 $markup = ob_get_clean();
1464
1465                 echo wp_json_encode(array(
1466                         'replace-id' => $type . '-' . $item->name,
1467                         'markup' => $markup,
1468                 ));
1469         }
1470
1471         wp_die();
1472 }
1473
1474 /**
1475  * Ajax handler for internal linking.
1476  *
1477  * @since 3.1.0
1478  */
1479 function wp_ajax_wp_link_ajax() {
1480         check_ajax_referer( 'internal-linking', '_ajax_linking_nonce' );
1481
1482         $args = array();
1483
1484         if ( isset( $_POST['search'] ) ) {
1485                 $args['s'] = wp_unslash( $_POST['search'] );
1486         }
1487
1488         if ( isset( $_POST['term'] ) ) {
1489                 $args['s'] = wp_unslash( $_POST['term'] );
1490         }
1491
1492         $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
1493
1494         require(ABSPATH . WPINC . '/class-wp-editor.php');
1495         $results = _WP_Editors::wp_link_query( $args );
1496
1497         if ( ! isset( $results ) )
1498                 wp_die( 0 );
1499
1500         echo wp_json_encode( $results );
1501         echo "\n";
1502
1503         wp_die();
1504 }
1505
1506 /**
1507  * Ajax handler for menu locations save.
1508  *
1509  * @since 3.1.0
1510  */
1511 function wp_ajax_menu_locations_save() {
1512         if ( ! current_user_can( 'edit_theme_options' ) )
1513                 wp_die( -1 );
1514         check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1515         if ( ! isset( $_POST['menu-locations'] ) )
1516                 wp_die( 0 );
1517         set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_POST['menu-locations'] ) );
1518         wp_die( 1 );
1519 }
1520
1521 /**
1522  * Ajax handler for saving the meta box order.
1523  *
1524  * @since 3.1.0
1525  */
1526 function wp_ajax_meta_box_order() {
1527         check_ajax_referer( 'meta-box-order' );
1528         $order = isset( $_POST['order'] ) ? (array) $_POST['order'] : false;
1529         $page_columns = isset( $_POST['page_columns'] ) ? $_POST['page_columns'] : 'auto';
1530
1531         if ( $page_columns != 'auto' )
1532                 $page_columns = (int) $page_columns;
1533
1534         $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1535
1536         if ( $page != sanitize_key( $page ) )
1537                 wp_die( 0 );
1538
1539         if ( ! $user = wp_get_current_user() )
1540                 wp_die( -1 );
1541
1542         if ( $order )
1543                 update_user_option($user->ID, "meta-box-order_$page", $order, true);
1544
1545         if ( $page_columns )
1546                 update_user_option($user->ID, "screen_layout_$page", $page_columns, true);
1547
1548         wp_die( 1 );
1549 }
1550
1551 /**
1552  * Ajax handler for menu quick searching.
1553  *
1554  * @since 3.1.0
1555  */
1556 function wp_ajax_menu_quick_search() {
1557         if ( ! current_user_can( 'edit_theme_options' ) )
1558                 wp_die( -1 );
1559
1560         require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1561
1562         _wp_ajax_menu_quick_search( $_POST );
1563
1564         wp_die();
1565 }
1566
1567 /**
1568  * Ajax handler to retrieve a permalink.
1569  *
1570  * @since 3.1.0
1571  */
1572 function wp_ajax_get_permalink() {
1573         check_ajax_referer( 'getpermalink', 'getpermalinknonce' );
1574         $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1575         wp_die( get_preview_post_link( $post_id ) );
1576 }
1577
1578 /**
1579  * Ajax handler to retrieve a sample permalink.
1580  *
1581  * @since 3.1.0
1582  */
1583 function wp_ajax_sample_permalink() {
1584         check_ajax_referer( 'samplepermalink', 'samplepermalinknonce' );
1585         $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1586         $title = isset($_POST['new_title'])? $_POST['new_title'] : '';
1587         $slug = isset($_POST['new_slug'])? $_POST['new_slug'] : null;
1588         wp_die( get_sample_permalink_html( $post_id, $title, $slug ) );
1589 }
1590
1591 /**
1592  * Ajax handler for Quick Edit saving a post from a list table.
1593  *
1594  * @since 3.1.0
1595  *
1596  * @global WP_List_Table $wp_list_table
1597  */
1598 function wp_ajax_inline_save() {
1599         global $wp_list_table, $mode;
1600
1601         check_ajax_referer( 'inlineeditnonce', '_inline_edit' );
1602
1603         if ( ! isset($_POST['post_ID']) || ! ( $post_ID = (int) $_POST['post_ID'] ) )
1604                 wp_die();
1605
1606         if ( 'page' == $_POST['post_type'] ) {
1607                 if ( ! current_user_can( 'edit_page', $post_ID ) )
1608                         wp_die( __( 'You are not allowed to edit this page.' ) );
1609         } else {
1610                 if ( ! current_user_can( 'edit_post', $post_ID ) )
1611                         wp_die( __( 'You are not allowed to edit this post.' ) );
1612         }
1613
1614         if ( $last = wp_check_post_lock( $post_ID ) ) {
1615                 $last_user = get_userdata( $last );
1616                 $last_user_name = $last_user ? $last_user->display_name : __( 'Someone' );
1617                 printf( $_POST['post_type'] == 'page' ? __( 'Saving is disabled: %s is currently editing this page.' ) : __( 'Saving is disabled: %s is currently editing this post.' ),        esc_html( $last_user_name ) );
1618                 wp_die();
1619         }
1620
1621         $data = &$_POST;
1622
1623         $post = get_post( $post_ID, ARRAY_A );
1624
1625         // Since it's coming from the database.
1626         $post = wp_slash($post);
1627
1628         $data['content'] = $post['post_content'];
1629         $data['excerpt'] = $post['post_excerpt'];
1630
1631         // Rename.
1632         $data['user_ID'] = get_current_user_id();
1633
1634         if ( isset($data['post_parent']) )
1635                 $data['parent_id'] = $data['post_parent'];
1636
1637         // Status.
1638         if ( isset( $data['keep_private'] ) && 'private' == $data['keep_private'] ) {
1639                 $data['visibility']  = 'private';
1640                 $data['post_status'] = 'private';
1641         } else {
1642                 $data['post_status'] = $data['_status'];
1643         }
1644
1645         if ( empty($data['comment_status']) )
1646                 $data['comment_status'] = 'closed';
1647         if ( empty($data['ping_status']) )
1648                 $data['ping_status'] = 'closed';
1649
1650         // Exclude terms from taxonomies that are not supposed to appear in Quick Edit.
1651         if ( ! empty( $data['tax_input'] ) ) {
1652                 foreach ( $data['tax_input'] as $taxonomy => $terms ) {
1653                         $tax_object = get_taxonomy( $taxonomy );
1654                         /** This filter is documented in wp-admin/includes/class-wp-posts-list-table.php */
1655                         if ( ! apply_filters( 'quick_edit_show_taxonomy', $tax_object->show_in_quick_edit, $taxonomy, $post['post_type'] ) ) {
1656                                 unset( $data['tax_input'][ $taxonomy ] );
1657                         }
1658                 }
1659         }
1660
1661         // Hack: wp_unique_post_slug() doesn't work for drafts, so we will fake that our post is published.
1662         if ( ! empty( $data['post_name'] ) && in_array( $post['post_status'], array( 'draft', 'pending' ) ) ) {
1663                 $post['post_status'] = 'publish';
1664                 $data['post_name'] = wp_unique_post_slug( $data['post_name'], $post['ID'], $post['post_status'], $post['post_type'], $post['post_parent'] );
1665         }
1666
1667         // Update the post.
1668         edit_post();
1669
1670         $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => $_POST['screen'] ) );
1671
1672         $mode = $_POST['post_view'] === 'excerpt' ? 'excerpt' : 'list';
1673
1674         $level = 0;
1675         $request_post = array( get_post( $_POST['post_ID'] ) );
1676         $parent = $request_post[0]->post_parent;
1677
1678         while ( $parent > 0 ) {
1679                 $parent_post = get_post( $parent );
1680                 $parent = $parent_post->post_parent;
1681                 $level++;
1682         }
1683
1684         $wp_list_table->display_rows( array( get_post( $_POST['post_ID'] ) ), $level );
1685
1686         wp_die();
1687 }
1688
1689 /**
1690  * Ajax handler for quick edit saving for a term.
1691  *
1692  * @since 3.1.0
1693  *
1694  * @global WP_List_Table $wp_list_table
1695  */
1696 function wp_ajax_inline_save_tax() {
1697         global $wp_list_table;
1698
1699         check_ajax_referer( 'taxinlineeditnonce', '_inline_edit' );
1700
1701         $taxonomy = sanitize_key( $_POST['taxonomy'] );
1702         $tax = get_taxonomy( $taxonomy );
1703         if ( ! $tax )
1704                 wp_die( 0 );
1705
1706         if ( ! current_user_can( $tax->cap->edit_terms ) )
1707                 wp_die( -1 );
1708
1709         $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => 'edit-' . $taxonomy ) );
1710
1711         if ( ! isset($_POST['tax_ID']) || ! ( $id = (int) $_POST['tax_ID'] ) )
1712                 wp_die( -1 );
1713
1714         $tag = get_term( $id, $taxonomy );
1715         $_POST['description'] = $tag->description;
1716
1717         $updated = wp_update_term($id, $taxonomy, $_POST);
1718         if ( $updated && !is_wp_error($updated) ) {
1719                 $tag = get_term( $updated['term_id'], $taxonomy );
1720                 if ( !$tag || is_wp_error( $tag ) ) {
1721                         if ( is_wp_error($tag) && $tag->get_error_message() )
1722                                 wp_die( $tag->get_error_message() );
1723                         wp_die( __( 'Item not updated.' ) );
1724                 }
1725         } else {
1726                 if ( is_wp_error($updated) && $updated->get_error_message() )
1727                         wp_die( $updated->get_error_message() );
1728                 wp_die( __( 'Item not updated.' ) );
1729         }
1730         $level = 0;
1731         $parent = $tag->parent;
1732         while ( $parent > 0 ) {
1733                 $parent_tag = get_term( $parent, $taxonomy );
1734                 $parent = $parent_tag->parent;
1735                 $level++;
1736         }
1737         $wp_list_table->single_row( $tag, $level );
1738         wp_die();
1739 }
1740
1741 /**
1742  * Ajax handler for querying posts for the Find Posts modal.
1743  *
1744  * @see window.findPosts
1745  *
1746  * @since 3.1.0
1747  */
1748 function wp_ajax_find_posts() {
1749         check_ajax_referer( 'find-posts' );
1750
1751         $post_types = get_post_types( array( 'public' => true ), 'objects' );
1752         unset( $post_types['attachment'] );
1753
1754         $s = wp_unslash( $_POST['ps'] );
1755         $args = array(
1756                 'post_type' => array_keys( $post_types ),
1757                 'post_status' => 'any',
1758                 'posts_per_page' => 50,
1759         );
1760         if ( '' !== $s )
1761                 $args['s'] = $s;
1762
1763         $posts = get_posts( $args );
1764
1765         if ( ! $posts ) {
1766                 wp_send_json_error( __( 'No items found.' ) );
1767         }
1768
1769         $html = '<table class="widefat"><thead><tr><th class="found-radio"><br /></th><th>'.__('Title').'</th><th class="no-break">'.__('Type').'</th><th class="no-break">'.__('Date').'</th><th class="no-break">'.__('Status').'</th></tr></thead><tbody>';
1770         $alt = '';
1771         foreach ( $posts as $post ) {
1772                 $title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
1773                 $alt = ( 'alternate' == $alt ) ? '' : 'alternate';
1774
1775                 switch ( $post->post_status ) {
1776                         case 'publish' :
1777                         case 'private' :
1778                                 $stat = __('Published');
1779                                 break;
1780                         case 'future' :
1781                                 $stat = __('Scheduled');
1782                                 break;
1783                         case 'pending' :
1784                                 $stat = __('Pending Review');
1785                                 break;
1786                         case 'draft' :
1787                                 $stat = __('Draft');
1788                                 break;
1789                 }
1790
1791                 if ( '0000-00-00 00:00:00' == $post->post_date ) {
1792                         $time = '';
1793                 } else {
1794                         /* translators: date format in table columns, see http://php.net/date */
1795                         $time = mysql2date(__('Y/m/d'), $post->post_date);
1796                 }
1797
1798                 $html .= '<tr class="' . trim( 'found-posts ' . $alt ) . '"><td class="found-radio"><input type="radio" id="found-'.$post->ID.'" name="found_post_id" value="' . esc_attr($post->ID) . '"></td>';
1799                 $html .= '<td><label for="found-'.$post->ID.'">' . esc_html( $title ) . '</label></td><td class="no-break">' . esc_html( $post_types[$post->post_type]->labels->singular_name ) . '</td><td class="no-break">'.esc_html( $time ) . '</td><td class="no-break">' . esc_html( $stat ). ' </td></tr>' . "\n\n";
1800         }
1801
1802         $html .= '</tbody></table>';
1803
1804         wp_send_json_success( $html );
1805 }
1806
1807 /**
1808  * Ajax handler for saving the widgets order.
1809  *
1810  * @since 3.1.0
1811  */
1812 function wp_ajax_widgets_order() {
1813         check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1814
1815         if ( !current_user_can('edit_theme_options') )
1816                 wp_die( -1 );
1817
1818         unset( $_POST['savewidgets'], $_POST['action'] );
1819
1820         // Save widgets order for all sidebars.
1821         if ( is_array($_POST['sidebars']) ) {
1822                 $sidebars = array();
1823                 foreach ( $_POST['sidebars'] as $key => $val ) {
1824                         $sb = array();
1825                         if ( !empty($val) ) {
1826                                 $val = explode(',', $val);
1827                                 foreach ( $val as $k => $v ) {
1828                                         if ( strpos($v, 'widget-') === false )
1829                                                 continue;
1830
1831                                         $sb[$k] = substr($v, strpos($v, '_') + 1);
1832                                 }
1833                         }
1834                         $sidebars[$key] = $sb;
1835                 }
1836                 wp_set_sidebars_widgets($sidebars);
1837                 wp_die( 1 );
1838         }
1839
1840         wp_die( -1 );
1841 }
1842
1843 /**
1844  * Ajax handler for saving a widget.
1845  *
1846  * @since 3.1.0
1847  *
1848  * @global array $wp_registered_widgets
1849  * @global array $wp_registered_widget_controls
1850  * @global array $wp_registered_widget_updates
1851  */
1852 function wp_ajax_save_widget() {
1853         global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
1854
1855         check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1856
1857         if ( !current_user_can('edit_theme_options') || !isset($_POST['id_base']) )
1858                 wp_die( -1 );
1859
1860         unset( $_POST['savewidgets'], $_POST['action'] );
1861
1862         /**
1863          * Fires early when editing the widgets displayed in sidebars.
1864          *
1865          * @since 2.8.0
1866          */
1867         do_action( 'load-widgets.php' );
1868
1869         /**
1870          * Fires early when editing the widgets displayed in sidebars.
1871          *
1872          * @since 2.8.0
1873          */
1874         do_action( 'widgets.php' );
1875
1876         /** This action is documented in wp-admin/widgets.php */
1877         do_action( 'sidebar_admin_setup' );
1878
1879         $id_base = $_POST['id_base'];
1880         $widget_id = $_POST['widget-id'];
1881         $sidebar_id = $_POST['sidebar'];
1882         $multi_number = !empty($_POST['multi_number']) ? (int) $_POST['multi_number'] : 0;
1883         $settings = isset($_POST['widget-' . $id_base]) && is_array($_POST['widget-' . $id_base]) ? $_POST['widget-' . $id_base] : false;
1884         $error = '<p>' . __('An error has occurred. Please reload the page and try again.') . '</p>';
1885
1886         $sidebars = wp_get_sidebars_widgets();
1887         $sidebar = isset($sidebars[$sidebar_id]) ? $sidebars[$sidebar_id] : array();
1888
1889         // Delete.
1890         if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1891
1892                 if ( !isset($wp_registered_widgets[$widget_id]) )
1893                         wp_die( $error );
1894
1895                 $sidebar = array_diff( $sidebar, array($widget_id) );
1896                 $_POST = array('sidebar' => $sidebar_id, 'widget-' . $id_base => array(), 'the-widget-id' => $widget_id, 'delete_widget' => '1');
1897
1898                 /** This action is documented in wp-admin/widgets.php */
1899                 do_action( 'delete_widget', $widget_id, $sidebar_id, $id_base );
1900
1901         } elseif ( $settings && preg_match( '/__i__|%i%/', key($settings) ) ) {
1902                 if ( !$multi_number )
1903                         wp_die( $error );
1904
1905                 $_POST[ 'widget-' . $id_base ] = array( $multi_number => reset( $settings ) );
1906                 $widget_id = $id_base . '-' . $multi_number;
1907                 $sidebar[] = $widget_id;
1908         }
1909         $_POST['widget-id'] = $sidebar;
1910
1911         foreach ( (array) $wp_registered_widget_updates as $name => $control ) {
1912
1913                 if ( $name == $id_base ) {
1914                         if ( !is_callable( $control['callback'] ) )
1915                                 continue;
1916
1917                         ob_start();
1918                                 call_user_func_array( $control['callback'], $control['params'] );
1919                         ob_end_clean();
1920                         break;
1921                 }
1922         }
1923
1924         if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1925                 $sidebars[$sidebar_id] = $sidebar;
1926                 wp_set_sidebars_widgets($sidebars);
1927                 echo "deleted:$widget_id";
1928                 wp_die();
1929         }
1930
1931         if ( !empty($_POST['add_new']) )
1932                 wp_die();
1933
1934         if ( $form = $wp_registered_widget_controls[$widget_id] )
1935                 call_user_func_array( $form['callback'], $form['params'] );
1936
1937         wp_die();
1938 }
1939
1940 /**
1941  * Ajax handler for saving a widget.
1942  *
1943  * @since 3.9.0
1944  *
1945  * @global WP_Customize_Manager $wp_customize
1946  */
1947 function wp_ajax_update_widget() {
1948         global $wp_customize;
1949         $wp_customize->widgets->wp_ajax_update_widget();
1950 }
1951
1952 /**
1953  * Ajax handler for removing inactive widgets.
1954  *
1955  * @since 4.4.0
1956  */
1957 function wp_ajax_delete_inactive_widgets() {
1958         check_ajax_referer( 'remove-inactive-widgets', 'removeinactivewidgets' );
1959
1960         if ( ! current_user_can( 'edit_theme_options' ) ) {
1961                 wp_die( -1 );
1962         }
1963
1964         unset( $_POST['removeinactivewidgets'], $_POST['action'] );
1965
1966         do_action( 'load-widgets.php' );
1967         do_action( 'widgets.php' );
1968         do_action( 'sidebar_admin_setup' );
1969
1970         $sidebars_widgets = wp_get_sidebars_widgets();
1971
1972         foreach ( $sidebars_widgets['wp_inactive_widgets'] as $key => $widget_id ) {
1973                 $pieces = explode( '-', $widget_id );
1974                 $multi_number = array_pop( $pieces );
1975                 $id_base = implode( '-', $pieces );
1976                 $widget = get_option( 'widget_' . $id_base );
1977                 unset( $widget[$multi_number] );
1978                 update_option( 'widget_' . $id_base, $widget );
1979                 unset( $sidebars_widgets['wp_inactive_widgets'][$key] );
1980         }
1981
1982         wp_set_sidebars_widgets( $sidebars_widgets );
1983
1984         wp_die();
1985 }
1986
1987 /**
1988  * Ajax handler for uploading attachments
1989  *
1990  * @since 3.3.0
1991  */
1992 function wp_ajax_upload_attachment() {
1993         check_ajax_referer( 'media-form' );
1994         /*
1995          * This function does not use wp_send_json_success() / wp_send_json_error()
1996          * as the html4 Plupload handler requires a text/html content-type for older IE.
1997          * See https://core.trac.wordpress.org/ticket/31037
1998          */
1999
2000         if ( ! current_user_can( 'upload_files' ) ) {
2001                 echo wp_json_encode( array(
2002                         'success' => false,
2003                         'data'    => array(
2004                                 'message'  => __( 'You do not have permission to upload files.' ),
2005                                 'filename' => $_FILES['async-upload']['name'],
2006                         )
2007                 ) );
2008
2009                 wp_die();
2010         }
2011
2012         if ( isset( $_REQUEST['post_id'] ) ) {
2013                 $post_id = $_REQUEST['post_id'];
2014                 if ( ! current_user_can( 'edit_post', $post_id ) ) {
2015                         echo wp_json_encode( array(
2016                                 'success' => false,
2017                                 'data'    => array(
2018                                         'message'  => __( "You don't have permission to attach files to this post." ),
2019                                         'filename' => $_FILES['async-upload']['name'],
2020                                 )
2021                         ) );
2022
2023                         wp_die();
2024                 }
2025         } else {
2026                 $post_id = null;
2027         }
2028
2029         $post_data = isset( $_REQUEST['post_data'] ) ? $_REQUEST['post_data'] : array();
2030
2031         // If the context is custom header or background, make sure the uploaded file is an image.
2032         if ( isset( $post_data['context'] ) && in_array( $post_data['context'], array( 'custom-header', 'custom-background' ) ) ) {
2033                 $wp_filetype = wp_check_filetype_and_ext( $_FILES['async-upload']['tmp_name'], $_FILES['async-upload']['name'] );
2034                 if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) {
2035                         echo wp_json_encode( array(
2036                                 'success' => false,
2037                                 'data'    => array(
2038                                         'message'  => __( 'The uploaded file is not a valid image. Please try again.' ),
2039                                         'filename' => $_FILES['async-upload']['name'],
2040                                 )
2041                         ) );
2042
2043                         wp_die();
2044                 }
2045         }
2046
2047         $attachment_id = media_handle_upload( 'async-upload', $post_id, $post_data );
2048
2049         if ( is_wp_error( $attachment_id ) ) {
2050                 echo wp_json_encode( array(
2051                         'success' => false,
2052                         'data'    => array(
2053                                 'message'  => $attachment_id->get_error_message(),
2054                                 'filename' => $_FILES['async-upload']['name'],
2055                         )
2056                 ) );
2057
2058                 wp_die();
2059         }
2060
2061         if ( isset( $post_data['context'] ) && isset( $post_data['theme'] ) ) {
2062                 if ( 'custom-background' === $post_data['context'] )
2063                         update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', $post_data['theme'] );
2064
2065                 if ( 'custom-header' === $post_data['context'] )
2066                         update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', $post_data['theme'] );
2067         }
2068
2069         if ( ! $attachment = wp_prepare_attachment_for_js( $attachment_id ) )
2070                 wp_die();
2071
2072         echo wp_json_encode( array(
2073                 'success' => true,
2074                 'data'    => $attachment,
2075         ) );
2076
2077         wp_die();
2078 }
2079
2080 /**
2081  * Ajax handler for image editing.
2082  *
2083  * @since 3.1.0
2084  */
2085 function wp_ajax_image_editor() {
2086         $attachment_id = intval($_POST['postid']);
2087         if ( empty($attachment_id) || !current_user_can('edit_post', $attachment_id) )
2088                 wp_die( -1 );
2089
2090         check_ajax_referer( "image_editor-$attachment_id" );
2091         include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
2092
2093         $msg = false;
2094         switch ( $_POST['do'] ) {
2095                 case 'save' :
2096                         $msg = wp_save_image($attachment_id);
2097                         $msg = wp_json_encode($msg);
2098                         wp_die( $msg );
2099                         break;
2100                 case 'scale' :
2101                         $msg = wp_save_image($attachment_id);
2102                         break;
2103                 case 'restore' :
2104                         $msg = wp_restore_image($attachment_id);
2105                         break;
2106         }
2107
2108         wp_image_editor($attachment_id, $msg);
2109         wp_die();
2110 }
2111
2112 /**
2113  * Ajax handler for setting the featured image.
2114  *
2115  * @since 3.1.0
2116  */
2117 function wp_ajax_set_post_thumbnail() {
2118         $json = ! empty( $_REQUEST['json'] ); // New-style request
2119
2120         $post_ID = intval( $_POST['post_id'] );
2121         if ( ! current_user_can( 'edit_post', $post_ID ) )
2122                 wp_die( -1 );
2123
2124         $thumbnail_id = intval( $_POST['thumbnail_id'] );
2125
2126         if ( $json )
2127                 check_ajax_referer( "update-post_$post_ID" );
2128         else
2129                 check_ajax_referer( "set_post_thumbnail-$post_ID" );
2130
2131         if ( $thumbnail_id == '-1' ) {
2132                 if ( delete_post_thumbnail( $post_ID ) ) {
2133                         $return = _wp_post_thumbnail_html( null, $post_ID );
2134                         $json ? wp_send_json_success( $return ) : wp_die( $return );
2135                 } else {
2136                         wp_die( 0 );
2137                 }
2138         }
2139
2140         if ( set_post_thumbnail( $post_ID, $thumbnail_id ) ) {
2141                 $return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID );
2142                 $json ? wp_send_json_success( $return ) : wp_die( $return );
2143         }
2144
2145         wp_die( 0 );
2146 }
2147
2148 /**
2149  * AJAX handler for setting the featured image for an attachment.
2150  *
2151  * @since 4.0.0
2152  *
2153  * @see set_post_thumbnail()
2154  */
2155 function wp_ajax_set_attachment_thumbnail() {
2156         if ( empty( $_POST['urls'] ) || ! is_array( $_POST['urls'] ) ) {
2157                 wp_send_json_error();
2158         }
2159
2160         $thumbnail_id = (int) $_POST['thumbnail_id'];
2161         if ( empty( $thumbnail_id ) ) {
2162                 wp_send_json_error();
2163         }
2164
2165         $post_ids = array();
2166         // For each URL, try to find its corresponding post ID.
2167         foreach ( $_POST['urls'] as $url ) {
2168                 $post_id = attachment_url_to_postid( $url );
2169                 if ( ! empty( $post_id ) ) {
2170                         $post_ids[] = $post_id;
2171                 }
2172         }
2173
2174         if ( empty( $post_ids ) ) {
2175                 wp_send_json_error();
2176         }
2177
2178         $success = 0;
2179         // For each found attachment, set its thumbnail.
2180         foreach ( $post_ids as $post_id ) {
2181                 if ( ! current_user_can( 'edit_post', $post_id ) ) {
2182                         continue;
2183                 }
2184
2185                 if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) {
2186                         $success++;
2187                 }
2188         }
2189
2190         if ( 0 === $success ) {
2191                 wp_send_json_error();
2192         } else {
2193                 wp_send_json_success();
2194         }
2195
2196         wp_send_json_error();
2197 }
2198
2199 /**
2200  * Ajax handler for date formatting.
2201  *
2202  * @since 3.1.0
2203  */
2204 function wp_ajax_date_format() {
2205         wp_die( date_i18n( sanitize_option( 'date_format', wp_unslash( $_POST['date'] ) ) ) );
2206 }
2207
2208 /**
2209  * Ajax handler for time formatting.
2210  *
2211  * @since 3.1.0
2212  */
2213 function wp_ajax_time_format() {
2214         wp_die( date_i18n( sanitize_option( 'time_format', wp_unslash( $_POST['date'] ) ) ) );
2215 }
2216
2217 /**
2218  * Ajax handler for saving posts from the fullscreen editor.
2219  *
2220  * @since 3.1.0
2221  * @deprecated 4.3.0
2222  */
2223 function wp_ajax_wp_fullscreen_save_post() {
2224         $post_id = isset( $_POST['post_ID'] ) ? (int) $_POST['post_ID'] : 0;
2225
2226         $post = null;
2227
2228         if ( $post_id )
2229                 $post = get_post( $post_id );
2230
2231         check_ajax_referer('update-post_' . $post_id, '_wpnonce');
2232
2233         $post_id = edit_post();
2234
2235         if ( is_wp_error( $post_id ) ) {
2236                 wp_send_json_error();
2237         }
2238
2239         if ( $post ) {
2240                 $last_date = mysql2date( __( 'F j, Y' ), $post->post_modified );
2241                 $last_time = mysql2date( __( 'g:i a' ), $post->post_modified );
2242         } else {
2243                 $last_date = date_i18n( __( 'F j, Y' ) );
2244                 $last_time = date_i18n( __( 'g:i a' ) );
2245         }
2246
2247         if ( $last_id = get_post_meta( $post_id, '_edit_last', true ) ) {
2248                 $last_user = get_userdata( $last_id );
2249                 $last_edited = sprintf( __('Last edited by %1$s on %2$s at %3$s'), esc_html( $last_user->display_name ), $last_date, $last_time );
2250         } else {
2251                 $last_edited = sprintf( __('Last edited on %1$s at %2$s'), $last_date, $last_time );
2252         }
2253
2254         wp_send_json_success( array( 'last_edited' => $last_edited ) );
2255 }
2256
2257 /**
2258  * Ajax handler for removing a post lock.
2259  *
2260  * @since 3.1.0
2261  */
2262 function wp_ajax_wp_remove_post_lock() {
2263         if ( empty( $_POST['post_ID'] ) || empty( $_POST['active_post_lock'] ) )
2264                 wp_die( 0 );
2265         $post_id = (int) $_POST['post_ID'];
2266         if ( ! $post = get_post( $post_id ) )
2267                 wp_die( 0 );
2268
2269         check_ajax_referer( 'update-post_' . $post_id );
2270
2271         if ( ! current_user_can( 'edit_post', $post_id ) )
2272                 wp_die( -1 );
2273
2274         $active_lock = array_map( 'absint', explode( ':', $_POST['active_post_lock'] ) );
2275         if ( $active_lock[1] != get_current_user_id() )
2276                 wp_die( 0 );
2277
2278         /**
2279          * Filter the post lock window duration.
2280          *
2281          * @since 3.3.0
2282          *
2283          * @param int $interval The interval in seconds the post lock duration
2284          *                      should last, plus 5 seconds. Default 150.
2285          */
2286         $new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 150 ) + 5 ) . ':' . $active_lock[1];
2287         update_post_meta( $post_id, '_edit_lock', $new_lock, implode( ':', $active_lock ) );
2288         wp_die( 1 );
2289 }
2290
2291 /**
2292  * Ajax handler for dismissing a WordPress pointer.
2293  *
2294  * @since 3.1.0
2295  */
2296 function wp_ajax_dismiss_wp_pointer() {
2297         $pointer = $_POST['pointer'];
2298         if ( $pointer != sanitize_key( $pointer ) )
2299                 wp_die( 0 );
2300
2301 //      check_ajax_referer( 'dismiss-pointer_' . $pointer );
2302
2303         $dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) );
2304
2305         if ( in_array( $pointer, $dismissed ) )
2306                 wp_die( 0 );
2307
2308         $dismissed[] = $pointer;
2309         $dismissed = implode( ',', $dismissed );
2310
2311         update_user_meta( get_current_user_id(), 'dismissed_wp_pointers', $dismissed );
2312         wp_die( 1 );
2313 }
2314
2315 /**
2316  * Ajax handler for getting an attachment.
2317  *
2318  * @since 3.5.0
2319  */
2320 function wp_ajax_get_attachment() {
2321         if ( ! isset( $_REQUEST['id'] ) )
2322                 wp_send_json_error();
2323
2324         if ( ! $id = absint( $_REQUEST['id'] ) )
2325                 wp_send_json_error();
2326
2327         if ( ! $post = get_post( $id ) )
2328                 wp_send_json_error();
2329
2330         if ( 'attachment' != $post->post_type )
2331                 wp_send_json_error();
2332
2333         if ( ! current_user_can( 'upload_files' ) )
2334                 wp_send_json_error();
2335
2336         if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2337                 wp_send_json_error();
2338
2339         wp_send_json_success( $attachment );
2340 }
2341
2342 /**
2343  * Ajax handler for querying attachments.
2344  *
2345  * @since 3.5.0
2346  */
2347 function wp_ajax_query_attachments() {
2348         if ( ! current_user_can( 'upload_files' ) )
2349                 wp_send_json_error();
2350
2351         $query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array();
2352         $keys = array(
2353                 's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type',
2354                 'post_parent', 'post__in', 'post__not_in', 'year', 'monthnum'
2355         );
2356         foreach ( get_taxonomies_for_attachments( 'objects' ) as $t ) {
2357                 if ( $t->query_var && isset( $query[ $t->query_var ] ) ) {
2358                         $keys[] = $t->query_var;
2359                 }
2360         }
2361
2362         $query = array_intersect_key( $query, array_flip( $keys ) );
2363         $query['post_type'] = 'attachment';
2364         if ( MEDIA_TRASH
2365                 && ! empty( $_REQUEST['query']['post_status'] )
2366                 && 'trash' === $_REQUEST['query']['post_status'] ) {
2367                 $query['post_status'] = 'trash';
2368         } else {
2369                 $query['post_status'] = 'inherit';
2370         }
2371
2372         if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) )
2373                 $query['post_status'] .= ',private';
2374
2375         /**
2376          * Filter the arguments passed to WP_Query during an AJAX
2377          * call for querying attachments.
2378          *
2379          * @since 3.7.0
2380          *
2381          * @see WP_Query::parse_query()
2382          *
2383          * @param array $query An array of query variables.
2384          */
2385         $query = apply_filters( 'ajax_query_attachments_args', $query );
2386         $query = new WP_Query( $query );
2387
2388         $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts );
2389         $posts = array_filter( $posts );
2390
2391         wp_send_json_success( $posts );
2392 }
2393
2394 /**
2395  * Ajax handler for updating attachment attributes.
2396  *
2397  * @since 3.5.0
2398  */
2399 function wp_ajax_save_attachment() {
2400         if ( ! isset( $_REQUEST['id'] ) || ! isset( $_REQUEST['changes'] ) )
2401                 wp_send_json_error();
2402
2403         if ( ! $id = absint( $_REQUEST['id'] ) )
2404                 wp_send_json_error();
2405
2406         check_ajax_referer( 'update-post_' . $id, 'nonce' );
2407
2408         if ( ! current_user_can( 'edit_post', $id ) )
2409                 wp_send_json_error();
2410
2411         $changes = $_REQUEST['changes'];
2412         $post    = get_post( $id, ARRAY_A );
2413
2414         if ( 'attachment' != $post['post_type'] )
2415                 wp_send_json_error();
2416
2417         if ( isset( $changes['parent'] ) )
2418                 $post['post_parent'] = $changes['parent'];
2419
2420         if ( isset( $changes['title'] ) )
2421                 $post['post_title'] = $changes['title'];
2422
2423         if ( isset( $changes['caption'] ) )
2424                 $post['post_excerpt'] = $changes['caption'];
2425
2426         if ( isset( $changes['description'] ) )
2427                 $post['post_content'] = $changes['description'];
2428
2429         if ( MEDIA_TRASH && isset( $changes['status'] ) )
2430                 $post['post_status'] = $changes['status'];
2431
2432         if ( isset( $changes['alt'] ) ) {
2433                 $alt = wp_unslash( $changes['alt'] );
2434                 if ( $alt != get_post_meta( $id, '_wp_attachment_image_alt', true ) ) {
2435                         $alt = wp_strip_all_tags( $alt, true );
2436                         update_post_meta( $id, '_wp_attachment_image_alt', wp_slash( $alt ) );
2437                 }
2438         }
2439
2440         if ( wp_attachment_is( 'audio', $post['ID'] ) ) {
2441                 $changed = false;
2442                 $id3data = wp_get_attachment_metadata( $post['ID'] );
2443                 if ( ! is_array( $id3data ) ) {
2444                         $changed = true;
2445                         $id3data = array();
2446                 }
2447                 foreach ( wp_get_attachment_id3_keys( (object) $post, 'edit' ) as $key => $label ) {
2448                         if ( isset( $changes[ $key ] ) ) {
2449                                 $changed = true;
2450                                 $id3data[ $key ] = sanitize_text_field( wp_unslash( $changes[ $key ] ) );
2451                         }
2452                 }
2453
2454                 if ( $changed ) {
2455                         wp_update_attachment_metadata( $id, $id3data );
2456                 }
2457         }
2458
2459         if ( MEDIA_TRASH && isset( $changes['status'] ) && 'trash' === $changes['status'] ) {
2460                 wp_delete_post( $id );
2461         } else {
2462                 wp_update_post( $post );
2463         }
2464
2465         wp_send_json_success();
2466 }
2467
2468 /**
2469  * Ajax handler for saving backwards compatible attachment attributes.
2470  *
2471  * @since 3.5.0
2472  */
2473 function wp_ajax_save_attachment_compat() {
2474         if ( ! isset( $_REQUEST['id'] ) )
2475                 wp_send_json_error();
2476
2477         if ( ! $id = absint( $_REQUEST['id'] ) )
2478                 wp_send_json_error();
2479
2480         if ( empty( $_REQUEST['attachments'] ) || empty( $_REQUEST['attachments'][ $id ] ) )
2481                 wp_send_json_error();
2482         $attachment_data = $_REQUEST['attachments'][ $id ];
2483
2484         check_ajax_referer( 'update-post_' . $id, 'nonce' );
2485
2486         if ( ! current_user_can( 'edit_post', $id ) )
2487                 wp_send_json_error();
2488
2489         $post = get_post( $id, ARRAY_A );
2490
2491         if ( 'attachment' != $post['post_type'] )
2492                 wp_send_json_error();
2493
2494         /** This filter is documented in wp-admin/includes/media.php */
2495         $post = apply_filters( 'attachment_fields_to_save', $post, $attachment_data );
2496
2497         if ( isset( $post['errors'] ) ) {
2498                 $errors = $post['errors']; // @todo return me and display me!
2499                 unset( $post['errors'] );
2500         }
2501
2502         wp_update_post( $post );
2503
2504         foreach ( get_attachment_taxonomies( $post ) as $taxonomy ) {
2505                 if ( isset( $attachment_data[ $taxonomy ] ) )
2506                         wp_set_object_terms( $id, array_map( 'trim', preg_split( '/,+/', $attachment_data[ $taxonomy ] ) ), $taxonomy, false );
2507         }
2508
2509         if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2510                 wp_send_json_error();
2511
2512         wp_send_json_success( $attachment );
2513 }
2514
2515 /**
2516  * Ajax handler for saving the attachment order.
2517  *
2518  * @since 3.5.0
2519  */
2520 function wp_ajax_save_attachment_order() {
2521         if ( ! isset( $_REQUEST['post_id'] ) )
2522                 wp_send_json_error();
2523
2524         if ( ! $post_id = absint( $_REQUEST['post_id'] ) )
2525                 wp_send_json_error();
2526
2527         if ( empty( $_REQUEST['attachments'] ) )
2528                 wp_send_json_error();
2529
2530         check_ajax_referer( 'update-post_' . $post_id, 'nonce' );
2531
2532         $attachments = $_REQUEST['attachments'];
2533
2534         if ( ! current_user_can( 'edit_post', $post_id ) )
2535                 wp_send_json_error();
2536
2537         foreach ( $attachments as $attachment_id => $menu_order ) {
2538                 if ( ! current_user_can( 'edit_post', $attachment_id ) )
2539                         continue;
2540                 if ( ! $attachment = get_post( $attachment_id ) )
2541                         continue;
2542                 if ( 'attachment' != $attachment->post_type )
2543                         continue;
2544
2545                 wp_update_post( array( 'ID' => $attachment_id, 'menu_order' => $menu_order ) );
2546         }
2547
2548         wp_send_json_success();
2549 }
2550
2551 /**
2552  * Ajax handler for sending an attachment to the editor.
2553  *
2554  * Generates the HTML to send an attachment to the editor.
2555  * Backwards compatible with the media_send_to_editor filter
2556  * and the chain of filters that follow.
2557  *
2558  * @since 3.5.0
2559  */
2560 function wp_ajax_send_attachment_to_editor() {
2561         check_ajax_referer( 'media-send-to-editor', 'nonce' );
2562
2563         $attachment = wp_unslash( $_POST['attachment'] );
2564
2565         $id = intval( $attachment['id'] );
2566
2567         if ( ! $post = get_post( $id ) )
2568                 wp_send_json_error();
2569
2570         if ( 'attachment' != $post->post_type )
2571                 wp_send_json_error();
2572
2573         if ( current_user_can( 'edit_post', $id ) ) {
2574                 // If this attachment is unattached, attach it. Primarily a back compat thing.
2575                 if ( 0 == $post->post_parent && $insert_into_post_id = intval( $_POST['post_id'] ) ) {
2576                         wp_update_post( array( 'ID' => $id, 'post_parent' => $insert_into_post_id ) );
2577                 }
2578         }
2579
2580         $url = empty( $attachment['url'] ) ? '' : $attachment['url'];
2581         $rel = ( strpos( $url, 'attachment_id') || get_attachment_link( $id ) == $url );
2582
2583         remove_filter( 'media_send_to_editor', 'image_media_send_to_editor' );
2584
2585         if ( 'image' === substr( $post->post_mime_type, 0, 5 ) ) {
2586                 $align = isset( $attachment['align'] ) ? $attachment['align'] : 'none';
2587                 $size = isset( $attachment['image-size'] ) ? $attachment['image-size'] : 'medium';
2588                 $alt = isset( $attachment['image_alt'] ) ? $attachment['image_alt'] : '';
2589
2590                 // No whitespace-only captions.
2591                 $caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : '';
2592                 if ( '' === trim( $caption ) ) {
2593                         $caption = '';
2594                 }
2595
2596                 $title = ''; // We no longer insert title tags into <img> tags, as they are redundant.
2597                 $html = get_image_send_to_editor( $id, $caption, $title, $align, $url, $rel, $size, $alt );
2598         } elseif ( wp_attachment_is( 'video', $post ) || wp_attachment_is( 'audio', $post )  ) {
2599                 $html = stripslashes_deep( $_POST['html'] );
2600         } else {
2601                 $html = isset( $attachment['post_title'] ) ? $attachment['post_title'] : '';
2602                 $rel = $rel ? ' rel="attachment wp-att-' . $id . '"' : ''; // Hard-coded string, $id is already sanitized
2603
2604                 if ( ! empty( $url ) ) {
2605                         $html = '<a href="' . esc_url( $url ) . '"' . $rel . '>' . $html . '</a>';
2606                 }
2607         }
2608
2609         /** This filter is documented in wp-admin/includes/media.php */
2610         $html = apply_filters( 'media_send_to_editor', $html, $id, $attachment );
2611
2612         wp_send_json_success( $html );
2613 }
2614
2615 /**
2616  * Ajax handler for sending a link to the editor.
2617  *
2618  * Generates the HTML to send a non-image embed link to the editor.
2619  *
2620  * Backwards compatible with the following filters:
2621  * - file_send_to_editor_url
2622  * - audio_send_to_editor_url
2623  * - video_send_to_editor_url
2624  *
2625  * @since 3.5.0
2626  *
2627  * @global WP_Post  $post
2628  * @global WP_Embed $wp_embed
2629  */
2630 function wp_ajax_send_link_to_editor() {
2631         global $post, $wp_embed;
2632
2633         check_ajax_referer( 'media-send-to-editor', 'nonce' );
2634
2635         if ( ! $src = wp_unslash( $_POST['src'] ) )
2636                 wp_send_json_error();
2637
2638         if ( ! strpos( $src, '://' ) )
2639                 $src = 'http://' . $src;
2640
2641         if ( ! $src = esc_url_raw( $src ) )
2642                 wp_send_json_error();
2643
2644         if ( ! $link_text = trim( wp_unslash( $_POST['link_text'] ) ) )
2645                 $link_text = wp_basename( $src );
2646
2647         $post = get_post( isset( $_POST['post_id'] ) ? $_POST['post_id'] : 0 );
2648
2649         // Ping WordPress for an embed.
2650         $check_embed = $wp_embed->run_shortcode( '[embed]'. $src .'[/embed]' );
2651
2652         // Fallback that WordPress creates when no oEmbed was found.
2653         $fallback = $wp_embed->maybe_make_link( $src );
2654
2655         if ( $check_embed !== $fallback ) {
2656                 // TinyMCE view for [embed] will parse this
2657                 $html = '[embed]' . $src . '[/embed]';
2658         } elseif ( $link_text ) {
2659                 $html = '<a href="' . esc_url( $src ) . '">' . $link_text . '</a>';
2660         } else {
2661                 $html = '';
2662         }
2663
2664         // Figure out what filter to run:
2665         $type = 'file';
2666         if ( ( $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $src ) ) && ( $ext_type = wp_ext2type( $ext ) )
2667                 && ( 'audio' == $ext_type || 'video' == $ext_type ) )
2668                         $type = $ext_type;
2669
2670         /** This filter is documented in wp-admin/includes/media.php */
2671         $html = apply_filters( $type . '_send_to_editor_url', $html, $src, $link_text );
2672
2673         wp_send_json_success( $html );
2674 }
2675
2676 /**
2677  * Ajax handler for the Heartbeat API.
2678  *
2679  * Runs when the user is logged in.
2680  *
2681  * @since 3.6.0
2682  */
2683 function wp_ajax_heartbeat() {
2684         if ( empty( $_POST['_nonce'] ) ) {
2685                 wp_send_json_error();
2686         }
2687
2688         $response = $data = array();
2689         $nonce_state = wp_verify_nonce( $_POST['_nonce'], 'heartbeat-nonce' );
2690
2691         // screen_id is the same as $current_screen->id and the JS global 'pagenow'.
2692         if ( ! empty( $_POST['screen_id'] ) ) {
2693                 $screen_id = sanitize_key($_POST['screen_id']);
2694         } else {
2695                 $screen_id = 'front';
2696         }
2697
2698         if ( ! empty( $_POST['data'] ) ) {
2699                 $data = wp_unslash( (array) $_POST['data'] );
2700         }
2701
2702         if ( 1 !== $nonce_state ) {
2703                 $response = apply_filters( 'wp_refresh_nonces', $response, $data, $screen_id );
2704
2705                 if ( false === $nonce_state ) {
2706                         // User is logged in but nonces have expired.
2707                         $response['nonces_expired'] = true;
2708                         wp_send_json( $response );
2709                 }
2710         }
2711
2712         if ( ! empty( $data ) ) {
2713                 /**
2714                  * Filter the Heartbeat response received.
2715                  *
2716                  * @since 3.6.0
2717                  *
2718                  * @param array  $response  The Heartbeat response.
2719                  * @param array  $data      The $_POST data sent.
2720                  * @param string $screen_id The screen id.
2721                  */
2722                 $response = apply_filters( 'heartbeat_received', $response, $data, $screen_id );
2723         }
2724
2725         /**
2726          * Filter the Heartbeat response sent.
2727          *
2728          * @since 3.6.0
2729          *
2730          * @param array  $response  The Heartbeat response.
2731          * @param string $screen_id The screen id.
2732          */
2733         $response = apply_filters( 'heartbeat_send', $response, $screen_id );
2734
2735         /**
2736          * Fires when Heartbeat ticks in logged-in environments.
2737          *
2738          * Allows the transport to be easily replaced with long-polling.
2739          *
2740          * @since 3.6.0
2741          *
2742          * @param array  $response  The Heartbeat response.
2743          * @param string $screen_id The screen id.
2744          */
2745         do_action( 'heartbeat_tick', $response, $screen_id );
2746
2747         // Send the current time according to the server
2748         $response['server_time'] = time();
2749
2750         wp_send_json( $response );
2751 }
2752
2753 /**
2754  * Ajax handler for getting revision diffs.
2755  *
2756  * @since 3.6.0
2757  */
2758 function wp_ajax_get_revision_diffs() {
2759         require ABSPATH . 'wp-admin/includes/revision.php';
2760
2761         if ( ! $post = get_post( (int) $_REQUEST['post_id'] ) )
2762                 wp_send_json_error();
2763
2764         if ( ! current_user_can( 'read_post', $post->ID ) )
2765                 wp_send_json_error();
2766
2767         // Really just pre-loading the cache here.
2768         if ( ! $revisions = wp_get_post_revisions( $post->ID, array( 'check_enabled' => false ) ) )
2769                 wp_send_json_error();
2770
2771         $return = array();
2772         @set_time_limit( 0 );
2773
2774         foreach ( $_REQUEST['compare'] as $compare_key ) {
2775                 list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to
2776
2777                 $return[] = array(
2778                         'id' => $compare_key,
2779                         'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ),
2780                 );
2781         }
2782         wp_send_json_success( $return );
2783 }
2784
2785 /**
2786  * Ajax handler for auto-saving the selected color scheme for
2787  * a user's own profile.
2788  *
2789  * @since 3.8.0
2790  *
2791  * @global array $_wp_admin_css_colors
2792  */
2793 function wp_ajax_save_user_color_scheme() {
2794         global $_wp_admin_css_colors;
2795
2796         check_ajax_referer( 'save-color-scheme', 'nonce' );
2797
2798         $color_scheme = sanitize_key( $_POST['color_scheme'] );
2799
2800         if ( ! isset( $_wp_admin_css_colors[ $color_scheme ] ) ) {
2801                 wp_send_json_error();
2802         }
2803
2804         $previous_color_scheme = get_user_meta( get_current_user_id(), 'admin_color', true );
2805         update_user_meta( get_current_user_id(), 'admin_color', $color_scheme );
2806
2807         wp_send_json_success( array(
2808                 'previousScheme' => 'admin-color-' . $previous_color_scheme,
2809                 'currentScheme'  => 'admin-color-' . $color_scheme
2810         ) );
2811 }
2812
2813 /**
2814  * Ajax handler for getting themes from themes_api().
2815  *
2816  * @since 3.9.0
2817  *
2818  * @global array $themes_allowedtags
2819  * @global array $theme_field_defaults
2820  */
2821 function wp_ajax_query_themes() {
2822         global $themes_allowedtags, $theme_field_defaults;
2823
2824         if ( ! current_user_can( 'install_themes' ) ) {
2825                 wp_send_json_error();
2826         }
2827
2828         $args = wp_parse_args( wp_unslash( $_REQUEST['request'] ), array(
2829                 'per_page' => 20,
2830                 'fields'   => $theme_field_defaults
2831         ) );
2832
2833         if ( isset( $args['browse'] ) && 'favorites' === $args['browse'] && ! isset( $args['user'] ) ) {
2834                 $user = get_user_option( 'wporg_favorites' );
2835                 if ( $user ) {
2836                         $args['user'] = $user;
2837                 }
2838         }
2839
2840         $old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search';
2841
2842         /** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */
2843         $args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args );
2844
2845         $api = themes_api( 'query_themes', $args );
2846
2847         if ( is_wp_error( $api ) ) {
2848                 wp_send_json_error();
2849         }
2850
2851         $update_php = network_admin_url( 'update.php?action=install-theme' );
2852         foreach ( $api->themes as &$theme ) {
2853                 $theme->install_url = add_query_arg( array(
2854                         'theme'    => $theme->slug,
2855                         '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug )
2856                 ), $update_php );
2857
2858                 $theme->name        = wp_kses( $theme->name, $themes_allowedtags );
2859                 $theme->author      = wp_kses( $theme->author, $themes_allowedtags );
2860                 $theme->version     = wp_kses( $theme->version, $themes_allowedtags );
2861                 $theme->description = wp_kses( $theme->description, $themes_allowedtags );
2862                 $theme->stars       = wp_star_rating( array( 'rating' => $theme->rating, 'type' => 'percent', 'number' => $theme->num_ratings, 'echo' => false ) );
2863                 $theme->num_ratings = number_format_i18n( $theme->num_ratings );
2864                 $theme->preview_url = set_url_scheme( $theme->preview_url );
2865         }
2866
2867         wp_send_json_success( $api );
2868 }
2869
2870 /**
2871  * Apply [embed] AJAX handlers to a string.
2872  *
2873  * @since 4.0.0
2874  *
2875  * @global WP_Post    $post       Global $post.
2876  * @global WP_Embed   $wp_embed   Embed API instance.
2877  * @global WP_Scripts $wp_scripts
2878  */
2879 function wp_ajax_parse_embed() {
2880         global $post, $wp_embed;
2881
2882         if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) {
2883                 wp_send_json_error();
2884         }
2885
2886         if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) {
2887                 wp_send_json_error();
2888         }
2889
2890         $shortcode = wp_unslash( $_POST['shortcode'] );
2891
2892         preg_match( '/' . get_shortcode_regex() . '/s', $shortcode, $matches );
2893         $atts = shortcode_parse_atts( $matches[3] );
2894         if ( ! empty( $matches[5] ) ) {
2895                 $url = $matches[5];
2896         } elseif ( ! empty( $atts['src'] ) ) {
2897                 $url = $atts['src'];
2898         } else {
2899                 $url = '';
2900         }
2901
2902         $parsed = false;
2903         setup_postdata( $post );
2904
2905         $wp_embed->return_false_on_fail = true;
2906
2907         if ( is_ssl() && 0 === strpos( $url, 'http://' ) ) {
2908                 // Admin is ssl and the user pasted non-ssl URL.
2909                 // Check if the provider supports ssl embeds and use that for the preview.
2910                 $ssl_shortcode = preg_replace( '%^(\\[embed[^\\]]*\\])http://%i', '$1https://', $shortcode );
2911                 $parsed = $wp_embed->run_shortcode( $ssl_shortcode );
2912
2913                 if ( ! $parsed ) {
2914                         $no_ssl_support = true;
2915                 }
2916         }
2917
2918         if ( $url && ! $parsed ) {
2919                 $parsed = $wp_embed->run_shortcode( $shortcode );
2920         }
2921
2922         if ( ! $parsed ) {
2923                 wp_send_json_error( array(
2924                         'type' => 'not-embeddable',
2925                         'message' => sprintf( __( '%s failed to embed.' ), '<code>' . esc_html( $url ) . '</code>' ),
2926                 ) );
2927         }
2928
2929         if ( has_shortcode( $parsed, 'audio' ) || has_shortcode( $parsed, 'video' ) ) {
2930                 $styles = '';
2931                 $mce_styles = wpview_media_sandbox_styles();
2932                 foreach ( $mce_styles as $style ) {
2933                         $styles .= sprintf( '<link rel="stylesheet" href="%s"/>', $style );
2934                 }
2935
2936                 $html = do_shortcode( $parsed );
2937
2938                 global $wp_scripts;
2939                 if ( ! empty( $wp_scripts ) ) {
2940                         $wp_scripts->done = array();
2941                 }
2942                 ob_start();
2943                 wp_print_scripts( 'wp-mediaelement' );
2944                 $scripts = ob_get_clean();
2945
2946                 $parsed = $styles . $html . $scripts;
2947         }
2948
2949
2950         if ( ! empty( $no_ssl_support ) || ( is_ssl() && ( preg_match( '%<(iframe|script|embed) [^>]*src="http://%', $parsed ) ||
2951                 preg_match( '%<link [^>]*href="http://%', $parsed ) ) ) ) {
2952                 // Admin is ssl and the embed is not. Iframes, scripts, and other "active content" will be blocked.
2953                 wp_send_json_error( array(
2954                         'type' => 'not-ssl',
2955                         'message' => __( 'This preview is unavailable in the editor.' ),
2956                 ) );
2957         }
2958
2959         wp_send_json_success( array(
2960                 'body' => $parsed,
2961                 'attr' => $wp_embed->last_attr
2962         ) );
2963 }
2964
2965 /**
2966  * @since 4.0.0
2967  *
2968  * @global WP_Post    $post
2969  * @global WP_Scripts $wp_scripts
2970  */
2971 function wp_ajax_parse_media_shortcode() {
2972         global $post, $wp_scripts;
2973
2974         if ( empty( $_POST['shortcode'] ) ) {
2975                 wp_send_json_error();
2976         }
2977
2978         $shortcode = wp_unslash( $_POST['shortcode'] );
2979
2980         if ( ! empty( $_POST['post_ID'] ) ) {
2981                 $post = get_post( (int) $_POST['post_ID'] );
2982         }
2983
2984         // the embed shortcode requires a post
2985         if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) {
2986                 if ( 'embed' === $shortcode ) {
2987                         wp_send_json_error();
2988                 }
2989         } else {
2990                 setup_postdata( $post );
2991         }
2992
2993         $parsed = do_shortcode( $shortcode  );
2994
2995         if ( empty( $parsed ) ) {
2996                 wp_send_json_error( array(
2997                         'type' => 'no-items',
2998                         'message' => __( 'No items found.' ),
2999                 ) );
3000         }
3001
3002         $head = '';
3003         $styles = wpview_media_sandbox_styles();
3004
3005         foreach ( $styles as $style ) {
3006                 $head .= '<link type="text/css" rel="stylesheet" href="' . $style . '">';
3007         }
3008
3009         if ( ! empty( $wp_scripts ) ) {
3010                 $wp_scripts->done = array();
3011         }
3012
3013         ob_start();
3014
3015         echo $parsed;
3016
3017         if ( 'playlist' === $_REQUEST['type'] ) {
3018                 wp_underscore_playlist_templates();
3019
3020                 wp_print_scripts( 'wp-playlist' );
3021         } else {
3022                 wp_print_scripts( array( 'froogaloop', 'wp-mediaelement' ) );
3023         }
3024
3025         wp_send_json_success( array(
3026                 'head' => $head,
3027                 'body' => ob_get_clean()
3028         ) );
3029 }
3030
3031 /**
3032  * AJAX handler for destroying multiple open sessions for a user.
3033  *
3034  * @since 4.1.0
3035  */
3036 function wp_ajax_destroy_sessions() {
3037         $user = get_userdata( (int) $_POST['user_id'] );
3038         if ( $user ) {
3039                 if ( ! current_user_can( 'edit_user', $user->ID ) ) {
3040                         $user = false;
3041                 } elseif ( ! wp_verify_nonce( $_POST['nonce'], 'update-user_' . $user->ID ) ) {
3042                         $user = false;
3043                 }
3044         }
3045
3046         if ( ! $user ) {
3047                 wp_send_json_error( array(
3048                         'message' => __( 'Could not log out user sessions. Please try again.' ),
3049                 ) );
3050         }
3051
3052         $sessions = WP_Session_Tokens::get_instance( $user->ID );
3053
3054         if ( $user->ID === get_current_user_id() ) {
3055                 $sessions->destroy_others( wp_get_session_token() );
3056                 $message = __( 'You are now logged out everywhere else.' );
3057         } else {
3058                 $sessions->destroy_all();
3059                 /* translators: 1: User's display name. */
3060                 $message = sprintf( __( '%s has been logged out.' ), $user->display_name );
3061         }
3062
3063         wp_send_json_success( array( 'message' => $message ) );
3064 }
3065
3066
3067 /**
3068  * AJAX handler for updating a plugin.
3069  *
3070  * @since 4.2.0
3071  *
3072  * @see Plugin_Upgrader
3073  */
3074 function wp_ajax_update_plugin() {
3075         global $wp_filesystem;
3076
3077         $plugin = urldecode( $_POST['plugin'] );
3078
3079         $status = array(
3080                 'update'     => 'plugin',
3081                 'plugin'     => $plugin,
3082                 'slug'       => sanitize_key( $_POST['slug'] ),
3083                 'oldVersion' => '',
3084                 'newVersion' => '',
3085         );
3086
3087         $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
3088         if ( $plugin_data['Version'] ) {
3089                 $status['oldVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
3090         }
3091
3092         if ( ! current_user_can( 'update_plugins' ) ) {
3093                 $status['error'] = __( 'You do not have sufficient permissions to update plugins for this site.' );
3094                 wp_send_json_error( $status );
3095         }
3096
3097         check_ajax_referer( 'updates' );
3098
3099         include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
3100
3101         wp_update_plugins();
3102
3103         $skin = new Automatic_Upgrader_Skin();
3104         $upgrader = new Plugin_Upgrader( $skin );
3105         $result = $upgrader->bulk_upgrade( array( $plugin ) );
3106
3107         if ( is_array( $result ) && empty( $result[$plugin] ) && is_wp_error( $skin->result ) ) {
3108                 $result = $skin->result;
3109         }
3110
3111         if ( is_array( $result ) && !empty( $result[ $plugin ] ) ) {
3112                 $plugin_update_data = current( $result );
3113
3114                 /*
3115                  * If the `update_plugins` site transient is empty (e.g. when you update
3116                  * two plugins in quick succession before the transient repopulates),
3117                  * this may be the return.
3118                  *
3119                  * Preferably something can be done to ensure `update_plugins` isn't empty.
3120                  * For now, surface some sort of error here.
3121                  */
3122                 if ( $plugin_update_data === true ) {
3123                         $status['error'] = __( 'Plugin update failed.' );
3124                         wp_send_json_error( $status );
3125                 }
3126
3127                 $plugin_data = get_plugins( '/' . $result[ $plugin ]['destination_name'] );
3128                 $plugin_data = reset( $plugin_data );
3129
3130                 if ( $plugin_data['Version'] ) {
3131                         $status['newVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
3132                 }
3133
3134                 wp_send_json_success( $status );
3135         } else if ( is_wp_error( $result ) ) {
3136                 $status['error'] = $result->get_error_message();
3137                 wp_send_json_error( $status );
3138
3139         } else if ( is_bool( $result ) && ! $result ) {
3140                 $status['errorCode'] = 'unable_to_connect_to_filesystem';
3141                 $status['error'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' );
3142
3143                 // Pass through the error from WP_Filesystem if one was raised
3144                 if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
3145                         $status['error'] = $wp_filesystem->errors->get_error_message();
3146                 }
3147
3148                 wp_send_json_error( $status );
3149
3150         } else {
3151                 // An unhandled error occured
3152                 $status['error'] = __( 'Plugin update failed.' );
3153                 wp_send_json_error( $status );
3154         }
3155 }
3156
3157 /**
3158  * AJAX handler for saving a post from Press This.
3159  *
3160  * @since 4.2.0
3161  *
3162  * @global WP_Press_This $wp_press_this
3163  */
3164 function wp_ajax_press_this_save_post() {
3165         if ( empty( $GLOBALS['wp_press_this'] ) ) {
3166                 include( ABSPATH . 'wp-admin/includes/class-wp-press-this.php' );
3167         }
3168
3169         $GLOBALS['wp_press_this']->save_post();
3170 }
3171
3172 /**
3173  * AJAX handler for creating new category from Press This.
3174  *
3175  * @since 4.2.0
3176  *
3177  * @global WP_Press_This $wp_press_this
3178  */
3179 function wp_ajax_press_this_add_category() {
3180         if ( empty( $GLOBALS['wp_press_this'] ) ) {
3181                 include( ABSPATH . 'wp-admin/includes/class-wp-press-this.php' );
3182         }
3183
3184         $GLOBALS['wp_press_this']->add_category();
3185 }
3186
3187 /**
3188  * AJAX handler for cropping an image.
3189  *
3190  * @since 4.3.0
3191  *
3192  * @global WP_Site_Icon $wp_site_icon
3193  */
3194 function wp_ajax_crop_image() {
3195         $attachment_id = absint( $_POST['id'] );
3196
3197         check_ajax_referer( 'image_editor-' . $attachment_id, 'nonce' );
3198         if ( ! current_user_can( 'customize' ) ) {
3199                 wp_send_json_error();
3200         }
3201
3202         $context = str_replace( '_', '-', $_POST['context'] );
3203         $data    = array_map( 'absint', $_POST['cropDetails'] );
3204         $cropped = wp_crop_image( $attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height'] );
3205
3206         if ( ! $cropped || is_wp_error( $cropped ) ) {
3207                 wp_send_json_error( array( 'message' => __( 'Image could not be processed.' ) ) );
3208         }
3209
3210         switch ( $context ) {
3211                 case 'site-icon':
3212                         require_once ABSPATH . '/wp-admin/includes/class-wp-site-icon.php';
3213                         global $wp_site_icon;
3214
3215                         // Skip creating a new attachment if the attachment is a Site Icon.
3216                         if ( get_post_meta( $attachment_id, '_wp_attachment_context', true ) == $context ) {
3217
3218                                 // Delete the temporary cropped file, we don't need it.
3219                                 wp_delete_file( $cropped );
3220
3221                                 // Additional sizes in wp_prepare_attachment_for_js().
3222                                 add_filter( 'image_size_names_choose', array( $wp_site_icon, 'additional_sizes' ) );
3223                                 break;
3224                         }
3225
3226                         /** This filter is documented in wp-admin/custom-header.php */
3227                         $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.
3228                         $object  = $wp_site_icon->create_attachment_object( $cropped, $attachment_id );
3229                         unset( $object['ID'] );
3230
3231                         // Update the attachment.
3232                         add_filter( 'intermediate_image_sizes_advanced', array( $wp_site_icon, 'additional_sizes' ) );
3233                         $attachment_id = $wp_site_icon->insert_attachment( $object, $cropped );
3234                         remove_filter( 'intermediate_image_sizes_advanced', array( $wp_site_icon, 'additional_sizes' ) );
3235
3236                         // Additional sizes in wp_prepare_attachment_for_js().
3237                         add_filter( 'image_size_names_choose', array( $wp_site_icon, 'additional_sizes' ) );
3238                         break;
3239
3240                 default:
3241
3242                         /**
3243                          * Fires before a cropped image is saved.
3244                          *
3245                          * Allows to add filters to modify the way a cropped image is saved.
3246                          *
3247                          * @since 4.3.0
3248                          *
3249                          * @param string $context       The Customizer control requesting the cropped image.
3250                          * @param int    $attachment_id The attachment ID of the original image.
3251                          * @param string $cropped       Path to the cropped image file.
3252                          */
3253                         do_action( 'wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped );
3254
3255                         /** This filter is documented in wp-admin/custom-header.php */
3256                         $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.
3257
3258                         $parent_url = wp_get_attachment_url( $attachment_id );
3259                         $url        = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
3260
3261                         $size       = @getimagesize( $cropped );
3262                         $image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
3263
3264                         $object = array(
3265                                 'post_title'     => basename( $cropped ),
3266                                 'post_content'   => $url,
3267                                 'post_mime_type' => $image_type,
3268                                 'guid'           => $url,
3269                                 'context'        => $context,
3270                         );
3271
3272                         $attachment_id = wp_insert_attachment( $object, $cropped );
3273                         $metadata = wp_generate_attachment_metadata( $attachment_id, $cropped );
3274
3275                         /**
3276                          * Filter the cropped image attachment metadata.
3277                          *
3278                          * @since 4.3.0
3279                          *
3280                          * @see wp_generate_attachment_metadata()
3281                          *
3282                          * @param array $metadata Attachment metadata.
3283                          */
3284                         $metadata = apply_filters( 'wp_ajax_cropped_attachment_metadata', $metadata );
3285                         wp_update_attachment_metadata( $attachment_id, $metadata );
3286
3287                         /**
3288                          * Filter the attachment ID for a cropped image.
3289                          *
3290                          * @since 4.3.0
3291                          *
3292                          * @param int    $attachment_id The attachment ID of the cropped image.
3293                          * @param string $context       The Customizer control requesting the cropped image.
3294                          */
3295                         $attachment_id = apply_filters( 'wp_ajax_cropped_attachment_id', $attachment_id, $context );
3296         }
3297
3298         wp_send_json_success( wp_prepare_attachment_for_js( $attachment_id ) );
3299 }
3300
3301 /**
3302  * Ajax handler for generating a password.
3303  *
3304  * @since 4.4.0
3305  */
3306 function wp_ajax_generate_password() {
3307         wp_send_json_success( wp_generate_password( 24 ) );
3308 }
3309
3310 /**
3311  * Ajax handler for saving the user's WordPress.org username.
3312  *
3313  * @since 4.4.0
3314  */
3315 function wp_ajax_save_wporg_username() {
3316         if ( ! current_user_can( 'install_themes' ) && ! current_user_can( 'install_plugins' ) ) {
3317                 wp_send_json_error();
3318         }
3319
3320         check_ajax_referer( 'save_wporg_username_' . get_current_user_id() );
3321
3322         $username = isset( $_REQUEST['username'] ) ? wp_unslash( $_REQUEST['username'] ) : false;
3323
3324         if ( ! $username ) {
3325                 wp_send_json_error();
3326         }
3327
3328         wp_send_json_success( update_user_meta( get_current_user_id(), 'wporg_favorites', $username ) );
3329 }