+
+/**
+ * Ajax handler for getting 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 );
+}
+
+/**
+ * Ajax handler for querying 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', 'year', 'monthnum'
+ ) ) );
+
+ $query['post_type'] = 'attachment';
+ if ( MEDIA_TRASH
+ && ! empty( $_REQUEST['query']['post_status'] )
+ && 'trash' === $_REQUEST['query']['post_status'] ) {
+ $query['post_status'] = 'trash';
+ } else {
+ $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
+ *
+ * @see WP_Query::parse_query()
+ *
+ * @param array $query An array of query variables.
+ */
+ $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 );
+}
+
+/**
+ * Ajax handler for updating 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 ( MEDIA_TRASH && isset( $changes['status'] ) )
+ $post['post_status'] = $changes['status'];
+
+ 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 ) );
+ }
+ }
+
+ if ( 0 === strpos( $post['post_mime_type'], 'audio/' ) ) {
+ $changed = false;
+ $id3data = wp_get_attachment_metadata( $post['ID'] );
+ if ( ! is_array( $id3data ) ) {
+ $changed = true;
+ $id3data = array();
+ }
+ foreach ( wp_get_attachment_id3_keys( (object) $post, 'edit' ) as $key => $label ) {
+ if ( isset( $changes[ $key ] ) ) {
+ $changed = true;
+ $id3data[ $key ] = sanitize_text_field( wp_unslash( $changes[ $key ] ) );
+ }
+ }
+
+ if ( $changed ) {
+ wp_update_attachment_metadata( $id, $id3data );
+ }
+ }
+
+ if ( MEDIA_TRASH && isset( $changes['status'] ) && 'trash' === $changes['status'] ) {
+ wp_delete_post( $id );
+ } else {
+ wp_update_post( $post );
+ }
+
+ wp_send_json_success();
+}
+
+/**
+ * Ajax handler for saving 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 );
+}
+
+/**
+ * Ajax handler for saving the attachment order.
+ *
+ * @since 3.5.0
+ */
+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();
+
+ 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();
+}
+
+/**
+ * Ajax handler for sending an attachment to the editor.
+ *
+ * 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 = 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 );
+}
+
+/**
+ * Ajax handler for sending a link to the editor.
+ *
+ * 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() {
+ global $post, $wp_embed;
+
+ 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 );
+
+ $post = get_post( isset( $_POST['post_id'] ) ? $_POST['post_id'] : 0 );
+
+ // Ping WordPress for an embed.
+ $check_embed = $wp_embed->run_shortcode( '[embed]'. $src .'[/embed]' );
+
+ // Fallback that WordPress creates when no oEmbed was found.
+ $fallback = $wp_embed->maybe_make_link( $src );
+
+ if ( $check_embed !== $fallback ) {
+ // TinyMCE view for [embed] will parse this
+ $html = '[embed]' . $src . '[/embed]';
+ } elseif ( $title ) {
+ $html = '<a href="' . esc_url( $src ) . '">' . $title . '</a>';
+ } else {
+ $html = '';
+ }
+
+ // 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 );
+}
+
+/**
+ * Ajax handler for the Heartbeat API.
+ *
+ * Runs when the user is logged in.
+ *
+ * @since 3.6.0
+ */
+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 = wp_unslash( (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);
+}
+
+/**
+ * Ajax handler for getting revision diffs.
+ *
+ * @since 3.6.0
+ */
+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 );
+}
+
+/**
+ * Ajax handler for auto-saving 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();
+}
+
+/**
+ * Ajax handler for getting themes from themes_api().
+ *
+ * @since 3.9.0
+ */
+function wp_ajax_query_themes() {
+ global $themes_allowedtags, $theme_field_defaults;
+
+ if ( ! current_user_can( 'install_themes' ) ) {
+ wp_send_json_error();
+ }
+
+ $args = wp_parse_args( wp_unslash( $_REQUEST['request'] ), array(
+ 'per_page' => 20,
+ 'fields' => $theme_field_defaults
+ ) );
+
+ $old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search';
+
+ /** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */
+ $args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args );
+
+ $api = themes_api( 'query_themes', $args );
+
+ if ( is_wp_error( $api ) ) {
+ wp_send_json_error();
+ }
+
+ $update_php = network_admin_url( 'update.php?action=install-theme' );
+ foreach ( $api->themes as &$theme ) {
+ $theme->install_url = add_query_arg( array(
+ 'theme' => $theme->slug,
+ '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug )
+ ), $update_php );
+
+ $theme->name = wp_kses( $theme->name, $themes_allowedtags );
+ $theme->author = wp_kses( $theme->author, $themes_allowedtags );
+ $theme->version = wp_kses( $theme->version, $themes_allowedtags );
+ $theme->description = wp_kses( $theme->description, $themes_allowedtags );
+ $theme->num_ratings = sprintf( _n( '(based on %s rating)', '(based on %s ratings)', $theme->num_ratings ), number_format_i18n( $theme->num_ratings ) );
+ $theme->preview_url = set_url_scheme( $theme->preview_url );
+ }
+
+ wp_send_json_success( $api );
+}
+
+/**
+ * Apply [embed] AJAX handlers to a string.
+ *
+ * @since 4.0.0
+ *
+ * @global WP_Post $post Global $post.
+ * @global WP_Embed $wp_embed Embed API instance.
+ */
+function wp_ajax_parse_embed() {
+ global $post, $wp_embed;
+
+ if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) {
+ wp_send_json_error();
+ }
+
+ if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) {
+ wp_send_json_error();
+ }
+
+ $shortcode = wp_unslash( $_POST['shortcode'] );
+ $url = str_replace( '[embed]', '', str_replace( '[/embed]', '', $shortcode ) );
+ $parsed = false;
+ setup_postdata( $post );
+
+ $wp_embed->return_false_on_fail = true;
+
+ if ( is_ssl() && preg_match( '%^\\[embed[^\\]]*\\]http://%i', $shortcode ) ) {
+ // Admin is ssl and the user pasted non-ssl URL.
+ // Check if the provider supports ssl embeds and use that for the preview.
+ $ssl_shortcode = preg_replace( '%^(\\[embed[^\\]]*\\])http://%i', '$1https://', $shortcode );
+ $parsed = $wp_embed->run_shortcode( $ssl_shortcode );
+
+ if ( ! $parsed ) {
+ $no_ssl_support = true;
+ }
+ }
+
+ if ( ! $parsed ) {
+ $parsed = $wp_embed->run_shortcode( $shortcode );
+ }
+
+ if ( ! $parsed ) {
+ wp_send_json_error( array(
+ 'type' => 'not-embeddable',
+ 'message' => sprintf( __( '%s failed to embed.' ), '<code>' . esc_html( $url ) . '</code>' ),
+ ) );
+ }
+
+ if ( has_shortcode( $parsed, 'audio' ) || has_shortcode( $parsed, 'video' ) ) {
+ $styles = '';
+ $mce_styles = wpview_media_sandbox_styles();
+ foreach ( $mce_styles as $style ) {
+ $styles .= sprintf( '<link rel="stylesheet" href="%s"/>', $style );
+ }
+
+ $html = do_shortcode( $parsed );
+
+ global $wp_scripts;
+ if ( ! empty( $wp_scripts ) ) {
+ $wp_scripts->done = array();
+ }
+ ob_start();
+ wp_print_scripts( 'wp-mediaelement' );
+ $scripts = ob_get_clean();
+
+ $parsed = $styles . $html . $scripts;
+ }
+
+
+ if ( ! empty( $no_ssl_support ) || ( is_ssl() && ( preg_match( '%<(iframe|script|embed) [^>]*src="http://%', $parsed ) ||
+ preg_match( '%<link [^>]*href="http://%', $parsed ) ) ) ) {
+ // Admin is ssl and the embed is not. Iframes, scripts, and other "active content" will be blocked.
+ wp_send_json_error( array(
+ 'type' => 'not-ssl',
+ 'message' => __( 'This preview is unavailable in the editor.' ),
+ ) );
+ }
+
+ wp_send_json_success( array(
+ 'body' => $parsed
+ ) );
+}
+
+function wp_ajax_parse_media_shortcode() {
+ global $post, $wp_scripts;
+
+ if ( ! $post = get_post( (int) $_POST['post_ID'] ) ) {
+ wp_send_json_error();
+ }
+
+ if ( empty( $_POST['shortcode'] ) || ! current_user_can( 'edit_post', $post->ID ) ) {
+ wp_send_json_error();
+ }
+
+ setup_postdata( $post );
+ $shortcode = do_shortcode( wp_unslash( $_POST['shortcode'] ) );
+
+ if ( empty( $shortcode ) ) {
+ wp_send_json_error( array(
+ 'type' => 'no-items',
+ 'message' => __( 'No items found.' ),
+ ) );
+ }
+
+ $head = '';
+ $styles = wpview_media_sandbox_styles();
+
+ foreach ( $styles as $style ) {
+ $head .= '<link type="text/css" rel="stylesheet" href="' . $style . '">';
+ }
+
+ if ( ! empty( $wp_scripts ) ) {
+ $wp_scripts->done = array();
+ }
+
+ ob_start();
+
+ echo $shortcode;
+
+ if ( 'playlist' === $_REQUEST['type'] ) {
+ wp_underscore_playlist_templates();
+
+ wp_print_scripts( 'wp-playlist' );
+ } else {
+ wp_print_scripts( 'wp-mediaelement' );
+ }
+
+ wp_send_json_success( array(
+ 'head' => $head,
+ 'body' => ob_get_clean()
+ ) );
+}
+
+/**
+ * AJAX handler for destroying multiple open sessions for a user.
+ *
+ * @since 4.1.0
+ */
+function wp_ajax_destroy_sessions() {
+
+ $user = get_userdata( (int) $_POST['user_id'] );
+ if ( $user ) {
+ if ( ! current_user_can( 'edit_user', $user->ID ) ) {
+ $user = false;
+ } elseif ( ! wp_verify_nonce( $_POST['nonce'], 'update-user_' . $user->ID ) ) {
+ $user = false;
+ }
+ }
+
+ if ( ! $user ) {
+ wp_send_json_error( array(
+ 'message' => __( 'Could not log out user sessions. Please try again.' ),
+ ) );
+ }
+
+ $sessions = WP_Session_Tokens::get_instance( $user->ID );
+
+ if ( $user->ID === get_current_user_id() ) {
+ $sessions->destroy_others( wp_get_session_token() );
+ $message = __( 'You are now logged out everywhere else.' );
+ } else {
+ $sessions->destroy_all();
+ /* translators: 1: User's display name. */
+ $message = sprintf( __( '%s has been logged out.' ), $user->display_name );
+ }
+
+ wp_send_json_success( array( 'message' => $message ) );
+}