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