+
+/**
+ * Get an attachment.
+ *
+ * @since 3.5.0
+ */
+function wp_ajax_get_attachment() {
+ if ( ! isset( $_REQUEST['id'] ) )
+ wp_send_json_error();
+
+ if ( ! $id = absint( $_REQUEST['id'] ) )
+ wp_send_json_error();
+
+ if ( ! $post = get_post( $id ) )
+ wp_send_json_error();
+
+ if ( 'attachment' != $post->post_type )
+ wp_send_json_error();
+
+ if ( ! current_user_can( 'upload_files' ) )
+ wp_send_json_error();
+
+ if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
+ wp_send_json_error();
+
+ wp_send_json_success( $attachment );
+}
+
+/**
+ * Query for attachments.
+ *
+ * @since 3.5.0
+ */
+function wp_ajax_query_attachments() {
+ if ( ! current_user_can( 'upload_files' ) )
+ wp_send_json_error();
+
+ $query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array();
+ $query = array_intersect_key( $query, array_flip( array(
+ 's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type',
+ 'post_parent', 'post__in', 'post__not_in',
+ ) ) );
+
+ $query['post_type'] = 'attachment';
+ $query['post_status'] = 'inherit';
+ if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) )
+ $query['post_status'] .= ',private';
+
+ /**
+ * Filter the arguments passed to WP_Query during an AJAX call for querying attachments.
+ *
+ * @since 3.7.0
+ *
+ * @param array $query An array of query variables. @see WP_Query::parse_query()
+ */
+ $query = apply_filters( 'ajax_query_attachments_args', $query );
+ $query = new WP_Query( $query );
+
+ $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts );
+ $posts = array_filter( $posts );
+
+ wp_send_json_success( $posts );
+}
+
+/**
+ * Save attachment attributes.
+ *
+ * @since 3.5.0
+ */
+function wp_ajax_save_attachment() {
+ if ( ! isset( $_REQUEST['id'] ) || ! isset( $_REQUEST['changes'] ) )
+ wp_send_json_error();
+
+ if ( ! $id = absint( $_REQUEST['id'] ) )
+ wp_send_json_error();
+
+ check_ajax_referer( 'update-post_' . $id, 'nonce' );
+
+ if ( ! current_user_can( 'edit_post', $id ) )
+ wp_send_json_error();
+
+ $changes = $_REQUEST['changes'];
+ $post = get_post( $id, ARRAY_A );
+
+ if ( 'attachment' != $post['post_type'] )
+ wp_send_json_error();
+
+ if ( isset( $changes['title'] ) )
+ $post['post_title'] = $changes['title'];
+
+ if ( isset( $changes['caption'] ) )
+ $post['post_excerpt'] = $changes['caption'];
+
+ if ( isset( $changes['description'] ) )
+ $post['post_content'] = $changes['description'];
+
+ if ( isset( $changes['alt'] ) ) {
+ $alt = wp_unslash( $changes['alt'] );
+ if ( $alt != get_post_meta( $id, '_wp_attachment_image_alt', true ) ) {
+ $alt = wp_strip_all_tags( $alt, true );
+ update_post_meta( $id, '_wp_attachment_image_alt', wp_slash( $alt ) );
+ }
+ }
+
+ wp_update_post( $post );
+ wp_send_json_success();
+}
+
+/**
+ * Save backwards compatible attachment attributes.
+ *
+ * @since 3.5.0
+ */
+function wp_ajax_save_attachment_compat() {
+ if ( ! isset( $_REQUEST['id'] ) )
+ wp_send_json_error();
+
+ if ( ! $id = absint( $_REQUEST['id'] ) )
+ wp_send_json_error();
+
+ if ( empty( $_REQUEST['attachments'] ) || empty( $_REQUEST['attachments'][ $id ] ) )
+ wp_send_json_error();
+ $attachment_data = $_REQUEST['attachments'][ $id ];
+
+ check_ajax_referer( 'update-post_' . $id, 'nonce' );
+
+ if ( ! current_user_can( 'edit_post', $id ) )
+ wp_send_json_error();
+
+ $post = get_post( $id, ARRAY_A );
+
+ if ( 'attachment' != $post['post_type'] )
+ wp_send_json_error();
+
+ /** This filter is documented in wp-admin/includes/media.php */
+ $post = apply_filters( 'attachment_fields_to_save', $post, $attachment_data );
+
+ if ( isset( $post['errors'] ) ) {
+ $errors = $post['errors']; // @todo return me and display me!
+ unset( $post['errors'] );
+ }
+
+ wp_update_post( $post );
+
+ foreach ( get_attachment_taxonomies( $post ) as $taxonomy ) {
+ if ( isset( $attachment_data[ $taxonomy ] ) )
+ wp_set_object_terms( $id, array_map( 'trim', preg_split( '/,+/', $attachment_data[ $taxonomy ] ) ), $taxonomy, false );
+ }
+
+ if ( ! $attachment = wp_prepare_attachment_for_js( $id ) )
+ wp_send_json_error();
+
+ wp_send_json_success( $attachment );
+}
+
+function wp_ajax_save_attachment_order() {
+ if ( ! isset( $_REQUEST['post_id'] ) )
+ wp_send_json_error();
+
+ if ( ! $post_id = absint( $_REQUEST['post_id'] ) )
+ wp_send_json_error();
+
+ if ( empty( $_REQUEST['attachments'] ) )
+ wp_send_json_error();
+
+ check_ajax_referer( 'update-post_' . $post_id, 'nonce' );
+
+ $attachments = $_REQUEST['attachments'];
+
+ if ( ! current_user_can( 'edit_post', $post_id ) )
+ wp_send_json_error();
+
+ $post = get_post( $post_id, ARRAY_A );
+
+ foreach ( $attachments as $attachment_id => $menu_order ) {
+ if ( ! current_user_can( 'edit_post', $attachment_id ) )
+ continue;
+ if ( ! $attachment = get_post( $attachment_id ) )
+ continue;
+ if ( 'attachment' != $attachment->post_type )
+ continue;
+
+ wp_update_post( array( 'ID' => $attachment_id, 'menu_order' => $menu_order ) );
+ }
+
+ wp_send_json_success();
+}
+
+/**
+ * Generates the HTML to send an attachment to the editor.
+ * Backwards compatible with the media_send_to_editor filter and the chain
+ * of filters that follow.
+ *
+ * @since 3.5.0
+ */
+function wp_ajax_send_attachment_to_editor() {
+ check_ajax_referer( 'media-send-to-editor', 'nonce' );
+
+ $attachment = wp_unslash( $_POST['attachment'] );
+
+ $id = intval( $attachment['id'] );
+
+ if ( ! $post = get_post( $id ) )
+ wp_send_json_error();
+
+ if ( 'attachment' != $post->post_type )
+ wp_send_json_error();
+
+ if ( current_user_can( 'edit_post', $id ) ) {
+ // If this attachment is unattached, attach it. Primarily a back compat thing.
+ if ( 0 == $post->post_parent && $insert_into_post_id = intval( $_POST['post_id'] ) ) {
+ wp_update_post( array( 'ID' => $id, 'post_parent' => $insert_into_post_id ) );
+ }
+ }
+
+ $rel = $url = '';
+ $html = $title = isset( $attachment['post_title'] ) ? $attachment['post_title'] : '';
+ if ( ! empty( $attachment['url'] ) ) {
+ $url = $attachment['url'];
+ if ( strpos( $url, 'attachment_id') || get_attachment_link( $id ) == $url )
+ $rel = ' rel="attachment wp-att-' . $id . '"';
+ $html = '<a href="' . esc_url( $url ) . '"' . $rel . '>' . $html . '</a>';
+ }
+
+ remove_filter( 'media_send_to_editor', 'image_media_send_to_editor' );
+
+ if ( 'image' === substr( $post->post_mime_type, 0, 5 ) ) {
+ $align = isset( $attachment['align'] ) ? $attachment['align'] : 'none';
+ $size = isset( $attachment['image-size'] ) ? $attachment['image-size'] : 'medium';
+ $alt = isset( $attachment['image_alt'] ) ? $attachment['image_alt'] : '';
+ $caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : '';
+ $title = ''; // We no longer insert title tags into <img> tags, as they are redundant.
+ $html = get_image_send_to_editor( $id, $caption, $title, $align, $url, (bool) $rel, $size, $alt );
+ } elseif ( 'video' === substr( $post->post_mime_type, 0, 5 ) || 'audio' === substr( $post->post_mime_type, 0, 5 ) ) {
+ $html = stripslashes_deep( $_POST['html'] );
+ }
+
+ /** This filter is documented in wp-admin/includes/media.php */
+ $html = apply_filters( 'media_send_to_editor', $html, $id, $attachment );
+
+ wp_send_json_success( $html );
+}
+
+/**
+ * Generates the HTML to send a non-image embed link to the editor.
+ *
+ * Backwards compatible with the following filters:
+ * - file_send_to_editor_url
+ * - audio_send_to_editor_url
+ * - video_send_to_editor_url
+ *
+ * @since 3.5.0
+ */
+function wp_ajax_send_link_to_editor() {
+ check_ajax_referer( 'media-send-to-editor', 'nonce' );
+
+ if ( ! $src = wp_unslash( $_POST['src'] ) )
+ wp_send_json_error();
+
+ if ( ! strpos( $src, '://' ) )
+ $src = 'http://' . $src;
+
+ if ( ! $src = esc_url_raw( $src ) )
+ wp_send_json_error();
+
+ if ( ! $title = trim( wp_unslash( $_POST['title'] ) ) )
+ $title = wp_basename( $src );
+
+ $html = '';
+ if ( $title )
+ $html = '<a href="' . esc_url( $src ) . '">' . $title . '</a>';
+
+ // Figure out what filter to run:
+ $type = 'file';
+ if ( ( $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $src ) ) && ( $ext_type = wp_ext2type( $ext ) )
+ && ( 'audio' == $ext_type || 'video' == $ext_type ) )
+ $type = $ext_type;
+
+ /** This filter is documented in wp-admin/includes/media.php */
+ $html = apply_filters( $type . '_send_to_editor_url', $html, $src, $title );
+
+ wp_send_json_success( $html );
+}
+
+/**
+ * Heartbeat API (experimental)
+ *
+ * Runs when the user is logged in.
+ */
+function wp_ajax_heartbeat() {
+ if ( empty( $_POST['_nonce'] ) )
+ wp_send_json_error();
+
+ $response = array();
+
+ if ( false === wp_verify_nonce( $_POST['_nonce'], 'heartbeat-nonce' ) ) {
+ // User is logged in but nonces have expired.
+ $response['nonces_expired'] = true;
+ wp_send_json($response);
+ }
+
+ // screen_id is the same as $current_screen->id and the JS global 'pagenow'
+ if ( ! empty($_POST['screen_id']) )
+ $screen_id = sanitize_key($_POST['screen_id']);
+ else
+ $screen_id = 'front';
+
+ if ( ! empty($_POST['data']) ) {
+ $data = (array) $_POST['data'];
+
+ /**
+ * Filter the Heartbeat response received.
+ *
+ * @since 3.6.0
+ *
+ * @param array|object $response The Heartbeat response object or array.
+ * @param array $data The $_POST data sent.
+ * @param string $screen_id The screen id.
+ */
+ $response = apply_filters( 'heartbeat_received', $response, $data, $screen_id );
+ }
+
+ /**
+ * Filter the Heartbeat response sent.
+ *
+ * @since 3.6.0
+ *
+ * @param array|object $response The Heartbeat response object or array.
+ * @param string $screen_id The screen id.
+ */
+ $response = apply_filters( 'heartbeat_send', $response, $screen_id );
+
+ /**
+ * Fires when Heartbeat ticks in logged-in environments.
+ *
+ * Allows the transport to be easily replaced with long-polling.
+ *
+ * @since 3.6.0
+ *
+ * @param array|object $response The Heartbeat response object or array.
+ * @param string $screen_id The screen id.
+ */
+ do_action( 'heartbeat_tick', $response, $screen_id );
+
+ // Send the current time according to the server
+ $response['server_time'] = time();
+
+ wp_send_json($response);
+}
+
+function wp_ajax_get_revision_diffs() {
+ require ABSPATH . 'wp-admin/includes/revision.php';
+
+ if ( ! $post = get_post( (int) $_REQUEST['post_id'] ) )
+ wp_send_json_error();
+
+ if ( ! current_user_can( 'read_post', $post->ID ) )
+ wp_send_json_error();
+
+ // Really just pre-loading the cache here.
+ if ( ! $revisions = wp_get_post_revisions( $post->ID, array( 'check_enabled' => false ) ) )
+ wp_send_json_error();
+
+ $return = array();
+ @set_time_limit( 0 );
+
+ foreach ( $_REQUEST['compare'] as $compare_key ) {
+ list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to
+
+ $return[] = array(
+ 'id' => $compare_key,
+ 'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ),
+ );
+ }
+ wp_send_json_success( $return );
+}
+
+/**
+ * Auto-save the selected color scheme for a user's own profile.
+ *
+ * @since 3.8.0
+ */
+function wp_ajax_save_user_color_scheme() {
+ global $_wp_admin_css_colors;
+
+ check_ajax_referer( 'save-color-scheme', 'nonce' );
+
+ $color_scheme = sanitize_key( $_POST['color_scheme'] );
+
+ if ( ! isset( $_wp_admin_css_colors[ $color_scheme ] ) ) {
+ wp_send_json_error();
+ }
+
+ update_user_meta( get_current_user_id(), 'admin_color', $color_scheme );
+ wp_send_json_success();
+}