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 @@
+