Localisation updates from https://translatewiki.net.
[mediawiki.git] / resources / src / mediawiki.widgets / MediaSearch / mw.widgets.MediaResourceProvider.js
blob87089433caab064a192214f64dec9b3b4953df0e
1 /*!
2  * MediaWiki Widgets - MediaResourceProvider class.
3  *
4  * @copyright 2011-2016 VisualEditor Team and others; see AUTHORS.txt
5  * @license The MIT License (MIT); see LICENSE.txt
6  */
7 ( function () {
9         /**
10          * @classdesc Media resource provider.
11          *
12          * @class
13          * @extends mw.widgets.APIResultsProvider
14          *
15          * @constructor
16          * @description Create an instance of `mw.widgets.MediaResourceProvider`.
17          * @param {string} apiurl The API url
18          * @param {Object} [config] Configuration options
19          * @param {string} [config.scriptDirUrl] The url of the API script
20          */
21         mw.widgets.MediaResourceProvider = function MwWidgetsMediaResourceProvider( apiurl, config ) {
22                 config = config || {};
24                 // Parent constructor
25                 mw.widgets.MediaResourceProvider.super.call( this, apiurl, config );
27                 // Fetching configuration
28                 this.scriptDirUrl = config.scriptDirUrl;
29                 this.isLocal = config.local !== undefined;
31                 if ( this.isLocal ) {
32                         this.setAPIurl( mw.util.wikiScript( 'api' ) );
33                 } else {
34                         // If 'apiurl' is set, use that. Otherwise, build the url
35                         // from scriptDirUrl and /api.php suffix
36                         this.setAPIurl( this.getAPIurl() || ( this.scriptDirUrl + '/api.php' ) );
37                 }
39                 this.siteInfoPromise = null;
40                 this.thumbSizes = [];
41                 this.imageSizes = [];
42         };
44         /* Inheritance */
45         OO.inheritClass( mw.widgets.MediaResourceProvider, mw.widgets.APIResultsProvider );
47         /* Methods */
49         /**
50          * @inheritdoc
51          */
52         mw.widgets.MediaResourceProvider.prototype.getStaticParams = function () {
53                 return Object.assign(
54                         {},
55                         // Parent method
56                         mw.widgets.MediaResourceProvider.super.prototype.getStaticParams.call( this ),
57                         {
58                                 action: 'query',
59                                 iiprop: 'dimensions|url|mediatype|extmetadata|timestamp|user',
60                                 iiextmetadatalanguage: this.getLang(),
61                                 prop: 'imageinfo'
62                         }
63                 );
64         };
66         /**
67          * Initialize the source and get the site info.
68          *
69          * Connect to the api url and retrieve the siteinfo parameters
70          * that are required for fetching results.
71          *
72          * @return {jQuery.Promise} Promise that resolves when the class
73          * properties are set.
74          */
75         mw.widgets.MediaResourceProvider.prototype.loadSiteInfo = function () {
77                 if ( !this.siteInfoPromise ) {
78                         this.siteInfoPromise = new mw.Api().get( {
79                                 action: 'query',
80                                 meta: 'siteinfo'
81                         } )
82                                 .then( ( data ) => {
83                                         this.setImageSizes( data.query.general.imagelimits || [] );
84                                         this.setThumbSizes( data.query.general.thumblimits || [] );
85                                         this.setUserParams( {
86                                                 // Standard width per resource
87                                                 iiurlwidth: this.getStandardWidth()
88                                         } );
89                                 } );
90                 }
91                 return this.siteInfoPromise;
92         };
94         /**
95          * Override parent method and get results from the source.
96          *
97          * @param {number} [howMany] The number of items to pull from the API
98          * @return {jQuery.Promise} Promise that is resolved into an array
99          * of available results, or is rejected if no results are available.
100          */
101         mw.widgets.MediaResourceProvider.prototype.getResults = function ( howMany ) {
102                 let xhr,
103                         aborted = false;
105                 return this.loadSiteInfo()
106                         .then( () => {
107                                 if ( aborted ) {
108                                         return $.Deferred().reject();
109                                 }
110                                 xhr = this.fetchAPIresults( howMany );
111                                 return xhr;
112                         } )
113                         .then(
114                                 ( results ) => {
115                                         if ( !results || results.length === 0 ) {
116                                                 this.toggleDepleted( true );
117                                                 return [];
118                                         }
119                                         return results;
120                                 },
121                                 // Process failed, return an empty promise
122                                 () => {
123                                         this.toggleDepleted( true );
124                                         return $.Deferred().resolve( [] );
125                                 }
126                         )
127                         .promise( { abort: function () {
128                                 aborted = true;
129                                 if ( xhr ) {
130                                         xhr.abort();
131                                 }
132                         } } );
133         };
135         /**
136          * Get continuation API data.
137          *
138          * @param {number} howMany The number of results to retrieve
139          * @return {Object} API request data
140          */
141         mw.widgets.MediaResourceProvider.prototype.getContinueData = function () {
142                 return {};
143         };
145         /**
146          * Set continuation data for the next page.
147          *
148          * @param {Object} continueData Continuation data
149          */
150         mw.widgets.MediaResourceProvider.prototype.setContinue = function () {
151         };
153         /**
154          * Sort the results.
155          *
156          * @param {Object[]} results API results
157          * @return {Object[]} Sorted results
158          */
159         mw.widgets.MediaResourceProvider.prototype.sort = function ( results ) {
160                 return results;
161         };
163         /**
164          * Call the API for search results.
165          *
166          * @param {number} howMany The number of results to retrieve
167          * @return {jQuery.Promise} Promise that resolves with an array of objects that contain
168          *  the fetched data.
169          */
170         mw.widgets.MediaResourceProvider.prototype.fetchAPIresults = function ( howMany ) {
171                 if ( !this.isValid() ) {
172                         return $.Deferred().reject().promise( { abort: function () {} } );
173                 }
175                 const api = this.isLocal ? new mw.Api() : new mw.ForeignApi( this.getAPIurl(), { anonymous: true } );
176                 const xhr = api.get( Object.assign( {}, this.getStaticParams(), this.getUserParams(), this.getContinueData( howMany ) ) );
177                 return xhr
178                         .then( ( data ) => {
179                                 const results = [];
181                                 if ( data.error ) {
182                                         this.toggleDepleted( true );
183                                         return [];
184                                 }
186                                 if ( data.continue ) {
187                                         // Update the offset for next time
188                                         this.setContinue( data.continue );
189                                 } else {
190                                         // This is the last available set of results. Mark as depleted!
191                                         this.toggleDepleted( true );
192                                 }
194                                 // If the source returned no results, it will not have a
195                                 // query property
196                                 if ( data.query ) {
197                                         const raw = data.query.pages;
198                                         if ( raw ) {
199                                                 // Strip away the page ids
200                                                 for ( const page in raw ) {
201                                                         if ( !raw[ page ].imageinfo ) {
202                                                                 // The search may give us pages that belong to the File:
203                                                                 // namespace but have no files in them, either because
204                                                                 // they were deleted or imported wrongly, or just started
205                                                                 // as pages. In that case, the response will not include
206                                                                 // imageinfo. Skip those files.
207                                                                 continue;
208                                                         }
209                                                         const newObj = raw[ page ].imageinfo[ 0 ];
210                                                         newObj.title = raw[ page ].title;
211                                                         newObj.index = raw[ page ].index;
212                                                         results.push( newObj );
213                                                 }
214                                         }
215                                 }
216                                 return this.sort( results );
217                         } )
218                         .promise( { abort: xhr.abort } );
219         };
221         /**
222          * Set name.
223          *
224          * @param {string} name
225          */
226         mw.widgets.MediaResourceProvider.prototype.setName = function ( name ) {
227                 this.name = name;
228         };
230         /**
231          * Get name.
232          *
233          * @return {string} name
234          */
235         mw.widgets.MediaResourceProvider.prototype.getName = function () {
236                 return this.name;
237         };
239         /**
240          * Get standard width, based on the provider source's thumb sizes.
241          *
242          * @return {number|undefined} fetchWidth
243          */
244         mw.widgets.MediaResourceProvider.prototype.getStandardWidth = function () {
245                 return ( this.thumbSizes && this.thumbSizes[ this.thumbSizes.length - 1 ] ) ||
246                         ( this.imageSizes && this.imageSizes[ 0 ] ) ||
247                         // Fall back on a number
248                         300;
249         };
251         /**
252          * Get prop.
253          *
254          * @return {string} prop
255          */
256         mw.widgets.MediaResourceProvider.prototype.getFetchProp = function () {
257                 return this.fetchProp;
258         };
260         /**
261          * Set prop.
262          *
263          * @param {string} prop
264          */
265         mw.widgets.MediaResourceProvider.prototype.setFetchProp = function ( prop ) {
266                 this.fetchProp = prop;
267         };
269         /**
270          * Set thumb sizes.
271          *
272          * @param {number[]} sizes Available thumbnail sizes
273          */
274         mw.widgets.MediaResourceProvider.prototype.setThumbSizes = function ( sizes ) {
275                 this.thumbSizes = sizes;
276         };
278         /**
279          * Set image sizes.
280          *
281          * @param {number[]} sizes Available image sizes
282          */
283         mw.widgets.MediaResourceProvider.prototype.setImageSizes = function ( sizes ) {
284                 this.imageSizes = sizes;
285         };
287         /**
288          * Get thumb sizes.
289          *
290          * @return {number[]} sizes Available thumbnail sizes
291          */
292         mw.widgets.MediaResourceProvider.prototype.getThumbSizes = function () {
293                 return this.thumbSizes;
294         };
296         /**
297          * Get image sizes.
298          *
299          * @return {number[]} sizes Available image sizes
300          */
301         mw.widgets.MediaResourceProvider.prototype.getImageSizes = function () {
302                 return this.imageSizes;
303         };
305         /**
306          * Check if this source is valid.
307          *
308          * @return {boolean} Source is valid
309          */
310         mw.widgets.MediaResourceProvider.prototype.isValid = function () {
311                 return this.isLocal ||
312                         // If we don't have either 'apiurl' or 'scriptDirUrl'
313                         // the source is invalid, and we will skip it
314                         this.apiurl !== undefined ||
315                         this.scriptDirUrl !== undefined;
316         };
317 }() );