Merge "API: Allow anonymous CORS from anywhere, when specifically requested"
[lhc/web/wiklou.git] / resources / src / mediawiki / mediawiki.ForeignStructuredUpload.js
1 ( function ( mw, $, OO ) {
2 /**
3 * @class mw.ForeignStructuredUpload
4 * @extends mw.ForeignUpload
5 *
6 * Used to represent an upload in progress on the frontend.
7 *
8 * This subclass will upload to a wiki using a structured metadata
9 * system similar to (or identical to) the one on Wikimedia Commons.
10 *
11 * See <https://commons.wikimedia.org/wiki/Commons:Structured_data> for
12 * a more detailed description of how that system works.
13 *
14 * **TODO: This currently only supports uploads under CC-BY-SA 4.0,
15 * and should really have support for more licenses.**
16 *
17 * @inheritdoc
18 */
19 function ForeignStructuredUpload( target, apiconfig ) {
20 this.date = undefined;
21 this.descriptions = [];
22 this.categories = [];
23
24 // Config for uploads to local wiki.
25 // Can be overridden with foreign wiki config when #loadConfig is called.
26 this.config = mw.config.get( 'wgUploadDialog' );
27
28 mw.ForeignUpload.call( this, target, apiconfig );
29 }
30
31 OO.inheritClass( ForeignStructuredUpload, mw.ForeignUpload );
32
33 /**
34 * Get the configuration for the form and filepage from the foreign wiki, if any, and use it for
35 * this upload.
36 *
37 * @return {jQuery.Promise} Promise returning config object
38 */
39 ForeignStructuredUpload.prototype.loadConfig = function () {
40 var deferred,
41 upload = this;
42
43 if ( this.configPromise ) {
44 return this.configPromise;
45 }
46
47 if ( this.target === 'local' ) {
48 deferred = $.Deferred();
49 setTimeout( function () {
50 // Resolve asynchronously, so that it's harder to accidentally write synchronous code that
51 // will break for cross-wiki uploads
52 deferred.resolve( upload.config );
53 } );
54 this.configPromise = deferred.promise();
55 } else {
56 this.configPromise = this.apiPromise.then( function ( api ) {
57 // Get the config from the foreign wiki
58 return api.get( {
59 action: 'query',
60 meta: 'siteinfo',
61 siprop: 'uploaddialog',
62 // For convenient true/false booleans
63 formatversion: 2
64 } ).then( function ( resp ) {
65 // Foreign wiki might be running a pre-1.27 MediaWiki, without support for this
66 if ( resp.query && resp.query.uploaddialog ) {
67 upload.config = resp.query.uploaddialog;
68 return upload.config;
69 } else {
70 return $.Deferred().reject( 'upload-foreign-cant-load-config' );
71 }
72 }, function () {
73 return $.Deferred().reject( 'upload-foreign-cant-load-config' );
74 } );
75 } );
76 }
77
78 return this.configPromise;
79 };
80
81 /**
82 * Add categories to the upload.
83 *
84 * @param {string[]} categories Array of categories to which this upload will be added.
85 */
86 ForeignStructuredUpload.prototype.addCategories = function ( categories ) {
87 var i, category;
88
89 for ( i = 0; i < categories.length; i++ ) {
90 category = categories[ i ];
91 this.categories.push( category );
92 }
93 };
94
95 /**
96 * Empty the list of categories for the upload.
97 */
98 ForeignStructuredUpload.prototype.clearCategories = function () {
99 this.categories = [];
100 };
101
102 /**
103 * Add a description to the upload.
104 *
105 * @param {string} language The language code for the description's language. Must have a template on the target wiki to work properly.
106 * @param {string} description The description of the file.
107 */
108 ForeignStructuredUpload.prototype.addDescription = function ( language, description ) {
109 this.descriptions.push( {
110 language: language,
111 text: description
112 } );
113 };
114
115 /**
116 * Empty the list of descriptions for the upload.
117 */
118 ForeignStructuredUpload.prototype.clearDescriptions = function () {
119 this.descriptions = [];
120 };
121
122 /**
123 * Set the date of creation for the upload.
124 *
125 * @param {Date} date
126 */
127 ForeignStructuredUpload.prototype.setDate = function ( date ) {
128 this.date = date;
129 };
130
131 /**
132 * Get the text of the file page, to be created on upload. Brings together
133 * several different pieces of information to create useful text.
134 *
135 * @return {string}
136 */
137 ForeignStructuredUpload.prototype.getText = function () {
138 return this.config.format.filepage
139 // Replace "named parameters" with the given information
140 .replace( '$DESCRIPTION', this.getDescriptions() )
141 .replace( '$DATE', this.getDate() )
142 .replace( '$SOURCE', this.getSource() )
143 .replace( '$AUTHOR', this.getUser() )
144 .replace( '$LICENSE', this.getLicense() )
145 .replace( '$CATEGORIES', this.getCategories() );
146 };
147
148 /**
149 * @inheritdoc
150 */
151 ForeignStructuredUpload.prototype.getComment = function () {
152 var
153 isLocal = this.target === 'local',
154 comment = typeof this.config.comment === 'string' ?
155 this.config.comment :
156 this.config.comment[ isLocal ? 'local' : 'foreign' ];
157 return comment
158 .replace( '$PAGENAME', mw.config.get( 'wgPageName' ) )
159 .replace( '$HOST', location.host );
160 };
161
162 /**
163 * Gets the wikitext for the creation date of this upload.
164 *
165 * @private
166 * @return {string}
167 */
168 ForeignStructuredUpload.prototype.getDate = function () {
169 if ( !this.date ) {
170 return '';
171 }
172
173 return this.date.toString();
174 };
175
176 /**
177 * Fetches the wikitext for any descriptions that have been added
178 * to the upload.
179 *
180 * @private
181 * @return {string}
182 */
183 ForeignStructuredUpload.prototype.getDescriptions = function () {
184 var i, desc, templateCalls = [];
185
186 for ( i = 0; i < this.descriptions.length; i++ ) {
187 desc = this.descriptions[ i ];
188 templateCalls.push(
189 this.config.format.description
190 .replace( '$LANGUAGE', desc.language )
191 .replace( '$TEXT', desc.text )
192 );
193 }
194
195 return templateCalls.join( '\n' );
196 };
197
198 /**
199 * Fetches the wikitext for the categories to which the upload will
200 * be added.
201 *
202 * @private
203 * @return {string}
204 */
205 ForeignStructuredUpload.prototype.getCategories = function () {
206 var i, cat, categoryLinks = [];
207
208 if ( this.categories.length === 0 ) {
209 return this.config.format.uncategorized;
210 }
211
212 for ( i = 0; i < this.categories.length; i++ ) {
213 cat = this.categories[ i ];
214 categoryLinks.push( '[[Category:' + cat + ']]' );
215 }
216
217 return categoryLinks.join( '\n' );
218 };
219
220 /**
221 * Gets the wikitext for the license of the upload.
222 *
223 * @private
224 * @return {string}
225 */
226 ForeignStructuredUpload.prototype.getLicense = function () {
227 return this.config.format.license;
228 };
229
230 /**
231 * Get the source. This should be some sort of localised text for "Own work".
232 *
233 * @private
234 * @return {string}
235 */
236 ForeignStructuredUpload.prototype.getSource = function () {
237 return this.config.format.ownwork;
238 };
239
240 /**
241 * Get the username.
242 *
243 * @private
244 * @return {string}
245 */
246 ForeignStructuredUpload.prototype.getUser = function () {
247 var username, namespace;
248 // Do not localise, we don't know the language of target wiki
249 namespace = 'User';
250 username = mw.config.get( 'wgUserName' );
251 if ( !username ) {
252 // The user is not logged in locally. However, they might be logged in on the foreign wiki.
253 // We should record their username there. (If they're not logged in there either, this will
254 // record the IP address.) It's also possible that the user opened this dialog, got an error
255 // about not being logged in, logged in in another browser tab, then continued uploading.
256 username = '{{subst:REVISIONUSER}}';
257 }
258 return '[[' + namespace + ':' + username + '|' + username + ']]';
259 };
260
261 mw.ForeignStructuredUpload = ForeignStructuredUpload;
262 }( mediaWiki, jQuery, OO ) );