WordPress 3.8
[autoinstalls/wordpress.git] / wp-admin / includes / ajax-actions.php
1 <?php
2 /**
3  * WordPress Core Ajax Handlers.
4  *
5  * @package WordPress
6  * @subpackage Administration
7  */
8
9 /*
10  * No-privilege Ajax handlers.
11  */
12
13 /**
14  * Heartbeat API (experimental)
15  *
16  * Runs when the user is not logged in.
17  */
18 function wp_ajax_nopriv_heartbeat() {
19         $response = array();
20
21         // screen_id is the same as $current_screen->id and the JS global 'pagenow'
22         if ( ! empty($_POST['screen_id']) )
23                 $screen_id = sanitize_key($_POST['screen_id']);
24         else
25                 $screen_id = 'front';
26
27         if ( ! empty($_POST['data']) ) {
28                 $data = wp_unslash( (array) $_POST['data'] );
29
30                 /**
31                  * Filter Heartbeat AJAX response in no-privilege environments.
32                  *
33                  * @since 3.6.0
34                  *
35                  * @param array|object $response  The no-priv Heartbeat response object or array.
36                  * @param array        $data      An array of data passed via $_POST.
37                  * @param string       $screen_id The screen id.
38                  */
39                 $response = apply_filters( 'heartbeat_nopriv_received', $response, $data, $screen_id );
40         }
41
42         /**
43          * Filter Heartbeat AJAX response when no data is passed.
44          *
45          * @since 3.6.0
46          *
47          * @param array|object $response  The Heartbeat response object or array.
48          * @param string       $screen_id The screen id.
49          */
50         $response = apply_filters( 'heartbeat_nopriv_send', $response, $screen_id );
51
52         /**
53          * Fires when Heartbeat ticks in no-privilege environments.
54          *
55          * Allows the transport to be easily replaced with long-polling.
56          *
57          * @since 3.6.0
58          *
59          * @param array|object $response  The no-priv Heartbeat response.
60          * @param string       $screen_id The screen id.
61          */
62         do_action( 'heartbeat_nopriv_tick', $response, $screen_id );
63
64         // send the current time according to the server
65         $response['server_time'] = time();
66
67         wp_send_json($response);
68 }
69
70 /*
71  * GET-based Ajax handlers.
72  */
73 function wp_ajax_fetch_list() {
74         global $wp_list_table;
75
76         $list_class = $_GET['list_args']['class'];
77         check_ajax_referer( "fetch-list-$list_class", '_ajax_fetch_list_nonce' );
78
79         $wp_list_table = _get_list_table( $list_class, array( 'screen' => $_GET['list_args']['screen']['id'] ) );
80         if ( ! $wp_list_table )
81                 wp_die( 0 );
82
83         if ( ! $wp_list_table->ajax_user_can() )
84                 wp_die( -1 );
85
86         $wp_list_table->ajax_response();
87
88         wp_die( 0 );
89 }
90 function wp_ajax_ajax_tag_search() {
91         global $wpdb;
92
93         if ( isset( $_GET['tax'] ) ) {
94                 $taxonomy = sanitize_key( $_GET['tax'] );
95                 $tax = get_taxonomy( $taxonomy );
96                 if ( ! $tax )
97                         wp_die( 0 );
98                 if ( ! current_user_can( $tax->cap->assign_terms ) )
99                         wp_die( -1 );
100         } else {
101                 wp_die( 0 );
102         }
103
104         $s = wp_unslash( $_GET['q'] );
105
106         $comma = _x( ',', 'tag delimiter' );
107         if ( ',' !== $comma )
108                 $s = str_replace( $comma, ',', $s );
109         if ( false !== strpos( $s, ',' ) ) {
110                 $s = explode( ',', $s );
111                 $s = $s[count( $s ) - 1];
112         }
113         $s = trim( $s );
114         if ( strlen( $s ) < 2 )
115                 wp_die(); // require 2 chars for matching
116
117         $results = get_terms( $taxonomy, array( 'name__like' => $s, 'fields' => 'names', 'hide_empty' => false ) );
118
119         echo join( $results, "\n" );
120         wp_die();
121 }
122
123 function wp_ajax_wp_compression_test() {
124         if ( !current_user_can( 'manage_options' ) )
125                 wp_die( -1 );
126
127         if ( ini_get('zlib.output_compression') || 'ob_gzhandler' == ini_get('output_handler') ) {
128                 update_site_option('can_compress_scripts', 0);
129                 wp_die( 0 );
130         }
131
132         if ( isset($_GET['test']) ) {
133                 header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' );
134                 header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
135                 header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
136                 header( 'Pragma: no-cache' );
137                 header('Content-Type: application/x-javascript; charset=UTF-8');
138                 $force_gzip = ( defined('ENFORCE_GZIP') && ENFORCE_GZIP );
139                 $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."';
140
141                  if ( 1 == $_GET['test'] ) {
142                         echo $test_str;
143                         wp_die();
144                  } elseif ( 2 == $_GET['test'] ) {
145                         if ( !isset($_SERVER['HTTP_ACCEPT_ENCODING']) )
146                                 wp_die( -1 );
147                         if ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') && function_exists('gzdeflate') && ! $force_gzip ) {
148                                 header('Content-Encoding: deflate');
149                                 $out = gzdeflate( $test_str, 1 );
150                         } elseif ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') && function_exists('gzencode') ) {
151                                 header('Content-Encoding: gzip');
152                                 $out = gzencode( $test_str, 1 );
153                         } else {
154                                 wp_die( -1 );
155                         }
156                         echo $out;
157                         wp_die();
158                 } elseif ( 'no' == $_GET['test'] ) {
159                         update_site_option('can_compress_scripts', 0);
160                 } elseif ( 'yes' == $_GET['test'] ) {
161                         update_site_option('can_compress_scripts', 1);
162                 }
163         }
164
165         wp_die( 0 );
166 }
167
168 function wp_ajax_imgedit_preview() {
169         $post_id = intval($_GET['postid']);
170         if ( empty($post_id) || !current_user_can('edit_post', $post_id) )
171                 wp_die( -1 );
172
173         check_ajax_referer( "image_editor-$post_id" );
174
175         include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
176         if ( ! stream_preview_image($post_id) )
177                 wp_die( -1 );
178
179         wp_die();
180 }
181
182 function wp_ajax_oembed_cache() {
183         global $wp_embed;
184
185         $return = ( $wp_embed->cache_oembed( $_GET['post'] ) ) ? '1' : '0';
186         wp_die( $return );
187 }
188
189 function wp_ajax_autocomplete_user() {
190         if ( ! is_multisite() || ! current_user_can( 'promote_users' ) || wp_is_large_network( 'users' ) )
191                 wp_die( -1 );
192
193         /** This filter is documented in wp-admin/user-new.php */
194         if ( ! is_super_admin() && ! apply_filters( 'autocomplete_users_for_site_admins', false ) )
195                 wp_die( -1 );
196
197         $return = array();
198
199         // Check the type of request
200         if ( isset( $_REQUEST['autocomplete_type'] ) )
201                 $type = $_REQUEST['autocomplete_type'];
202         else
203                 $type = 'add';
204
205         // Exclude current users of this blog
206         if ( isset( $_REQUEST['site_id'] ) )
207                 $id = absint( $_REQUEST['site_id'] );
208         else
209                 $id = get_current_blog_id();
210
211         $include_blog_users = ( $type == 'search' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
212         $exclude_blog_users = ( $type == 'add' ? get_users( array( 'blog_id' => $id, 'fields' => 'ID' ) ) : array() );
213
214         $users = get_users( array(
215                 'blog_id' => false,
216                 'search'  => '*' . $_REQUEST['term'] . '*',
217                 'include' => $include_blog_users,
218                 'exclude' => $exclude_blog_users,
219                 'search_columns' => array( 'user_login', 'user_nicename', 'user_email' ),
220         ) );
221
222         foreach ( $users as $user ) {
223                 $return[] = array(
224                         /* translators: 1: user_login, 2: user_email */
225                         'label' => sprintf( __( '%1$s (%2$s)' ), $user->user_login, $user->user_email ),
226                         'value' => $user->user_login,
227                 );
228         }
229
230         wp_die( json_encode( $return ) );
231 }
232
233 function wp_ajax_dashboard_widgets() {
234         require_once ABSPATH . 'wp-admin/includes/dashboard.php';
235
236         $pagenow = $_GET['pagenow'];
237         if ( $pagenow === 'dashboard-user' || $pagenow === 'dashboard-network' || $pagenow === 'dashboard' ) {
238                 set_current_screen( $pagenow );
239         }
240
241         switch ( $_GET['widget'] ) {
242                 case 'dashboard_primary' :
243                         wp_dashboard_primary();
244                         break;
245         }
246         wp_die();
247 }
248
249 function wp_ajax_logged_in() {
250         wp_die( 1 );
251 }
252
253 /*
254  * Ajax helper.
255  */
256
257 /**
258  * Sends back current comment total and new page links if they need to be updated.
259  *
260  * Contrary to normal success AJAX response ("1"), die with time() on success.
261  *
262  * @since 2.7
263  *
264  * @param int $comment_id
265  * @return die
266  */
267 function _wp_ajax_delete_comment_response( $comment_id, $delta = -1 ) {
268         $total    = isset( $_POST['_total'] )    ? (int) $_POST['_total']    : 0;
269         $per_page = isset( $_POST['_per_page'] ) ? (int) $_POST['_per_page'] : 0;
270         $page     = isset( $_POST['_page'] )     ? (int) $_POST['_page']     : 0;
271         $url      = isset( $_POST['_url'] )      ? esc_url_raw( $_POST['_url'] ) : '';
272
273         // JS didn't send us everything we need to know. Just die with success message
274         if ( !$total || !$per_page || !$page || !$url )
275                 wp_die( time() );
276
277         $total += $delta;
278         if ( $total < 0 )
279                 $total = 0;
280
281         // Only do the expensive stuff on a page-break, and about 1 other time per page
282         if ( 0 == $total % $per_page || 1 == mt_rand( 1, $per_page ) ) {
283                 $post_id = 0;
284                 $status = 'total_comments'; // What type of comment count are we looking for?
285                 $parsed = parse_url( $url );
286                 if ( isset( $parsed['query'] ) ) {
287                         parse_str( $parsed['query'], $query_vars );
288                         if ( !empty( $query_vars['comment_status'] ) )
289                                 $status = $query_vars['comment_status'];
290                         if ( !empty( $query_vars['p'] ) )
291                                 $post_id = (int) $query_vars['p'];
292                 }
293
294                 $comment_count = wp_count_comments($post_id);
295
296                 if ( isset( $comment_count->$status ) ) // We're looking for a known type of comment count
297                         $total = $comment_count->$status;
298                         // else use the decremented value from above
299         }
300
301         $time = time(); // The time since the last comment count
302
303         $x = new WP_Ajax_Response( array(
304                 'what' => 'comment',
305                 'id' => $comment_id, // here for completeness - not used
306                 'supplemental' => array(
307                         'total_items_i18n' => sprintf( _n( '1 item', '%s items', $total ), number_format_i18n( $total ) ),
308                         'total_pages' => ceil( $total / $per_page ),
309                         'total_pages_i18n' => number_format_i18n( ceil( $total / $per_page ) ),
310                         'total' => $total,
311                         'time' => $time
312                 )
313         ) );
314         $x->send();
315 }
316
317 /*
318  * POST-based Ajax handlers.
319  */
320
321 function _wp_ajax_add_hierarchical_term() {
322         $action = $_POST['action'];
323         $taxonomy = get_taxonomy(substr($action, 4));
324         check_ajax_referer( $action, '_ajax_nonce-add-' . $taxonomy->name );
325         if ( !current_user_can( $taxonomy->cap->edit_terms ) )
326                 wp_die( -1 );
327         $names = explode(',', $_POST['new'.$taxonomy->name]);
328         $parent = isset($_POST['new'.$taxonomy->name.'_parent']) ? (int) $_POST['new'.$taxonomy->name.'_parent'] : 0;
329         if ( 0 > $parent )
330                 $parent = 0;
331         if ( $taxonomy->name == 'category' )
332                 $post_category = isset($_POST['post_category']) ? (array) $_POST['post_category'] : array();
333         else
334                 $post_category = ( isset($_POST['tax_input']) && isset($_POST['tax_input'][$taxonomy->name]) ) ? (array) $_POST['tax_input'][$taxonomy->name] : array();
335         $checked_categories = array_map( 'absint', (array) $post_category );
336         $popular_ids = wp_popular_terms_checklist($taxonomy->name, 0, 10, false);
337
338         foreach ( $names as $cat_name ) {
339                 $cat_name = trim($cat_name);
340                 $category_nicename = sanitize_title($cat_name);
341                 if ( '' === $category_nicename )
342                         continue;
343                 if ( !$cat_id = term_exists( $cat_name, $taxonomy->name, $parent ) )
344                         $cat_id = wp_insert_term( $cat_name, $taxonomy->name, array( 'parent' => $parent ) );
345                 if ( is_wp_error( $cat_id ) )
346                         continue;
347                 else if ( is_array( $cat_id ) )
348                         $cat_id = $cat_id['term_id'];
349                 $checked_categories[] = $cat_id;
350                 if ( $parent ) // Do these all at once in a second
351                         continue;
352                 ob_start();
353                         wp_terms_checklist( 0, array( 'taxonomy' => $taxonomy->name, 'descendants_and_self' => $cat_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids ));
354                 $data = ob_get_contents();
355                 ob_end_clean();
356                 $add = array(
357                         'what' => $taxonomy->name,
358                         'id' => $cat_id,
359                         'data' => str_replace( array("\n", "\t"), '', $data),
360                         'position' => -1
361                 );
362         }
363
364         if ( $parent ) { // Foncy - replace the parent and all its children
365                 $parent = get_term( $parent, $taxonomy->name );
366                 $term_id = $parent->term_id;
367
368                 while ( $parent->parent ) { // get the top parent
369                         $parent = get_term( $parent->parent, $taxonomy->name );
370                         if ( is_wp_error( $parent ) )
371                                 break;
372                         $term_id = $parent->term_id;
373                 }
374
375                 ob_start();
376                         wp_terms_checklist( 0, array('taxonomy' => $taxonomy->name, 'descendants_and_self' => $term_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids));
377                 $data = ob_get_contents();
378                 ob_end_clean();
379                 $add = array(
380                         'what' => $taxonomy->name,
381                         'id' => $term_id,
382                         'data' => str_replace( array("\n", "\t"), '', $data),
383                         'position' => -1
384                 );
385         }
386
387         ob_start();
388                 wp_dropdown_categories( array(
389                         'taxonomy' => $taxonomy->name, 'hide_empty' => 0, 'name' => 'new'.$taxonomy->name.'_parent', 'orderby' => 'name',
390                         'hierarchical' => 1, 'show_option_none' => '&mdash; '.$taxonomy->labels->parent_item.' &mdash;'
391                 ) );
392         $sup = ob_get_contents();
393         ob_end_clean();
394         $add['supplemental'] = array( 'newcat_parent' => $sup );
395
396         $x = new WP_Ajax_Response( $add );
397         $x->send();
398 }
399
400 function wp_ajax_delete_comment() {
401         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
402
403         if ( !$comment = get_comment( $id ) )
404                 wp_die( time() );
405         if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
406                 wp_die( -1 );
407
408         check_ajax_referer( "delete-comment_$id" );
409         $status = wp_get_comment_status( $comment->comment_ID );
410
411         $delta = -1;
412         if ( isset($_POST['trash']) && 1 == $_POST['trash'] ) {
413                 if ( 'trash' == $status )
414                         wp_die( time() );
415                 $r = wp_trash_comment( $comment->comment_ID );
416         } elseif ( isset($_POST['untrash']) && 1 == $_POST['untrash'] ) {
417                 if ( 'trash' != $status )
418                         wp_die( time() );
419                 $r = wp_untrash_comment( $comment->comment_ID );
420                 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'trash' ) // undo trash, not in trash
421                         $delta = 1;
422         } elseif ( isset($_POST['spam']) && 1 == $_POST['spam'] ) {
423                 if ( 'spam' == $status )
424                         wp_die( time() );
425                 $r = wp_spam_comment( $comment->comment_ID );
426         } elseif ( isset($_POST['unspam']) && 1 == $_POST['unspam'] ) {
427                 if ( 'spam' != $status )
428                         wp_die( time() );
429                 $r = wp_unspam_comment( $comment->comment_ID );
430                 if ( ! isset( $_POST['comment_status'] ) || $_POST['comment_status'] != 'spam' ) // undo spam, not in spam
431                         $delta = 1;
432         } elseif ( isset($_POST['delete']) && 1 == $_POST['delete'] ) {
433                 $r = wp_delete_comment( $comment->comment_ID );
434         } else {
435                 wp_die( -1 );
436         }
437
438         if ( $r ) // Decide if we need to send back '1' or a more complicated response including page links and comment counts
439                 _wp_ajax_delete_comment_response( $comment->comment_ID, $delta );
440         wp_die( 0 );
441 }
442
443 function wp_ajax_delete_tag() {
444         $tag_id = (int) $_POST['tag_ID'];
445         check_ajax_referer( "delete-tag_$tag_id" );
446
447         $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
448         $tax = get_taxonomy($taxonomy);
449
450         if ( !current_user_can( $tax->cap->delete_terms ) )
451                 wp_die( -1 );
452
453         $tag = get_term( $tag_id, $taxonomy );
454         if ( !$tag || is_wp_error( $tag ) )
455                 wp_die( 1 );
456
457         if ( wp_delete_term($tag_id, $taxonomy))
458                 wp_die( 1 );
459         else
460                 wp_die( 0 );
461 }
462
463 function wp_ajax_delete_link() {
464         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
465
466         check_ajax_referer( "delete-bookmark_$id" );
467         if ( !current_user_can( 'manage_links' ) )
468                 wp_die( -1 );
469
470         $link = get_bookmark( $id );
471         if ( !$link || is_wp_error( $link ) )
472                 wp_die( 1 );
473
474         if ( wp_delete_link( $id ) )
475                 wp_die( 1 );
476         else
477                 wp_die( 0 );
478 }
479
480 function wp_ajax_delete_meta() {
481         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
482
483         check_ajax_referer( "delete-meta_$id" );
484         if ( !$meta = get_metadata_by_mid( 'post', $id ) )
485                 wp_die( 1 );
486
487         if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta',  $meta->post_id, $meta->meta_key ) )
488                 wp_die( -1 );
489         if ( delete_meta( $meta->meta_id ) )
490                 wp_die( 1 );
491         wp_die( 0 );
492 }
493
494 function wp_ajax_delete_post( $action ) {
495         if ( empty( $action ) )
496                 $action = 'delete-post';
497         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
498
499         check_ajax_referer( "{$action}_$id" );
500         if ( !current_user_can( 'delete_post', $id ) )
501                 wp_die( -1 );
502
503         if ( !get_post( $id ) )
504                 wp_die( 1 );
505
506         if ( wp_delete_post( $id ) )
507                 wp_die( 1 );
508         else
509                 wp_die( 0 );
510 }
511
512 function wp_ajax_trash_post( $action ) {
513         if ( empty( $action ) )
514                 $action = 'trash-post';
515         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
516
517         check_ajax_referer( "{$action}_$id" );
518         if ( !current_user_can( 'delete_post', $id ) )
519                 wp_die( -1 );
520
521         if ( !get_post( $id ) )
522                 wp_die( 1 );
523
524         if ( 'trash-post' == $action )
525                 $done = wp_trash_post( $id );
526         else
527                 $done = wp_untrash_post( $id );
528
529         if ( $done )
530                 wp_die( 1 );
531
532         wp_die( 0 );
533 }
534
535 function wp_ajax_untrash_post( $action ) {
536         if ( empty( $action ) )
537                 $action = 'untrash-post';
538         wp_ajax_trash_post( $action );
539 }
540
541 function wp_ajax_delete_page( $action ) {
542         if ( empty( $action ) )
543                 $action = 'delete-page';
544         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
545
546         check_ajax_referer( "{$action}_$id" );
547         if ( !current_user_can( 'delete_page', $id ) )
548                 wp_die( -1 );
549
550         if ( ! get_post( $id ) )
551                 wp_die( 1 );
552
553         if ( wp_delete_post( $id ) )
554                 wp_die( 1 );
555         else
556                 wp_die( 0 );
557 }
558
559 function wp_ajax_dim_comment() {
560         $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0;
561
562         if ( !$comment = get_comment( $id ) ) {
563                 $x = new WP_Ajax_Response( array(
564                         'what' => 'comment',
565                         'id' => new WP_Error('invalid_comment', sprintf(__('Comment %d does not exist'), $id))
566                 ) );
567                 $x->send();
568         }
569
570         if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) && ! current_user_can( 'moderate_comments' ) )
571                 wp_die( -1 );
572
573         $current = wp_get_comment_status( $comment->comment_ID );
574         if ( isset( $_POST['new'] ) && $_POST['new'] == $current )
575                 wp_die( time() );
576
577         check_ajax_referer( "approve-comment_$id" );
578         if ( in_array( $current, array( 'unapproved', 'spam' ) ) )
579                 $result = wp_set_comment_status( $comment->comment_ID, 'approve', true );
580         else
581                 $result = wp_set_comment_status( $comment->comment_ID, 'hold', true );
582
583         if ( is_wp_error($result) ) {
584                 $x = new WP_Ajax_Response( array(
585                         'what' => 'comment',
586                         'id' => $result
587                 ) );
588                 $x->send();
589         }
590
591         // Decide if we need to send back '1' or a more complicated response including page links and comment counts
592         _wp_ajax_delete_comment_response( $comment->comment_ID );
593         wp_die( 0 );
594 }
595
596 function wp_ajax_add_link_category( $action ) {
597         if ( empty( $action ) )
598                 $action = 'add-link-category';
599         check_ajax_referer( $action );
600         if ( !current_user_can( 'manage_categories' ) )
601                 wp_die( -1 );
602         $names = explode(',', wp_unslash( $_POST['newcat'] ) );
603         $x = new WP_Ajax_Response();
604         foreach ( $names as $cat_name ) {
605                 $cat_name = trim($cat_name);
606                 $slug = sanitize_title($cat_name);
607                 if ( '' === $slug )
608                         continue;
609                 if ( !$cat_id = term_exists( $cat_name, 'link_category' ) )
610                         $cat_id = wp_insert_term( $cat_name, 'link_category' );
611                 if ( is_wp_error( $cat_id ) )
612                         continue;
613                 else if ( is_array( $cat_id ) )
614                         $cat_id = $cat_id['term_id'];
615                 $cat_name = esc_html( $cat_name );
616                 $x->add( array(
617                         'what' => 'link-category',
618                         'id' => $cat_id,
619                         '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>",
620                         'position' => -1
621                 ) );
622         }
623         $x->send();
624 }
625
626 function wp_ajax_add_tag() {
627         global $wp_list_table;
628
629         check_ajax_referer( 'add-tag', '_wpnonce_add-tag' );
630         $post_type = !empty($_POST['post_type']) ? $_POST['post_type'] : 'post';
631         $taxonomy = !empty($_POST['taxonomy']) ? $_POST['taxonomy'] : 'post_tag';
632         $tax = get_taxonomy($taxonomy);
633
634         if ( !current_user_can( $tax->cap->edit_terms ) )
635                 wp_die( -1 );
636
637         $x = new WP_Ajax_Response();
638
639         $tag = wp_insert_term($_POST['tag-name'], $taxonomy, $_POST );
640
641         if ( !$tag || is_wp_error($tag) || (!$tag = get_term( $tag['term_id'], $taxonomy )) ) {
642                 $message = __('An error has occurred. Please reload the page and try again.');
643                 if ( is_wp_error($tag) && $tag->get_error_message() )
644                         $message = $tag->get_error_message();
645
646                 $x->add( array(
647                         'what' => 'taxonomy',
648                         'data' => new WP_Error('error', $message )
649                 ) );
650                 $x->send();
651         }
652
653         $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => $_POST['screen'] ) );
654
655         $level = 0;
656         if ( is_taxonomy_hierarchical($taxonomy) ) {
657                 $level = count( get_ancestors( $tag->term_id, $taxonomy ) );
658                 ob_start();
659                 $wp_list_table->single_row( $tag, $level );
660                 $noparents = ob_get_clean();
661         }
662
663         ob_start();
664         $wp_list_table->single_row( $tag );
665         $parents = ob_get_clean();
666
667         $x->add( array(
668                 'what' => 'taxonomy',
669                 'supplemental' => compact('parents', 'noparents')
670                 ) );
671         $x->add( array(
672                 'what' => 'term',
673                 'position' => $level,
674                 'supplemental' => (array) $tag
675                 ) );
676         $x->send();
677 }
678
679 function wp_ajax_get_tagcloud() {
680         if ( isset( $_POST['tax'] ) ) {
681                 $taxonomy = sanitize_key( $_POST['tax'] );
682                 $tax = get_taxonomy( $taxonomy );
683                 if ( ! $tax )
684                         wp_die( 0 );
685                 if ( ! current_user_can( $tax->cap->assign_terms ) )
686                         wp_die( -1 );
687         } else {
688                 wp_die( 0 );
689         }
690
691         $tags = get_terms( $taxonomy, array( 'number' => 45, 'orderby' => 'count', 'order' => 'DESC' ) );
692
693         if ( empty( $tags ) )
694                 wp_die( $tax->labels->not_found );
695
696         if ( is_wp_error( $tags ) )
697                 wp_die( $tags->get_error_message() );
698
699         foreach ( $tags as $key => $tag ) {
700                 $tags[ $key ]->link = '#';
701                 $tags[ $key ]->id = $tag->term_id;
702         }
703
704         // We need raw tag names here, so don't filter the output
705         $return = wp_generate_tag_cloud( $tags, array('filter' => 0) );
706
707         if ( empty($return) )
708                 wp_die( 0 );
709
710         echo $return;
711
712         wp_die();
713 }
714
715 function wp_ajax_get_comments( $action ) {
716         global $wp_list_table, $post_id;
717         if ( empty( $action ) )
718                 $action = 'get-comments';
719
720         check_ajax_referer( $action );
721
722         if ( empty( $post_id ) && ! empty( $_REQUEST['p'] ) ) {
723                 $id = absint( $_REQUEST['p'] );
724                 if ( ! empty( $id ) )
725                         $post_id = $id;
726         }
727
728         if ( empty( $post_id ) )
729                 wp_die( -1 );
730
731         $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
732
733         if ( ! current_user_can( 'edit_post', $post_id ) )
734                 wp_die( -1 );
735
736         $wp_list_table->prepare_items();
737
738         if ( !$wp_list_table->has_items() )
739                 wp_die( 1 );
740
741         $x = new WP_Ajax_Response();
742         ob_start();
743         foreach ( $wp_list_table->items as $comment ) {
744                 if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) )
745                         continue;
746                 get_comment( $comment );
747                 $wp_list_table->single_row( $comment );
748         }
749         $comment_list_item = ob_get_contents();
750         ob_end_clean();
751
752         $x->add( array(
753                 'what' => 'comments',
754                 'data' => $comment_list_item
755         ) );
756         $x->send();
757 }
758
759 function wp_ajax_replyto_comment( $action ) {
760         global $wp_list_table, $wpdb;
761         if ( empty( $action ) )
762                 $action = 'replyto-comment';
763
764         check_ajax_referer( $action, '_ajax_nonce-replyto-comment' );
765
766         $comment_post_ID = (int) $_POST['comment_post_ID'];
767         $post = get_post( $comment_post_ID );
768         if ( ! $post )
769                 wp_die( -1 );
770
771         if ( !current_user_can( 'edit_post', $comment_post_ID ) )
772                 wp_die( -1 );
773
774         if ( empty( $post->post_status ) )
775                 wp_die( 1 );
776         elseif ( in_array($post->post_status, array('draft', 'pending', 'trash') ) )
777                 wp_die( __('ERROR: you are replying to a comment on a draft post.') );
778
779         $user = wp_get_current_user();
780         if ( $user->exists() ) {
781                 $user_ID = $user->ID;
782                 $comment_author       = wp_slash( $user->display_name );
783                 $comment_author_email = wp_slash( $user->user_email );
784                 $comment_author_url   = wp_slash( $user->user_url );
785                 $comment_content      = trim($_POST['content']);
786                 if ( current_user_can( 'unfiltered_html' ) ) {
787                         if ( ! isset( $_POST['_wp_unfiltered_html_comment'] ) )
788                                 $_POST['_wp_unfiltered_html_comment'] = '';
789
790                         if ( wp_create_nonce( 'unfiltered-html-comment' ) != $_POST['_wp_unfiltered_html_comment'] ) {
791                                 kses_remove_filters(); // start with a clean slate
792                                 kses_init_filters(); // set up the filters
793                         }
794                 }
795         } else {
796                 wp_die( __( 'Sorry, you must be logged in to reply to a comment.' ) );
797         }
798
799         if ( '' == $comment_content )
800                 wp_die( __( 'ERROR: please type a comment.' ) );
801
802         $comment_parent = 0;
803         if ( isset( $_POST['comment_ID'] ) )
804                 $comment_parent = absint( $_POST['comment_ID'] );
805         $comment_auto_approved = false;
806         $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID');
807
808         // automatically approve parent comment
809         if ( !empty($_POST['approve_parent']) ) {
810                 $parent = get_comment( $comment_parent );
811
812                 if ( $parent && $parent->comment_approved === '0' && $parent->comment_post_ID == $comment_post_ID ) {
813                         if ( wp_set_comment_status( $parent->comment_ID, 'approve' ) )
814                                 $comment_auto_approved = true;
815                 }
816         }
817
818         $comment_id = wp_new_comment( $commentdata );
819         $comment = get_comment($comment_id);
820         if ( ! $comment ) wp_die( 1 );
821
822         $position = ( isset($_POST['position']) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1';
823
824         ob_start();
825         if ( isset( $_REQUEST['mode'] ) && 'dashboard' == $_REQUEST['mode'] ) {
826                 require_once( ABSPATH . 'wp-admin/includes/dashboard.php' );
827                 _wp_dashboard_recent_comments_row( $comment );
828         } else {
829                 if ( isset( $_REQUEST['mode'] ) && 'single' == $_REQUEST['mode'] ) {
830                         $wp_list_table = _get_list_table('WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
831                 } else {
832                         $wp_list_table = _get_list_table('WP_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
833                 }
834                 $wp_list_table->single_row( $comment );
835         }
836         $comment_list_item = ob_get_clean();
837
838         $response =  array(
839                 'what' => 'comment',
840                 'id' => $comment->comment_ID,
841                 'data' => $comment_list_item,
842                 'position' => $position
843         );
844
845         if ( $comment_auto_approved )
846                 $response['supplemental'] = array( 'parent_approved' => $parent->comment_ID );
847
848         $x = new WP_Ajax_Response();
849         $x->add( $response );
850         $x->send();
851 }
852
853 function wp_ajax_edit_comment() {
854         global $wp_list_table;
855
856         check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' );
857
858         $comment_id = (int) $_POST['comment_ID'];
859         if ( ! current_user_can( 'edit_comment', $comment_id ) )
860                 wp_die( -1 );
861
862         if ( '' == $_POST['content'] )
863                 wp_die( __( 'ERROR: please type a comment.' ) );
864
865         if ( isset( $_POST['status'] ) )
866                 $_POST['comment_status'] = $_POST['status'];
867         edit_comment();
868
869         $position = ( isset($_POST['position']) && (int) $_POST['position']) ? (int) $_POST['position'] : '-1';
870         $comments_status = isset($_POST['comments_listing']) ? $_POST['comments_listing'] : '';
871
872         $checkbox = ( isset($_POST['checkbox']) && true == $_POST['checkbox'] ) ? 1 : 0;
873         $wp_list_table = _get_list_table( $checkbox ? 'WP_Comments_List_Table' : 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) );
874
875         $comment = get_comment( $comment_id );
876         if ( empty( $comment->comment_ID ) )
877                 wp_die( -1 );
878
879         ob_start();
880         $wp_list_table->single_row( $comment );
881         $comment_list_item = ob_get_clean();
882
883         $x = new WP_Ajax_Response();
884
885         $x->add( array(
886                 'what' => 'edit_comment',
887                 'id' => $comment->comment_ID,
888                 'data' => $comment_list_item,
889                 'position' => $position
890         ));
891
892         $x->send();
893 }
894
895 function wp_ajax_add_menu_item() {
896         check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
897
898         if ( ! current_user_can( 'edit_theme_options' ) )
899                 wp_die( -1 );
900
901         require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
902
903         // For performance reasons, we omit some object properties from the checklist.
904         // The following is a hacky way to restore them when adding non-custom items.
905
906         $menu_items_data = array();
907         foreach ( (array) $_POST['menu-item'] as $menu_item_data ) {
908                 if (
909                         ! empty( $menu_item_data['menu-item-type'] ) &&
910                         'custom' != $menu_item_data['menu-item-type'] &&
911                         ! empty( $menu_item_data['menu-item-object-id'] )
912                 ) {
913                         switch( $menu_item_data['menu-item-type'] ) {
914                                 case 'post_type' :
915                                         $_object = get_post( $menu_item_data['menu-item-object-id'] );
916                                 break;
917
918                                 case 'taxonomy' :
919                                         $_object = get_term( $menu_item_data['menu-item-object-id'], $menu_item_data['menu-item-object'] );
920                                 break;
921                         }
922
923                         $_menu_items = array_map( 'wp_setup_nav_menu_item', array( $_object ) );
924                         $_menu_item = array_shift( $_menu_items );
925
926                         // Restore the missing menu item properties
927                         $menu_item_data['menu-item-description'] = $_menu_item->description;
928                 }
929
930                 $menu_items_data[] = $menu_item_data;
931         }
932
933         $item_ids = wp_save_nav_menu_items( 0, $menu_items_data );
934         if ( is_wp_error( $item_ids ) )
935                 wp_die( 0 );
936
937         $menu_items = array();
938
939         foreach ( (array) $item_ids as $menu_item_id ) {
940                 $menu_obj = get_post( $menu_item_id );
941                 if ( ! empty( $menu_obj->ID ) ) {
942                         $menu_obj = wp_setup_nav_menu_item( $menu_obj );
943                         $menu_obj->label = $menu_obj->title; // don't show "(pending)" in ajax-added items
944                         $menu_items[] = $menu_obj;
945                 }
946         }
947
948         /**
949          * Filter the Walker class used when adding nav menu items.
950          *
951          * @since 3.4.0
952          *
953          * @param string $class   The walker class to use. Default 'Walker_Nav_Menu_Edit'.
954          * @param int    $menu_id The menu id, derived from $_POST['menu'].
955          */
956         $walker_class_name = apply_filters( 'wp_edit_nav_menu_walker', 'Walker_Nav_Menu_Edit', $_POST['menu'] );
957
958         if ( ! class_exists( $walker_class_name ) )
959                 wp_die( 0 );
960
961         if ( ! empty( $menu_items ) ) {
962                 $args = array(
963                         'after' => '',
964                         'before' => '',
965                         'link_after' => '',
966                         'link_before' => '',
967                         'walker' => new $walker_class_name,
968                 );
969                 echo walk_nav_menu_tree( $menu_items, 0, (object) $args );
970         }
971         wp_die();
972 }
973
974 function wp_ajax_add_meta() {
975         check_ajax_referer( 'add-meta', '_ajax_nonce-add-meta' );
976         $c = 0;
977         $pid = (int) $_POST['post_id'];
978         $post = get_post( $pid );
979
980         if ( isset($_POST['metakeyselect']) || isset($_POST['metakeyinput']) ) {
981                 if ( !current_user_can( 'edit_post', $pid ) )
982                         wp_die( -1 );
983                 if ( isset($_POST['metakeyselect']) && '#NONE#' == $_POST['metakeyselect'] && empty($_POST['metakeyinput']) )
984                         wp_die( 1 );
985                 if ( $post->post_status == 'auto-draft' ) {
986                         $save_POST = $_POST; // Backup $_POST
987                         $_POST = array(); // Make it empty for edit_post()
988                         $_POST['action'] = 'draft'; // Warning fix
989                         $_POST['post_ID'] = $pid;
990                         $_POST['post_type'] = $post->post_type;
991                         $_POST['post_status'] = 'draft';
992                         $now = current_time('timestamp', 1);
993                         $_POST['post_title'] = sprintf( __( 'Draft created on %1$s at %2$s' ), date( get_option( 'date_format' ), $now ), date( get_option( 'time_format' ), $now ) );
994
995                         if ( $pid = edit_post() ) {
996                                 if ( is_wp_error( $pid ) ) {
997                                         $x = new WP_Ajax_Response( array(
998                                                 'what' => 'meta',
999                                                 'data' => $pid
1000                                         ) );
1001                                         $x->send();
1002                                 }
1003                                 $_POST = $save_POST; // Now we can restore original $_POST again
1004                                 if ( !$mid = add_meta( $pid ) )
1005                                         wp_die( __( 'Please provide a custom field value.' ) );
1006                         } else {
1007                                 wp_die( 0 );
1008                         }
1009                 } else if ( !$mid = add_meta( $pid ) ) {
1010                         wp_die( __( 'Please provide a custom field value.' ) );
1011                 }
1012
1013                 $meta = get_metadata_by_mid( 'post', $mid );
1014                 $pid = (int) $meta->post_id;
1015                 $meta = get_object_vars( $meta );
1016                 $x = new WP_Ajax_Response( array(
1017                         'what' => 'meta',
1018                         'id' => $mid,
1019                         'data' => _list_meta_row( $meta, $c ),
1020                         'position' => 1,
1021                         'supplemental' => array('postid' => $pid)
1022                 ) );
1023         } else { // Update?
1024                 $mid = (int) key( $_POST['meta'] );
1025                 $key = wp_unslash( $_POST['meta'][$mid]['key'] );
1026                 $value = wp_unslash( $_POST['meta'][$mid]['value'] );
1027                 if ( '' == trim($key) )
1028                         wp_die( __( 'Please provide a custom field name.' ) );
1029                 if ( '' == trim($value) )
1030                         wp_die( __( 'Please provide a custom field value.' ) );
1031                 if ( ! $meta = get_metadata_by_mid( 'post', $mid ) )
1032                         wp_die( 0 ); // if meta doesn't exist
1033                 if ( is_protected_meta( $meta->meta_key, 'post' ) || is_protected_meta( $key, 'post' ) ||
1034                         ! current_user_can( 'edit_post_meta', $meta->post_id, $meta->meta_key ) ||
1035                         ! current_user_can( 'edit_post_meta', $meta->post_id, $key ) )
1036                         wp_die( -1 );
1037                 if ( $meta->meta_value != $value || $meta->meta_key != $key ) {
1038                         if ( !$u = update_metadata_by_mid( 'post', $mid, $value, $key ) )
1039                                 wp_die( 0 ); // We know meta exists; we also know it's unchanged (or DB error, in which case there are bigger problems).
1040                 }
1041
1042                 $x = new WP_Ajax_Response( array(
1043                         'what' => 'meta',
1044                         'id' => $mid, 'old_id' => $mid,
1045                         'data' => _list_meta_row( array(
1046                                 'meta_key' => $key,
1047                                 'meta_value' => $value,
1048                                 'meta_id' => $mid
1049                         ), $c ),
1050                         'position' => 0,
1051                         'supplemental' => array('postid' => $meta->post_id)
1052                 ) );
1053         }
1054         $x->send();
1055 }
1056
1057 function wp_ajax_add_user( $action ) {
1058         global $wp_list_table;
1059         if ( empty( $action ) )
1060                 $action = 'add-user';
1061
1062         check_ajax_referer( $action );
1063         if ( ! current_user_can('create_users') )
1064                 wp_die( -1 );
1065         if ( ! $user_id = edit_user() ) {
1066                 wp_die( 0 );
1067         } elseif ( is_wp_error( $user_id ) ) {
1068                 $x = new WP_Ajax_Response( array(
1069                         'what' => 'user',
1070                         'id' => $user_id
1071                 ) );
1072                 $x->send();
1073         }
1074         $user_object = get_userdata( $user_id );
1075
1076         $wp_list_table = _get_list_table('WP_Users_List_Table');
1077
1078         $role = current( $user_object->roles );
1079
1080         $x = new WP_Ajax_Response( array(
1081                 'what' => 'user',
1082                 'id' => $user_id,
1083                 'data' => $wp_list_table->single_row( $user_object, '', $role ),
1084                 'supplemental' => array(
1085                         'show-link' => sprintf(__( 'User <a href="#%s">%s</a> added' ), "user-$user_id", $user_object->user_login),
1086                         'role' => $role,
1087                 )
1088         ) );
1089         $x->send();
1090 }
1091
1092 function wp_ajax_autosave() {
1093         define( 'DOING_AUTOSAVE', true );
1094
1095         check_ajax_referer( 'autosave', 'autosavenonce' );
1096
1097         if ( ! empty( $_POST['catslist'] ) )
1098                 $_POST['post_category'] = explode( ',', $_POST['catslist'] );
1099         if ( $_POST['post_type'] == 'page' || empty( $_POST['post_category'] ) )
1100                 unset( $_POST['post_category'] );
1101
1102         $data = '';
1103         $supplemental = array();
1104         $id = $revision_id = 0;
1105
1106         $post_id = (int) $_POST['post_id'];
1107         $_POST['ID'] = $_POST['post_ID'] = $post_id;
1108         $post = get_post( $post_id );
1109         if ( empty( $post->ID ) || ! current_user_can( 'edit_post', $post->ID ) )
1110                 wp_die( __( 'You are not allowed to edit this post.' ) );
1111
1112         if ( 'page' == $post->post_type && ! current_user_can( 'edit_page', $post->ID ) )
1113                 wp_die( __( 'You are not allowed to edit this page.' ) );
1114
1115         if ( 'auto-draft' == $post->post_status )
1116                 $_POST['post_status'] = 'draft';
1117
1118         if ( ! empty( $_POST['autosave'] ) ) {
1119                 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'auto-draft' == $post->post_status || 'draft' == $post->post_status ) ) {
1120                         // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked
1121                         $id = edit_post();
1122                 } else {
1123                         // Non drafts or other users drafts are not overwritten. The autosave is stored in a special post revision for each user.
1124                         $revision_id = wp_create_post_autosave( $post->ID );
1125                         if ( is_wp_error($revision_id) )
1126                                 $id = $revision_id;
1127                         else
1128                                 $id = $post->ID;
1129                 }
1130
1131                 if ( ! is_wp_error($id) ) {
1132                         /* translators: draft saved date format, see http://php.net/date */
1133                         $draft_saved_date_format = __('g:i:s a');
1134                         /* translators: %s: date and time */
1135                         $data = sprintf( __('Draft saved at %s.'), date_i18n( $draft_saved_date_format ) );
1136                 }
1137         } else {
1138                 if ( ! empty( $_POST['auto_draft'] ) )
1139                         $id = 0; // This tells us it didn't actually save
1140                 else
1141                         $id = $post->ID;
1142         }
1143
1144         // @todo Consider exposing any errors, rather than having 'Saving draft...'
1145         $x = new WP_Ajax_Response( array(
1146                 'what' => 'autosave',
1147                 'id' => $id,
1148                 'data' => $data,
1149                 'supplemental' => $supplemental
1150         ) );
1151         $x->send();
1152 }
1153
1154 function wp_ajax_closed_postboxes() {
1155         check_ajax_referer( 'closedpostboxes', 'closedpostboxesnonce' );
1156         $closed = isset( $_POST['closed'] ) ? explode( ',', $_POST['closed']) : array();
1157         $closed = array_filter($closed);
1158
1159         $hidden = isset( $_POST['hidden'] ) ? explode( ',', $_POST['hidden']) : array();
1160         $hidden = array_filter($hidden);
1161
1162         $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1163
1164         if ( $page != sanitize_key( $page ) )
1165                 wp_die( 0 );
1166
1167         if ( ! $user = wp_get_current_user() )
1168                 wp_die( -1 );
1169
1170         if ( is_array($closed) )
1171                 update_user_option($user->ID, "closedpostboxes_$page", $closed, true);
1172
1173         if ( is_array($hidden) ) {
1174                 $hidden = array_diff( $hidden, array('submitdiv', 'linksubmitdiv', 'manage-menu', 'create-menu') ); // postboxes that are always shown
1175                 update_user_option($user->ID, "metaboxhidden_$page", $hidden, true);
1176         }
1177
1178         wp_die( 1 );
1179 }
1180
1181 function wp_ajax_hidden_columns() {
1182         check_ajax_referer( 'screen-options-nonce', 'screenoptionnonce' );
1183         $hidden = isset( $_POST['hidden'] ) ? $_POST['hidden'] : '';
1184         $hidden = explode( ',', $_POST['hidden'] );
1185         $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1186
1187         if ( $page != sanitize_key( $page ) )
1188                 wp_die( 0 );
1189
1190         if ( ! $user = wp_get_current_user() )
1191                 wp_die( -1 );
1192
1193         if ( is_array($hidden) )
1194                 update_user_option($user->ID, "manage{$page}columnshidden", $hidden, true);
1195
1196         wp_die( 1 );
1197 }
1198
1199 function wp_ajax_update_welcome_panel() {
1200         check_ajax_referer( 'welcome-panel-nonce', 'welcomepanelnonce' );
1201
1202         if ( ! current_user_can( 'edit_theme_options' ) )
1203                 wp_die( -1 );
1204
1205         update_user_meta( get_current_user_id(), 'show_welcome_panel', empty( $_POST['visible'] ) ? 0 : 1 );
1206
1207         wp_die( 1 );
1208 }
1209
1210 function wp_ajax_menu_get_metabox() {
1211         if ( ! current_user_can( 'edit_theme_options' ) )
1212                 wp_die( -1 );
1213
1214         require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1215
1216         if ( isset( $_POST['item-type'] ) && 'post_type' == $_POST['item-type'] ) {
1217                 $type = 'posttype';
1218                 $callback = 'wp_nav_menu_item_post_type_meta_box';
1219                 $items = (array) get_post_types( array( 'show_in_nav_menus' => true ), 'object' );
1220         } elseif ( isset( $_POST['item-type'] ) && 'taxonomy' == $_POST['item-type'] ) {
1221                 $type = 'taxonomy';
1222                 $callback = 'wp_nav_menu_item_taxonomy_meta_box';
1223                 $items = (array) get_taxonomies( array( 'show_ui' => true ), 'object' );
1224         }
1225
1226         if ( ! empty( $_POST['item-object'] ) && isset( $items[$_POST['item-object']] ) ) {
1227                 $menus_meta_box_object = $items[ $_POST['item-object'] ];
1228                 /**
1229                  * Filter a nav menu meta box object.
1230                  *
1231                  * @since 3.0.0
1232                  *
1233                  * @param object $menus_meta_box_object A nav menu meta box object, such as Page, Post, Category, Tag, etc.
1234                  */
1235                 $item = apply_filters( 'nav_menu_meta_box_object', $menus_meta_box_object );
1236                 ob_start();
1237                 call_user_func_array($callback, array(
1238                         null,
1239                         array(
1240                                 'id' => 'add-' . $item->name,
1241                                 'title' => $item->labels->name,
1242                                 'callback' => $callback,
1243                                 'args' => $item,
1244                         )
1245                 ));
1246
1247                 $markup = ob_get_clean();
1248
1249                 echo json_encode(array(
1250                         'replace-id' => $type . '-' . $item->name,
1251                         'markup' => $markup,
1252                 ));
1253         }
1254
1255         wp_die();
1256 }
1257
1258 function wp_ajax_wp_link_ajax() {
1259         check_ajax_referer( 'internal-linking', '_ajax_linking_nonce' );
1260
1261         $args = array();
1262
1263         if ( isset( $_POST['search'] ) )
1264                 $args['s'] = wp_unslash( $_POST['search'] );
1265         $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
1266
1267         require(ABSPATH . WPINC . '/class-wp-editor.php');
1268         $results = _WP_Editors::wp_link_query( $args );
1269
1270         if ( ! isset( $results ) )
1271                 wp_die( 0 );
1272
1273         echo json_encode( $results );
1274         echo "\n";
1275
1276         wp_die();
1277 }
1278
1279 function wp_ajax_menu_locations_save() {
1280         if ( ! current_user_can( 'edit_theme_options' ) )
1281                 wp_die( -1 );
1282         check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' );
1283         if ( ! isset( $_POST['menu-locations'] ) )
1284                 wp_die( 0 );
1285         set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_POST['menu-locations'] ) );
1286         wp_die( 1 );
1287 }
1288
1289 function wp_ajax_meta_box_order() {
1290         check_ajax_referer( 'meta-box-order' );
1291         $order = isset( $_POST['order'] ) ? (array) $_POST['order'] : false;
1292         $page_columns = isset( $_POST['page_columns'] ) ? $_POST['page_columns'] : 'auto';
1293
1294         if ( $page_columns != 'auto' )
1295                 $page_columns = (int) $page_columns;
1296
1297         $page = isset( $_POST['page'] ) ? $_POST['page'] : '';
1298
1299         if ( $page != sanitize_key( $page ) )
1300                 wp_die( 0 );
1301
1302         if ( ! $user = wp_get_current_user() )
1303                 wp_die( -1 );
1304
1305         if ( $order )
1306                 update_user_option($user->ID, "meta-box-order_$page", $order, true);
1307
1308         if ( $page_columns )
1309                 update_user_option($user->ID, "screen_layout_$page", $page_columns, true);
1310
1311         wp_die( 1 );
1312 }
1313
1314 function wp_ajax_menu_quick_search() {
1315         if ( ! current_user_can( 'edit_theme_options' ) )
1316                 wp_die( -1 );
1317
1318         require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
1319
1320         _wp_ajax_menu_quick_search( $_POST );
1321
1322         wp_die();
1323 }
1324
1325 function wp_ajax_get_permalink() {
1326         check_ajax_referer( 'getpermalink', 'getpermalinknonce' );
1327         $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1328         wp_die( add_query_arg( array( 'preview' => 'true' ), get_permalink( $post_id ) ) );
1329 }
1330
1331 function wp_ajax_sample_permalink() {
1332         check_ajax_referer( 'samplepermalink', 'samplepermalinknonce' );
1333         $post_id = isset($_POST['post_id'])? intval($_POST['post_id']) : 0;
1334         $title = isset($_POST['new_title'])? $_POST['new_title'] : '';
1335         $slug = isset($_POST['new_slug'])? $_POST['new_slug'] : null;
1336         wp_die( get_sample_permalink_html( $post_id, $title, $slug ) );
1337 }
1338
1339 function wp_ajax_inline_save() {
1340         global $wp_list_table;
1341
1342         check_ajax_referer( 'inlineeditnonce', '_inline_edit' );
1343
1344         if ( ! isset($_POST['post_ID']) || ! ( $post_ID = (int) $_POST['post_ID'] ) )
1345                 wp_die();
1346
1347         if ( 'page' == $_POST['post_type'] ) {
1348                 if ( ! current_user_can( 'edit_page', $post_ID ) )
1349                         wp_die( __( 'You are not allowed to edit this page.' ) );
1350         } else {
1351                 if ( ! current_user_can( 'edit_post', $post_ID ) )
1352                         wp_die( __( 'You are not allowed to edit this post.' ) );
1353         }
1354
1355         if ( $last = wp_check_post_lock( $post_ID ) ) {
1356                 $last_user = get_userdata( $last );
1357                 $last_user_name = $last_user ? $last_user->display_name : __( 'Someone' );
1358                 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 ) );
1359                 wp_die();
1360         }
1361
1362         $data = &$_POST;
1363
1364         $post = get_post( $post_ID, ARRAY_A );
1365         $post = wp_slash($post); //since it is from db
1366
1367         $data['content'] = $post['post_content'];
1368         $data['excerpt'] = $post['post_excerpt'];
1369
1370         // rename
1371         $data['user_ID'] = get_current_user_id();
1372
1373         if ( isset($data['post_parent']) )
1374                 $data['parent_id'] = $data['post_parent'];
1375
1376         // status
1377         if ( isset($data['keep_private']) && 'private' == $data['keep_private'] )
1378                 $data['post_status'] = 'private';
1379         else
1380                 $data['post_status'] = $data['_status'];
1381
1382         if ( empty($data['comment_status']) )
1383                 $data['comment_status'] = 'closed';
1384         if ( empty($data['ping_status']) )
1385                 $data['ping_status'] = 'closed';
1386
1387         // Hack: wp_unique_post_slug() doesn't work for drafts, so we will fake that our post is published.
1388         if ( ! empty( $data['post_name'] ) && in_array( $post['post_status'], array( 'draft', 'pending' ) ) ) {
1389                 $post['post_status'] = 'publish';
1390                 $data['post_name'] = wp_unique_post_slug( $data['post_name'], $post['ID'], $post['post_status'], $post['post_type'], $post['post_parent'] );
1391         }
1392
1393         // update the post
1394         edit_post();
1395
1396         $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => $_POST['screen'] ) );
1397
1398         $mode = $_POST['post_view'];
1399
1400         $level = 0;
1401         $request_post = array( get_post( $_POST['post_ID'] ) );
1402         $parent = $request_post[0]->post_parent;
1403
1404         while ( $parent > 0 ) {
1405                 $parent_post = get_post( $parent );
1406                 $parent = $parent_post->post_parent;
1407                 $level++;
1408         }
1409
1410         $wp_list_table->display_rows( array( get_post( $_POST['post_ID'] ) ), $level );
1411
1412         wp_die();
1413 }
1414
1415 function wp_ajax_inline_save_tax() {
1416         global $wp_list_table;
1417
1418         check_ajax_referer( 'taxinlineeditnonce', '_inline_edit' );
1419
1420         $taxonomy = sanitize_key( $_POST['taxonomy'] );
1421         $tax = get_taxonomy( $taxonomy );
1422         if ( ! $tax )
1423                 wp_die( 0 );
1424
1425         if ( ! current_user_can( $tax->cap->edit_terms ) )
1426                 wp_die( -1 );
1427
1428         $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => 'edit-' . $taxonomy ) );
1429
1430         if ( ! isset($_POST['tax_ID']) || ! ( $id = (int) $_POST['tax_ID'] ) )
1431                 wp_die( -1 );
1432
1433         $tag = get_term( $id, $taxonomy );
1434         $_POST['description'] = $tag->description;
1435
1436         $updated = wp_update_term($id, $taxonomy, $_POST);
1437         if ( $updated && !is_wp_error($updated) ) {
1438                 $tag = get_term( $updated['term_id'], $taxonomy );
1439                 if ( !$tag || is_wp_error( $tag ) ) {
1440                         if ( is_wp_error($tag) && $tag->get_error_message() )
1441                                 wp_die( $tag->get_error_message() );
1442                         wp_die( __( 'Item not updated.' ) );
1443                 }
1444         } else {
1445                 if ( is_wp_error($updated) && $updated->get_error_message() )
1446                         wp_die( $updated->get_error_message() );
1447                 wp_die( __( 'Item not updated.' ) );
1448         }
1449         $level = 0;
1450         $parent = $tag->parent;
1451         while ( $parent > 0 ) {
1452                 $parent_tag = get_term( $parent, $taxonomy );
1453                 $parent = $parent_tag->parent;
1454                 $level++;
1455         }
1456         $wp_list_table->single_row( $tag, $level );
1457         wp_die();
1458 }
1459
1460 function wp_ajax_find_posts() {
1461         global $wpdb;
1462
1463         check_ajax_referer( 'find-posts' );
1464
1465         $post_types = get_post_types( array( 'public' => true ), 'objects' );
1466         unset( $post_types['attachment'] );
1467
1468         $s = wp_unslash( $_POST['ps'] );
1469         $searchand = $search = '';
1470         $args = array(
1471                 'post_type' => array_keys( $post_types ),
1472                 'post_status' => 'any',
1473                 'posts_per_page' => 50,
1474         );
1475         if ( '' !== $s )
1476                 $args['s'] = $s;
1477
1478         $posts = get_posts( $args );
1479
1480         if ( ! $posts )
1481                 wp_die( __('No items found.') );
1482
1483         $html = '<table class="widefat" cellspacing="0"><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>';
1484         foreach ( $posts as $post ) {
1485                 $title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
1486
1487                 switch ( $post->post_status ) {
1488                         case 'publish' :
1489                         case 'private' :
1490                                 $stat = __('Published');
1491                                 break;
1492                         case 'future' :
1493                                 $stat = __('Scheduled');
1494                                 break;
1495                         case 'pending' :
1496                                 $stat = __('Pending Review');
1497                                 break;
1498                         case 'draft' :
1499                                 $stat = __('Draft');
1500                                 break;
1501                 }
1502
1503                 if ( '0000-00-00 00:00:00' == $post->post_date ) {
1504                         $time = '';
1505                 } else {
1506                         /* translators: date format in table columns, see http://php.net/date */
1507                         $time = mysql2date(__('Y/m/d'), $post->post_date);
1508                 }
1509
1510                 $html .= '<tr class="found-posts"><td class="found-radio"><input type="radio" id="found-'.$post->ID.'" name="found_post_id" value="' . esc_attr($post->ID) . '"></td>';
1511                 $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";
1512         }
1513
1514         $html .= '</tbody></table>';
1515
1516         $x = new WP_Ajax_Response();
1517         $x->add( array(
1518                 'data' => $html
1519         ));
1520         $x->send();
1521 }
1522
1523 function wp_ajax_widgets_order() {
1524         check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1525
1526         if ( !current_user_can('edit_theme_options') )
1527                 wp_die( -1 );
1528
1529         unset( $_POST['savewidgets'], $_POST['action'] );
1530
1531         // save widgets order for all sidebars
1532         if ( is_array($_POST['sidebars']) ) {
1533                 $sidebars = array();
1534                 foreach ( $_POST['sidebars'] as $key => $val ) {
1535                         $sb = array();
1536                         if ( !empty($val) ) {
1537                                 $val = explode(',', $val);
1538                                 foreach ( $val as $k => $v ) {
1539                                         if ( strpos($v, 'widget-') === false )
1540                                                 continue;
1541
1542                                         $sb[$k] = substr($v, strpos($v, '_') + 1);
1543                                 }
1544                         }
1545                         $sidebars[$key] = $sb;
1546                 }
1547                 wp_set_sidebars_widgets($sidebars);
1548                 wp_die( 1 );
1549         }
1550
1551         wp_die( -1 );
1552 }
1553
1554 function wp_ajax_save_widget() {
1555         global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates;
1556
1557         check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
1558
1559         if ( !current_user_can('edit_theme_options') || !isset($_POST['id_base']) )
1560                 wp_die( -1 );
1561
1562         unset( $_POST['savewidgets'], $_POST['action'] );
1563
1564         /**
1565          * Fires early when editing the widgets displayed in sidebars.
1566          *
1567          * @since 2.8.0
1568          */
1569         do_action( 'load-widgets.php' );
1570
1571         /**
1572          * Fires early when editing the widgets displayed in sidebars.
1573          *
1574          * @since 2.8.0
1575          */
1576         do_action( 'widgets.php' );
1577
1578         /**
1579          * Fires early when editing the widgets displayed in sidebars.
1580          *
1581          * @since 2.2.0
1582          */
1583         do_action( 'sidebar_admin_setup' );
1584
1585         $id_base = $_POST['id_base'];
1586         $widget_id = $_POST['widget-id'];
1587         $sidebar_id = $_POST['sidebar'];
1588         $multi_number = !empty($_POST['multi_number']) ? (int) $_POST['multi_number'] : 0;
1589         $settings = isset($_POST['widget-' . $id_base]) && is_array($_POST['widget-' . $id_base]) ? $_POST['widget-' . $id_base] : false;
1590         $error = '<p>' . __('An error has occurred. Please reload the page and try again.') . '</p>';
1591
1592         $sidebars = wp_get_sidebars_widgets();
1593         $sidebar = isset($sidebars[$sidebar_id]) ? $sidebars[$sidebar_id] : array();
1594
1595         // delete
1596         if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1597
1598                 if ( !isset($wp_registered_widgets[$widget_id]) )
1599                         wp_die( $error );
1600
1601                 $sidebar = array_diff( $sidebar, array($widget_id) );
1602                 $_POST = array('sidebar' => $sidebar_id, 'widget-' . $id_base => array(), 'the-widget-id' => $widget_id, 'delete_widget' => '1');
1603         } elseif ( $settings && preg_match( '/__i__|%i%/', key($settings) ) ) {
1604                 if ( !$multi_number )
1605                         wp_die( $error );
1606
1607                 $_POST['widget-' . $id_base] = array( $multi_number => array_shift($settings) );
1608                 $widget_id = $id_base . '-' . $multi_number;
1609                 $sidebar[] = $widget_id;
1610         }
1611         $_POST['widget-id'] = $sidebar;
1612
1613         foreach ( (array) $wp_registered_widget_updates as $name => $control ) {
1614
1615                 if ( $name == $id_base ) {
1616                         if ( !is_callable( $control['callback'] ) )
1617                                 continue;
1618
1619                         ob_start();
1620                                 call_user_func_array( $control['callback'], $control['params'] );
1621                         ob_end_clean();
1622                         break;
1623                 }
1624         }
1625
1626         if ( isset($_POST['delete_widget']) && $_POST['delete_widget'] ) {
1627                 $sidebars[$sidebar_id] = $sidebar;
1628                 wp_set_sidebars_widgets($sidebars);
1629                 echo "deleted:$widget_id";
1630                 wp_die();
1631         }
1632
1633         if ( !empty($_POST['add_new']) )
1634                 wp_die();
1635
1636         if ( $form = $wp_registered_widget_controls[$widget_id] )
1637                 call_user_func_array( $form['callback'], $form['params'] );
1638
1639         wp_die();
1640 }
1641
1642 function wp_ajax_upload_attachment() {
1643         check_ajax_referer( 'media-form' );
1644
1645         if ( ! current_user_can( 'upload_files' ) )
1646                 wp_die();
1647
1648         if ( isset( $_REQUEST['post_id'] ) ) {
1649                 $post_id = $_REQUEST['post_id'];
1650                 if ( ! current_user_can( 'edit_post', $post_id ) )
1651                         wp_die();
1652         } else {
1653                 $post_id = null;
1654         }
1655
1656         $post_data = isset( $_REQUEST['post_data'] ) ? $_REQUEST['post_data'] : array();
1657
1658         // If the context is custom header or background, make sure the uploaded file is an image.
1659         if ( isset( $post_data['context'] ) && in_array( $post_data['context'], array( 'custom-header', 'custom-background' ) ) ) {
1660                 $wp_filetype = wp_check_filetype_and_ext( $_FILES['async-upload']['tmp_name'], $_FILES['async-upload']['name'], false );
1661                 if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) {
1662                         echo json_encode( array(
1663                                 'success' => false,
1664                                 'data'    => array(
1665                                         'message'  => __( 'The uploaded file is not a valid image. Please try again.' ),
1666                                         'filename' => $_FILES['async-upload']['name'],
1667                                 )
1668                         ) );
1669
1670                         wp_die();
1671                 }
1672         }
1673
1674         $attachment_id = media_handle_upload( 'async-upload', $post_id, $post_data );
1675
1676         if ( is_wp_error( $attachment_id ) ) {
1677                 echo json_encode( array(
1678                         'success' => false,
1679                         'data'    => array(
1680                                 'message'  => $attachment_id->get_error_message(),
1681                                 'filename' => $_FILES['async-upload']['name'],
1682                         )
1683                 ) );
1684
1685                 wp_die();
1686         }
1687
1688         if ( isset( $post_data['context'] ) && isset( $post_data['theme'] ) ) {
1689                 if ( 'custom-background' === $post_data['context'] )
1690                         update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', $post_data['theme'] );
1691
1692                 if ( 'custom-header' === $post_data['context'] )
1693                         update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', $post_data['theme'] );
1694         }
1695
1696         if ( ! $attachment = wp_prepare_attachment_for_js( $attachment_id ) )
1697                 wp_die();
1698
1699         echo json_encode( array(
1700                 'success' => true,
1701                 'data'    => $attachment,
1702         ) );
1703
1704         wp_die();
1705 }
1706
1707 function wp_ajax_image_editor() {
1708         $attachment_id = intval($_POST['postid']);
1709         if ( empty($attachment_id) || !current_user_can('edit_post', $attachment_id) )
1710                 wp_die( -1 );
1711
1712         check_ajax_referer( "image_editor-$attachment_id" );
1713         include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
1714
1715         $msg = false;
1716         switch ( $_POST['do'] ) {
1717                 case 'save' :
1718                         $msg = wp_save_image($attachment_id);
1719                         $msg = json_encode($msg);
1720                         wp_die( $msg );
1721                         break;
1722                 case 'scale' :
1723                         $msg = wp_save_image($attachment_id);
1724                         break;
1725                 case 'restore' :
1726                         $msg = wp_restore_image($attachment_id);
1727                         break;
1728         }
1729
1730         wp_image_editor($attachment_id, $msg);
1731         wp_die();
1732 }
1733
1734 function wp_ajax_set_post_thumbnail() {
1735         $json = ! empty( $_REQUEST['json'] ); // New-style request
1736
1737         $post_ID = intval( $_POST['post_id'] );
1738         if ( ! current_user_can( 'edit_post', $post_ID ) )
1739                 wp_die( -1 );
1740
1741         $thumbnail_id = intval( $_POST['thumbnail_id'] );
1742
1743         if ( $json )
1744                 check_ajax_referer( "update-post_$post_ID" );
1745         else
1746                 check_ajax_referer( "set_post_thumbnail-$post_ID" );
1747
1748         if ( $thumbnail_id == '-1' ) {
1749                 if ( delete_post_thumbnail( $post_ID ) ) {
1750                         $return = _wp_post_thumbnail_html( null, $post_ID );
1751                         $json ? wp_send_json_success( $return ) : wp_die( $return );
1752                 } else {
1753                         wp_die( 0 );
1754                 }
1755         }
1756
1757         if ( set_post_thumbnail( $post_ID, $thumbnail_id ) ) {
1758                 $return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID );
1759                 $json ? wp_send_json_success( $return ) : wp_die( $return );
1760         }
1761
1762         wp_die( 0 );
1763 }
1764
1765 function wp_ajax_date_format() {
1766         wp_die( date_i18n( sanitize_option( 'date_format', $_POST['date'] ) ) );
1767 }
1768
1769 function wp_ajax_time_format() {
1770         wp_die( date_i18n( sanitize_option( 'time_format', $_POST['date'] ) ) );
1771 }
1772
1773 function wp_ajax_wp_fullscreen_save_post() {
1774         $post_id = isset( $_POST['post_ID'] ) ? (int) $_POST['post_ID'] : 0;
1775
1776         $post = $post_type = null;
1777
1778         if ( $post_id )
1779                 $post = get_post( $post_id );
1780
1781         if ( $post )
1782                 $post_type = $post->post_type;
1783         elseif ( isset( $_POST['post_type'] ) && post_type_exists( $_POST['post_type'] ) )
1784                 $post_type = $_POST['post_type'];
1785
1786         check_ajax_referer('update-post_' . $post_id, '_wpnonce');
1787
1788         $post_id = edit_post();
1789
1790         if ( is_wp_error($post_id) ) {
1791                 if ( $post_id->get_error_message() )
1792                         $message = $post_id->get_error_message();
1793                 else
1794                         $message = __('Save failed');
1795
1796                 echo json_encode( array( 'message' => $message, 'last_edited' => '' ) );
1797                 wp_die();
1798         } else {
1799                 $message = __('Saved.');
1800         }
1801
1802         if ( $post ) {
1803                 $last_date = mysql2date( get_option('date_format'), $post->post_modified );
1804                 $last_time = mysql2date( get_option('time_format'), $post->post_modified );
1805         } else {
1806                 $last_date = date_i18n( get_option('date_format') );
1807                 $last_time = date_i18n( get_option('time_format') );
1808         }
1809
1810         if ( $last_id = get_post_meta($post_id, '_edit_last', true) ) {
1811                 $last_user = get_userdata($last_id);
1812                 $last_edited = sprintf( __('Last edited by %1$s on %2$s at %3$s'), esc_html( $last_user->display_name ), $last_date, $last_time );
1813         } else {
1814                 $last_edited = sprintf( __('Last edited on %1$s at %2$s'), $last_date, $last_time );
1815         }
1816
1817         echo json_encode( array( 'message' => $message, 'last_edited' => $last_edited ) );
1818         wp_die();
1819 }
1820
1821 function wp_ajax_wp_remove_post_lock() {
1822         if ( empty( $_POST['post_ID'] ) || empty( $_POST['active_post_lock'] ) )
1823                 wp_die( 0 );
1824         $post_id = (int) $_POST['post_ID'];
1825         if ( ! $post = get_post( $post_id ) )
1826                 wp_die( 0 );
1827
1828         check_ajax_referer( 'update-post_' . $post_id );
1829
1830         if ( ! current_user_can( 'edit_post', $post_id ) )
1831                 wp_die( -1 );
1832
1833         $active_lock = array_map( 'absint', explode( ':', $_POST['active_post_lock'] ) );
1834         if ( $active_lock[1] != get_current_user_id() )
1835                 wp_die( 0 );
1836
1837         /**
1838          * Filter the post lock window duration.
1839          *
1840          * @since 3.3.0
1841          *
1842          * @param int $interval The interval in seconds the post lock duration should last, plus 5 seconds. Default 120.
1843          */
1844         $new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 120 ) + 5 ) . ':' . $active_lock[1];
1845         update_post_meta( $post_id, '_edit_lock', $new_lock, implode( ':', $active_lock ) );
1846         wp_die( 1 );
1847 }
1848
1849 function wp_ajax_dismiss_wp_pointer() {
1850         $pointer = $_POST['pointer'];
1851         if ( $pointer != sanitize_key( $pointer ) )
1852                 wp_die( 0 );
1853
1854 //      check_ajax_referer( 'dismiss-pointer_' . $pointer );
1855
1856         $dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) );
1857
1858         if ( in_array( $pointer, $dismissed ) )
1859                 wp_die( 0 );
1860
1861         $dismissed[] = $pointer;
1862         $dismissed = implode( ',', $dismissed );
1863
1864         update_user_meta( get_current_user_id(), 'dismissed_wp_pointers', $dismissed );
1865         wp_die( 1 );
1866 }
1867
1868 /**
1869  * Get an attachment.
1870  *
1871  * @since 3.5.0
1872  */
1873 function wp_ajax_get_attachment() {
1874         if ( ! isset( $_REQUEST['id'] ) )
1875                 wp_send_json_error();
1876
1877         if ( ! $id = absint( $_REQUEST['id'] ) )
1878                 wp_send_json_error();
1879
1880         if ( ! $post = get_post( $id ) )
1881                 wp_send_json_error();
1882
1883         if ( 'attachment' != $post->post_type )
1884                 wp_send_json_error();
1885
1886         if ( ! current_user_can( 'upload_files' ) )
1887                 wp_send_json_error();
1888
1889         if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
1890                 wp_send_json_error();
1891
1892         wp_send_json_success( $attachment );
1893 }
1894
1895 /**
1896  * Query for attachments.
1897  *
1898  * @since 3.5.0
1899  */
1900 function wp_ajax_query_attachments() {
1901         if ( ! current_user_can( 'upload_files' ) )
1902                 wp_send_json_error();
1903
1904         $query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array();
1905         $query = array_intersect_key( $query, array_flip( array(
1906                 's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type',
1907                 'post_parent', 'post__in', 'post__not_in',
1908         ) ) );
1909
1910         $query['post_type'] = 'attachment';
1911         $query['post_status'] = 'inherit';
1912         if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) )
1913                 $query['post_status'] .= ',private';
1914
1915         /**
1916          * Filter the arguments passed to WP_Query during an AJAX call for querying attachments.
1917          *
1918          * @since 3.7.0
1919          *
1920          * @param array $query An array of query variables. @see WP_Query::parse_query()
1921          */
1922         $query = apply_filters( 'ajax_query_attachments_args', $query );
1923         $query = new WP_Query( $query );
1924
1925         $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts );
1926         $posts = array_filter( $posts );
1927
1928         wp_send_json_success( $posts );
1929 }
1930
1931 /**
1932  * Save attachment attributes.
1933  *
1934  * @since 3.5.0
1935  */
1936 function wp_ajax_save_attachment() {
1937         if ( ! isset( $_REQUEST['id'] ) || ! isset( $_REQUEST['changes'] ) )
1938                 wp_send_json_error();
1939
1940         if ( ! $id = absint( $_REQUEST['id'] ) )
1941                 wp_send_json_error();
1942
1943         check_ajax_referer( 'update-post_' . $id, 'nonce' );
1944
1945         if ( ! current_user_can( 'edit_post', $id ) )
1946                 wp_send_json_error();
1947
1948         $changes = $_REQUEST['changes'];
1949         $post    = get_post( $id, ARRAY_A );
1950
1951         if ( 'attachment' != $post['post_type'] )
1952                 wp_send_json_error();
1953
1954         if ( isset( $changes['title'] ) )
1955                 $post['post_title'] = $changes['title'];
1956
1957         if ( isset( $changes['caption'] ) )
1958                 $post['post_excerpt'] = $changes['caption'];
1959
1960         if ( isset( $changes['description'] ) )
1961                 $post['post_content'] = $changes['description'];
1962
1963         if ( isset( $changes['alt'] ) ) {
1964                 $alt = wp_unslash( $changes['alt'] );
1965                 if ( $alt != get_post_meta( $id, '_wp_attachment_image_alt', true ) ) {
1966                         $alt = wp_strip_all_tags( $alt, true );
1967                         update_post_meta( $id, '_wp_attachment_image_alt', wp_slash( $alt ) );
1968                 }
1969         }
1970
1971         wp_update_post( $post );
1972         wp_send_json_success();
1973 }
1974
1975 /**
1976  * Save backwards compatible attachment attributes.
1977  *
1978  * @since 3.5.0
1979  */
1980 function wp_ajax_save_attachment_compat() {
1981         if ( ! isset( $_REQUEST['id'] ) )
1982                 wp_send_json_error();
1983
1984         if ( ! $id = absint( $_REQUEST['id'] ) )
1985                 wp_send_json_error();
1986
1987         if ( empty( $_REQUEST['attachments'] ) || empty( $_REQUEST['attachments'][ $id ] ) )
1988                 wp_send_json_error();
1989         $attachment_data = $_REQUEST['attachments'][ $id ];
1990
1991         check_ajax_referer( 'update-post_' . $id, 'nonce' );
1992
1993         if ( ! current_user_can( 'edit_post', $id ) )
1994                 wp_send_json_error();
1995
1996         $post = get_post( $id, ARRAY_A );
1997
1998         if ( 'attachment' != $post['post_type'] )
1999                 wp_send_json_error();
2000
2001         /** This filter is documented in wp-admin/includes/media.php */
2002         $post = apply_filters( 'attachment_fields_to_save', $post, $attachment_data );
2003
2004         if ( isset( $post['errors'] ) ) {
2005                 $errors = $post['errors']; // @todo return me and display me!
2006                 unset( $post['errors'] );
2007         }
2008
2009         wp_update_post( $post );
2010
2011         foreach ( get_attachment_taxonomies( $post ) as $taxonomy ) {
2012                 if ( isset( $attachment_data[ $taxonomy ] ) )
2013                         wp_set_object_terms( $id, array_map( 'trim', preg_split( '/,+/', $attachment_data[ $taxonomy ] ) ), $taxonomy, false );
2014         }
2015
2016         if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
2017                 wp_send_json_error();
2018
2019         wp_send_json_success( $attachment );
2020 }
2021
2022 function wp_ajax_save_attachment_order() {
2023         if ( ! isset( $_REQUEST['post_id'] ) )
2024                 wp_send_json_error();
2025
2026         if ( ! $post_id = absint( $_REQUEST['post_id'] ) )
2027                 wp_send_json_error();
2028
2029         if ( empty( $_REQUEST['attachments'] ) )
2030                 wp_send_json_error();
2031
2032         check_ajax_referer( 'update-post_' . $post_id, 'nonce' );
2033
2034         $attachments = $_REQUEST['attachments'];
2035
2036         if ( ! current_user_can( 'edit_post', $post_id ) )
2037                 wp_send_json_error();
2038
2039         $post = get_post( $post_id, ARRAY_A );
2040
2041         foreach ( $attachments as $attachment_id => $menu_order ) {
2042                 if ( ! current_user_can( 'edit_post', $attachment_id ) )
2043                         continue;
2044                 if ( ! $attachment = get_post( $attachment_id ) )
2045                         continue;
2046                 if ( 'attachment' != $attachment->post_type )
2047                         continue;
2048
2049                 wp_update_post( array( 'ID' => $attachment_id, 'menu_order' => $menu_order ) );
2050         }
2051
2052         wp_send_json_success();
2053 }
2054
2055 /**
2056  * Generates the HTML to send an attachment to the editor.
2057  * Backwards compatible with the media_send_to_editor filter and the chain
2058  * of filters that follow.
2059  *
2060  * @since 3.5.0
2061  */
2062 function wp_ajax_send_attachment_to_editor() {
2063         check_ajax_referer( 'media-send-to-editor', 'nonce' );
2064
2065         $attachment = wp_unslash( $_POST['attachment'] );
2066
2067         $id = intval( $attachment['id'] );
2068
2069         if ( ! $post = get_post( $id ) )
2070                 wp_send_json_error();
2071
2072         if ( 'attachment' != $post->post_type )
2073                 wp_send_json_error();
2074
2075         if ( current_user_can( 'edit_post', $id ) ) {
2076                 // If this attachment is unattached, attach it. Primarily a back compat thing.
2077                 if ( 0 == $post->post_parent && $insert_into_post_id = intval( $_POST['post_id'] ) ) {
2078                         wp_update_post( array( 'ID' => $id, 'post_parent' => $insert_into_post_id ) );
2079                 }
2080         }
2081
2082         $rel = $url = '';
2083         $html = $title = isset( $attachment['post_title'] ) ? $attachment['post_title'] : '';
2084         if ( ! empty( $attachment['url'] ) ) {
2085                 $url = $attachment['url'];
2086                 if ( strpos( $url, 'attachment_id') || get_attachment_link( $id ) == $url )
2087                         $rel = ' rel="attachment wp-att-' . $id . '"';
2088                 $html = '<a href="' . esc_url( $url ) . '"' . $rel . '>' . $html . '</a>';
2089         }
2090
2091         remove_filter( 'media_send_to_editor', 'image_media_send_to_editor' );
2092
2093         if ( 'image' === substr( $post->post_mime_type, 0, 5 ) ) {
2094                 $align = isset( $attachment['align'] ) ? $attachment['align'] : 'none';
2095                 $size = isset( $attachment['image-size'] ) ? $attachment['image-size'] : 'medium';
2096                 $alt = isset( $attachment['image_alt'] ) ? $attachment['image_alt'] : '';
2097                 $caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : '';
2098                 $title = ''; // We no longer insert title tags into <img> tags, as they are redundant.
2099                 $html = get_image_send_to_editor( $id, $caption, $title, $align, $url, (bool) $rel, $size, $alt );
2100         } elseif ( 'video' === substr( $post->post_mime_type, 0, 5 ) || 'audio' === substr( $post->post_mime_type, 0, 5 )  ) {
2101                 $html = stripslashes_deep( $_POST['html'] );
2102         }
2103
2104         /** This filter is documented in wp-admin/includes/media.php */
2105         $html = apply_filters( 'media_send_to_editor', $html, $id, $attachment );
2106
2107         wp_send_json_success( $html );
2108 }
2109
2110 /**
2111  * Generates the HTML to send a non-image embed link to the editor.
2112  *
2113  * Backwards compatible with the following filters:
2114  * - file_send_to_editor_url
2115  * - audio_send_to_editor_url
2116  * - video_send_to_editor_url
2117  *
2118  * @since 3.5.0
2119  */
2120 function wp_ajax_send_link_to_editor() {
2121         check_ajax_referer( 'media-send-to-editor', 'nonce' );
2122
2123         if ( ! $src = wp_unslash( $_POST['src'] ) )
2124                 wp_send_json_error();
2125
2126         if ( ! strpos( $src, '://' ) )
2127                 $src = 'http://' . $src;
2128
2129         if ( ! $src = esc_url_raw( $src ) )
2130                 wp_send_json_error();
2131
2132         if ( ! $title = trim( wp_unslash( $_POST['title'] ) ) )
2133                 $title = wp_basename( $src );
2134
2135         $html = '';
2136         if ( $title )
2137                 $html = '<a href="' . esc_url( $src ) . '">' . $title . '</a>';
2138
2139         // Figure out what filter to run:
2140         $type = 'file';
2141         if ( ( $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $src ) ) && ( $ext_type = wp_ext2type( $ext ) )
2142                 && ( 'audio' == $ext_type || 'video' == $ext_type ) )
2143                         $type = $ext_type;
2144
2145         /** This filter is documented in wp-admin/includes/media.php */
2146         $html = apply_filters( $type . '_send_to_editor_url', $html, $src, $title );
2147
2148         wp_send_json_success( $html );
2149 }
2150
2151 /**
2152  * Heartbeat API (experimental)
2153  *
2154  * Runs when the user is logged in.
2155  */
2156 function wp_ajax_heartbeat() {
2157         if ( empty( $_POST['_nonce'] ) )
2158                 wp_send_json_error();
2159
2160         $response = array();
2161
2162         if ( false === wp_verify_nonce( $_POST['_nonce'], 'heartbeat-nonce' ) ) {
2163                 // User is logged in but nonces have expired.
2164                 $response['nonces_expired'] = true;
2165                 wp_send_json($response);
2166         }
2167
2168         // screen_id is the same as $current_screen->id and the JS global 'pagenow'
2169         if ( ! empty($_POST['screen_id']) )
2170                 $screen_id = sanitize_key($_POST['screen_id']);
2171         else
2172                 $screen_id = 'front';
2173
2174         if ( ! empty($_POST['data']) ) {
2175                 $data = (array) $_POST['data'];
2176
2177                 /**
2178                  * Filter the Heartbeat response received.
2179                  *
2180                  * @since 3.6.0
2181                  *
2182                  * @param array|object $response  The Heartbeat response object or array.
2183                  * @param array        $data      The $_POST data sent.
2184                  * @param string       $screen_id The screen id.
2185                  */
2186                 $response = apply_filters( 'heartbeat_received', $response, $data, $screen_id );
2187         }
2188
2189         /**
2190          * Filter the Heartbeat response sent.
2191          *
2192          * @since 3.6.0
2193          *
2194          * @param array|object $response  The Heartbeat response object or array.
2195          * @param string       $screen_id The screen id.
2196          */
2197         $response = apply_filters( 'heartbeat_send', $response, $screen_id );
2198
2199         /**
2200          * Fires when Heartbeat ticks in logged-in environments.
2201          *
2202          * Allows the transport to be easily replaced with long-polling.
2203          *
2204          * @since 3.6.0
2205          *
2206          * @param array|object $response  The Heartbeat response object or array.
2207          * @param string       $screen_id The screen id.
2208          */
2209         do_action( 'heartbeat_tick', $response, $screen_id );
2210
2211         // Send the current time according to the server
2212         $response['server_time'] = time();
2213
2214         wp_send_json($response);
2215 }
2216
2217 function wp_ajax_get_revision_diffs() {
2218         require ABSPATH . 'wp-admin/includes/revision.php';
2219
2220         if ( ! $post = get_post( (int) $_REQUEST['post_id'] ) )
2221                 wp_send_json_error();
2222
2223         if ( ! current_user_can( 'read_post', $post->ID ) )
2224                 wp_send_json_error();
2225
2226         // Really just pre-loading the cache here.
2227         if ( ! $revisions = wp_get_post_revisions( $post->ID, array( 'check_enabled' => false ) ) )
2228                 wp_send_json_error();
2229
2230         $return = array();
2231         @set_time_limit( 0 );
2232
2233         foreach ( $_REQUEST['compare'] as $compare_key ) {
2234                 list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to
2235
2236                 $return[] = array(
2237                         'id' => $compare_key,
2238                         'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ),
2239                 );
2240         }
2241         wp_send_json_success( $return );
2242 }
2243
2244 /**
2245  * Auto-save the selected color scheme for a user's own profile.
2246  *
2247  * @since  3.8.0
2248  */
2249 function wp_ajax_save_user_color_scheme() {
2250         global $_wp_admin_css_colors;
2251
2252         check_ajax_referer( 'save-color-scheme', 'nonce' );
2253
2254         $color_scheme = sanitize_key( $_POST['color_scheme'] );
2255
2256         if ( ! isset( $_wp_admin_css_colors[ $color_scheme ] ) ) {
2257                 wp_send_json_error();
2258         }
2259
2260         update_user_meta( get_current_user_id(), 'admin_color', $color_scheme );
2261         wp_send_json_success();
2262 }