diff --git a/src/wp-admin/js/widgets/media-video-widget.js b/src/wp-admin/js/widgets/media-video-widget.js index 07b203fde4..9eb532caa6 100644 --- a/src/wp-admin/js/widgets/media-video-widget.js +++ b/src/wp-admin/js/widgets/media-video-widget.js @@ -110,13 +110,12 @@ control.fetchEmbedDfd.abort(); } - control.fetchEmbedDfd = jQuery.ajax({ + control.fetchEmbedDfd = wp.apiRequest({ url: wp.media.view.settings.oEmbedProxyUrl, data: { url: control.model.get( 'url' ), maxwidth: control.model.get( 'width' ), maxheight: control.model.get( 'height' ), - _wpnonce: wp.media.view.settings.nonce.wpRestApi, discover: false }, type: 'GET', diff --git a/src/wp-admin/js/widgets/media-widgets.js b/src/wp-admin/js/widgets/media-widgets.js index e922670a2c..c4f23e1cda 100644 --- a/src/wp-admin/js/widgets/media-widgets.js +++ b/src/wp-admin/js/widgets/media-widgets.js @@ -200,13 +200,12 @@ wp.mediaWidgets = ( function( $ ) { embedLinkView.model.attributes.url = url; } - embedLinkView.dfd = $.ajax({ + embedLinkView.dfd = wp.apiRequest({ url: wp.media.view.settings.oEmbedProxyUrl, data: { url: url, maxwidth: embedLinkView.model.get( 'width' ), maxheight: embedLinkView.model.get( 'height' ), - _wpnonce: wp.media.view.settings.nonce.wpRestApi, discover: false }, type: 'GET', diff --git a/src/wp-includes/js/api-request.js b/src/wp-includes/js/api-request.js new file mode 100644 index 0000000000..2b4ac4b5e0 --- /dev/null +++ b/src/wp-includes/js/api-request.js @@ -0,0 +1,87 @@ +/** + * Thin jQuery.ajax wrapper for WP REST API requests. + * + * Currently only applies to requests that do not use the `wp-api.js` Backbone + * client library, though this may change. Serves several purposes: + * + * - Allows overriding these requests as needed by customized WP installations. + * - Sends the REST API nonce as a request header. + * - Allows specifying only an endpoint namespace/path instead of a full URL. + * + * @namespace wp.apiRequest + * @since 4.9.0 + */ + +( function( $ ) { + var wpApiSettings = window.wpApiSettings; + + function apiRequest( options ) { + options = apiRequest.buildAjaxOptions( options ); + return apiRequest.transport( options ); + }; + + apiRequest.buildAjaxOptions = function( options ) { + var url = options.url; + var path = options.path; + var namespaceTrimmed, endpointTrimmed; + var headers, addNonceHeader, headerName; + + if ( + typeof options.namespace === 'string' && + typeof options.endpoint === 'string' + ) { + namespaceTrimmed = options.namespace.replace( /^\/|\/$/g, '' ); + endpointTrimmed = options.endpoint.replace( /^\//, '' ); + if ( endpointTrimmed ) { + path = namespaceTrimmed + '/' + endpointTrimmed; + } else { + path = namespaceTrimmed; + } + } + if ( typeof path === 'string' ) { + url = wpApiSettings.root + path.replace( /^\//, '' ); + } + + // If ?_wpnonce=... is present, no need to add a nonce header. + addNonceHeader = ! ( options.data && options.data._wpnonce ); + + headers = options.headers || {}; + + // If an 'X-WP-Nonce' header (or any case-insensitive variation + // thereof) was specified, no need to add a nonce header. + if ( addNonceHeader ) { + for ( headerName in headers ) { + if ( headers.hasOwnProperty( headerName ) ) { + if ( headerName.toLowerCase() === 'x-wp-nonce' ) { + addNonceHeader = false; + break; + } + } + } + } + + if ( addNonceHeader ) { + // Do not mutate the original headers object, if any. + headers = $.extend( { + 'X-WP-Nonce': wpApiSettings.nonce + }, headers ); + } + + // Do not mutate the original options object. + options = $.extend( {}, options, { + headers: headers, + url: url + } ); + + delete options.path; + delete options.namespace; + delete options.endpoint; + + return options; + }; + + apiRequest.transport = $.ajax; + + window.wp = window.wp || {}; + window.wp.apiRequest = apiRequest; +} )( jQuery ); diff --git a/src/wp-includes/js/media-views.js b/src/wp-includes/js/media-views.js index 991b3d09c8..6d46229a91 100644 --- a/src/wp-includes/js/media-views.js +++ b/src/wp-includes/js/media-views.js @@ -4642,13 +4642,12 @@ EmbedLink = wp.media.view.Settings.extend({ url = 'https://www.youtube.com/watch?v=' + youTubeEmbedMatch[ 1 ]; } - this.dfd = $.ajax({ + this.dfd = wp.apiRequest({ url: wp.media.view.settings.oEmbedProxyUrl, data: { url: url, maxwidth: this.model.get( 'width' ), - maxheight: this.model.get( 'height' ), - _wpnonce: wp.media.view.settings.nonce.wpRestApi + maxheight: this.model.get( 'height' ) }, type: 'GET', dataType: 'json', diff --git a/src/wp-includes/js/media/views/embed/link.js b/src/wp-includes/js/media/views/embed/link.js index 36925f4974..12126d8aa1 100644 --- a/src/wp-includes/js/media/views/embed/link.js +++ b/src/wp-includes/js/media/views/embed/link.js @@ -53,13 +53,12 @@ EmbedLink = wp.media.view.Settings.extend({ url = 'https://www.youtube.com/watch?v=' + youTubeEmbedMatch[ 1 ]; } - this.dfd = $.ajax({ + this.dfd = wp.apiRequest({ url: wp.media.view.settings.oEmbedProxyUrl, data: { url: url, maxwidth: this.model.get( 'width' ), - maxheight: this.model.get( 'height' ), - _wpnonce: wp.media.view.settings.nonce.wpRestApi + maxheight: this.model.get( 'height' ) }, type: 'GET', dataType: 'json', diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 81dc502b59..6c4fce40ea 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -3437,7 +3437,6 @@ function wp_enqueue_media( $args = array() ) { 'captions' => ! apply_filters( 'disable_captions', '' ), 'nonce' => array( 'sendToEditor' => wp_create_nonce( 'media-send-to-editor' ), - 'wpRestApi' => wp_create_nonce( 'wp_rest' ), ), 'post' => array( 'id' => 0, diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 8858984d2a..3c3659ea14 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -133,6 +133,14 @@ function wp_default_scripts( &$scripts ) { 'broken' => __('An unidentified error has occurred.') ) ); + $scripts->add( 'wp-api-request', "/wp-includes/js/api-request$suffix.js", array( 'jquery' ), false, 1 ); + // `wpApiSettings` is also used by `wp-api`, which depends on this script. + did_action( 'init' ) && $scripts->localize( 'wp-api-request', 'wpApiSettings', array( + 'root' => esc_url_raw( get_rest_url() ), + 'nonce' => ( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' ), + 'versionString' => 'wp/v2/', + ) ); + $scripts->add( 'wp-pointer', "/wp-includes/js/wp-pointer$suffix.js", array( 'jquery-ui-widget', 'jquery-ui-position' ), '20111129a', 1 ); did_action( 'init' ) && $scripts->localize( 'wp-pointer', 'wpPointerL10n', array( 'dismiss' => __('Dismiss'), @@ -566,17 +574,12 @@ function wp_default_scripts( &$scripts ) { // To enqueue media-views or media-editor, call wp_enqueue_media(). // Both rely on numerous settings, styles, and templates to operate correctly. - $scripts->add( 'media-views', "/wp-includes/js/media-views$suffix.js", array( 'utils', 'media-models', 'wp-plupload', 'jquery-ui-sortable', 'wp-mediaelement' ), false, 1 ); + $scripts->add( 'media-views', "/wp-includes/js/media-views$suffix.js", array( 'utils', 'media-models', 'wp-plupload', 'jquery-ui-sortable', 'wp-mediaelement', 'wp-api-request' ), false, 1 ); $scripts->add( 'media-editor', "/wp-includes/js/media-editor$suffix.js", array( 'shortcode', 'media-views' ), false, 1 ); $scripts->add( 'media-audiovideo', "/wp-includes/js/media-audiovideo$suffix.js", array( 'media-editor' ), false, 1 ); $scripts->add( 'mce-view', "/wp-includes/js/mce-view$suffix.js", array( 'shortcode', 'jquery', 'media-views', 'media-audiovideo' ), false, 1 ); - $scripts->add( 'wp-api', "/wp-includes/js/wp-api$suffix.js", array( 'jquery', 'backbone', 'underscore' ), false, 1 ); - did_action( 'init' ) && $scripts->localize( 'wp-api', 'wpApiSettings', array( - 'root' => esc_url_raw( get_rest_url() ), - 'nonce' => ( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' ), - 'versionString' => 'wp/v2/', - ) ); + $scripts->add( 'wp-api', "/wp-includes/js/wp-api$suffix.js", array( 'jquery', 'backbone', 'underscore', 'wp-api-request' ), false, 1 ); if ( is_admin() ) { $scripts->add( 'admin-tags', "/wp-admin/js/tags$suffix.js", array( 'jquery', 'wp-ajax-response' ), false, 1 ); @@ -669,12 +672,12 @@ function wp_default_scripts( &$scripts ) { $scripts->add( 'admin-gallery', "/wp-admin/js/gallery$suffix.js", array( 'jquery-ui-sortable' ) ); $scripts->add( 'admin-widgets', "/wp-admin/js/widgets$suffix.js", array( 'jquery-ui-sortable', 'jquery-ui-draggable', 'jquery-ui-droppable' ), false, 1 ); - $scripts->add( 'media-widgets', "/wp-admin/js/widgets/media-widgets$suffix.js", array( 'jquery', 'media-models', 'media-views' ) ); + $scripts->add( 'media-widgets', "/wp-admin/js/widgets/media-widgets$suffix.js", array( 'jquery', 'media-models', 'media-views', 'wp-api-request' ) ); $scripts->add_inline_script( 'media-widgets', 'wp.mediaWidgets.init();', 'after' ); $scripts->add( 'media-audio-widget', "/wp-admin/js/widgets/media-audio-widget$suffix.js", array( 'media-widgets', 'media-audiovideo' ) ); $scripts->add( 'media-image-widget', "/wp-admin/js/widgets/media-image-widget$suffix.js", array( 'media-widgets' ) ); - $scripts->add( 'media-video-widget', "/wp-admin/js/widgets/media-video-widget$suffix.js", array( 'media-widgets', 'media-audiovideo' ) ); + $scripts->add( 'media-video-widget', "/wp-admin/js/widgets/media-video-widget$suffix.js", array( 'media-widgets', 'media-audiovideo', 'wp-api-request' ) ); $scripts->add( 'text-widgets', "/wp-admin/js/widgets/text-widgets$suffix.js", array( 'jquery', 'backbone', 'editor', 'wp-util', 'wp-a11y' ) ); $scripts->add_inline_script( 'text-widgets', 'wp.textWidgets.init();', 'after' ); diff --git a/tests/qunit/index.html b/tests/qunit/index.html index c41fffe63a..2ca82b3cdd 100644 --- a/tests/qunit/index.html +++ b/tests/qunit/index.html @@ -19,7 +19,8 @@ @@ -77,6 +78,7 @@ + @@ -122,6 +124,7 @@ +