Merge "Improve sorting on SpecialWanted*-Pages"
[mediawiki.git] / resources / src / mediawiki / ForeignApi.js
blob1a3cdd5a3f4451c0c336844690857d62d3e4c5d7
1 ( function ( mw, $ ) {
3         /**
4          * Create an object like mw.Api, but automatically handling everything required to communicate
5          * with another MediaWiki wiki via cross-origin requests (CORS).
6          *
7          * The foreign wiki must be configured to accept requests from the current wiki. See
8          * <https://www.mediawiki.org/wiki/Manual:$wgCrossSiteAJAXdomains> for details.
9          *
10          *     var api = new mw.ForeignApi( 'https://commons.wikimedia.org/w/api.php' );
11          *     api.get( {
12          *         action: 'query',
13          *         meta: 'userinfo'
14          *     } ).done( function ( data ) {
15          *         console.log( data );
16          *     } );
17          *
18          * To ensure that the user at the foreign wiki is logged in, pass the `assert: 'user'` parameter
19          * to #get/#post (since MW 1.23): if they are not, the API request will fail. (Note that this
20          * doesn't guarantee that it's the same user.)
21          *
22          * Authentication-related MediaWiki extensions may extend this class to ensure that the user
23          * authenticated on the current wiki will be automatically authenticated on the foreign one. These
24          * extension modules should be registered using the ResourceLoaderForeignApiModules hook. See
25          * CentralAuth for a practical example. The general pattern to extend and override the name is:
26          *
27          *     function MyForeignApi() {};
28          *     OO.inheritClass( MyForeignApi, mw.ForeignApi );
29          *     mw.ForeignApi = MyForeignApi;
30          *
31          * @class mw.ForeignApi
32          * @extends mw.Api
33          * @since 1.26
34          *
35          * @constructor
36          * @param {string|mw.Uri} url URL pointing to another wiki's `api.php` endpoint.
37          * @param {Object} [options] See mw.Api.
38          * @param {Object} [options.anonymous=false] Perform all requests anonymously. Use this option if
39          *     the target wiki may otherwise not accept cross-origin requests, or if you don't need to
40          *     perform write actions or read restricted information and want to avoid the overhead.
41          *
42          * @author Bartosz DziewoƄski
43          * @author Jon Robson
44          */
45         function CoreForeignApi( url, options ) {
46                 if ( !url || $.isPlainObject( url ) ) {
47                         throw new Error( 'mw.ForeignApi() requires a `url` parameter' );
48                 }
50                 this.apiUrl = String( url );
51                 this.anonymous = options && options.anonymous;
53                 options = $.extend( /* deep=*/ true,
54                         {
55                                 ajax: {
56                                         url: this.apiUrl,
57                                         xhrFields: {
58                                                 withCredentials: !this.anonymous
59                                         }
60                                 },
61                                 parameters: {
62                                         // Add 'origin' query parameter to all requests.
63                                         origin: this.getOrigin()
64                                 }
65                         },
66                         options
67                 );
69                 // Call parent constructor
70                 CoreForeignApi.parent.call( this, options );
71         }
73         OO.inheritClass( CoreForeignApi, mw.Api );
75         /**
76          * Return the origin to use for API requests, in the required format (protocol, host and port, if
77          * any).
78          *
79          * @protected
80          * @return {string}
81          */
82         CoreForeignApi.prototype.getOrigin = function () {
83                 var origin;
84                 if ( this.anonymous ) {
85                         return '*';
86                 }
87                 origin = location.protocol + '//' + location.hostname;
88                 if ( location.port ) {
89                         origin += ':' + location.port;
90                 }
91                 return origin;
92         };
94         /**
95          * @inheritdoc
96          */
97         CoreForeignApi.prototype.ajax = function ( parameters, ajaxOptions ) {
98                 var url, origin, newAjaxOptions;
100                 // 'origin' query parameter must be part of the request URI, and not just POST request body
101                 if ( ajaxOptions.type === 'POST' ) {
102                         url = ( ajaxOptions && ajaxOptions.url ) || this.defaults.ajax.url;
103                         origin = ( parameters && parameters.origin ) || this.defaults.parameters.origin;
104                         url += ( url.indexOf( '?' ) !== -1 ? '&' : '?' ) +
105                                 // Depending on server configuration, MediaWiki may forbid periods in URLs, due to an IE 6
106                                 // XSS bug. So let's escape them here. See WebRequest::checkUrlExtension() and T30235.
107                                 'origin=' + encodeURIComponent( origin ).replace( /\./g, '%2E' );
108                         newAjaxOptions = $.extend( {}, ajaxOptions, { url: url } );
109                 } else {
110                         newAjaxOptions = ajaxOptions;
111                 }
113                 return CoreForeignApi.parent.prototype.ajax.call( this, parameters, newAjaxOptions );
114         };
116         // Expose
117         mw.ForeignApi = CoreForeignApi;
119 }( mediaWiki, jQuery ) );