3 * @classdesc Upload to another MediaWiki site using structured metadata.
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.
10 * TODO: This currently only supports uploads under CC-BY-SA 4.0,
11 * and should really have support for more licenses.
13 * @class ForeignStructuredUpload
15 * @extends mw.ForeignUpload
18 * @description Used to represent an upload in progress on the frontend.
19 * @param {string} [target]
20 * @param {Object} [apiconfig]
22 function ForeignStructuredUpload( target, apiconfig ) {
23 this.date = undefined;
24 this.descriptions = [];
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 );
34 OO.inheritClass( ForeignStructuredUpload, mw.ForeignUpload );
37 * Get the configuration for the form and filepage from the foreign wiki, if any, and use it for
40 * @return {jQuery.Promise} Promise returning config object
42 ForeignStructuredUpload.prototype.loadConfig = function () {
43 if ( this.configPromise ) {
44 return this.configPromise;
47 if ( this.target === 'local' ) {
48 const deferred = $.Deferred();
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 );
54 this.configPromise = deferred.promise();
56 this.configPromise = this.apiPromise.then(
57 // Get the config from the foreign wiki
61 siprop: 'uploaddialog',
62 // For convenient true/false booleans
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;
70 return $.Deferred().reject( 'upload-foreign-cant-load-config' );
72 }, () => $.Deferred().reject( 'upload-foreign-cant-load-config' ) )
76 return this.configPromise;
80 * Add categories to the upload.
82 * @param {string[]} categories Array of categories to which this upload will be added.
84 ForeignStructuredUpload.prototype.addCategories = function ( categories ) {
85 this.categories.push( ...categories );
89 * Empty the list of categories for the upload.
91 ForeignStructuredUpload.prototype.clearCategories = function () {
96 * Add a description to the upload.
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.
101 ForeignStructuredUpload.prototype.addDescription = function ( language, description ) {
102 this.descriptions.push( {
109 * Empty the list of descriptions for the upload.
111 ForeignStructuredUpload.prototype.clearDescriptions = function () {
112 this.descriptions = [];
116 * Set the date of creation for the upload.
120 ForeignStructuredUpload.prototype.setDate = function ( date ) {
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.
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() );
144 ForeignStructuredUpload.prototype.getComment = function () {
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' );
152 .replace( '$PAGENAME', pagename.replace( /_/g, ' ' ) )
153 .replace( '$HOST', location.host );
157 * Gets the wikitext for the creation date of this upload.
162 ForeignStructuredUpload.prototype.getDate = function () {
167 return this.date.toString();
171 * Fetches the wikitext for any descriptions that have been added
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' );
184 * Fetches the wikitext for the categories to which the upload will
190 ForeignStructuredUpload.prototype.getCategories = function () {
191 if ( this.categories.length === 0 ) {
192 return this.config.format.uncategorized;
195 return this.categories.map( ( cat ) => '[[Category:' + cat + ']]' ).join( '\n' );
199 * Gets the wikitext for the license of the upload.
204 ForeignStructuredUpload.prototype.getLicense = function () {
205 return this.config.format.license;
209 * Get the source. This should be some sort of localised text for "Own work".
214 ForeignStructuredUpload.prototype.getSource = function () {
215 return this.config.format.ownwork;
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' );
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}}';
235 return '[[' + namespace + ':' + username + '|' + username + ']]';
238 mw.ForeignStructuredUpload = ForeignStructuredUpload;