Merge "Reset all logging tables together"
[lhc/web/wiklou.git] / resources / src / mediawiki.ForeignApi.core.js
1 ( function () {
2
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 }
49
50 this.apiUrl = String( url );
51 this.anonymous = options && options.anonymous;
52
53 options = $.extend( /* deep=*/ true,
54 {
55 ajax: {
56 url: this.apiUrl,
57 xhrFields: {
58 withCredentials: !this.anonymous
59 }
60 },
61 parameters: {
62 origin: this.getOrigin()
63 }
64 },
65 options
66 );
67
68 // Call parent constructor
69 CoreForeignApi.parent.call( this, options );
70 }
71
72 OO.inheritClass( CoreForeignApi, mw.Api );
73
74 /**
75 * Return the origin to use for API requests, in the required format (protocol, host and port, if
76 * any).
77 *
78 * @protected
79 * @return {string|undefined}
80 */
81 CoreForeignApi.prototype.getOrigin = function () {
82 var origin, apiUri, apiOrigin;
83 if ( this.anonymous ) {
84 return '*';
85 }
86
87 origin = location.protocol + '//' + location.hostname;
88 if ( location.port ) {
89 origin += ':' + location.port;
90 }
91
92 apiUri = new mw.Uri( this.apiUrl );
93 apiOrigin = apiUri.protocol + '://' + apiUri.getAuthority();
94 if ( origin === apiOrigin ) {
95 // requests are not cross-origin, omit parameter
96 return undefined;
97 }
98
99 return origin;
100 };
101
102 /**
103 * @inheritdoc
104 */
105 CoreForeignApi.prototype.ajax = function ( parameters, ajaxOptions ) {
106 var url, origin, newAjaxOptions;
107
108 // 'origin' query parameter must be part of the request URI, and not just POST request body
109 if ( ajaxOptions.type === 'POST' ) {
110 url = ( ajaxOptions && ajaxOptions.url ) || this.defaults.ajax.url;
111 origin = ( parameters && parameters.origin ) || this.defaults.parameters.origin;
112 if ( origin !== undefined ) {
113 url += ( url.indexOf( '?' ) !== -1 ? '&' : '?' ) +
114 // Depending on server configuration, MediaWiki may forbid periods in URLs, due to an IE 6
115 // XSS bug. So let's escape them here. See WebRequest::checkUrlExtension() and T30235.
116 'origin=' + encodeURIComponent( origin ).replace( /\./g, '%2E' );
117 }
118 newAjaxOptions = $.extend( {}, ajaxOptions, { url: url } );
119 } else {
120 newAjaxOptions = ajaxOptions;
121 }
122
123 return CoreForeignApi.parent.prototype.ajax.call( this, parameters, newAjaxOptions );
124 };
125
126 // Expose
127 mw.ForeignApi = CoreForeignApi;
128
129 }() );