MediaWiki:Gadget-slurpInterwiki.js

From Wikidata
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/*  _____________________________________________________________________________________________________
 * |                                                                                                     |
 * |                    === WARNING: GLOBAL GADGET FILE ===                                              |
 * |                  Changes to this page affect many users.                                            |
 * | Please discuss changes on the talk page or on [[MediaWiki talk:Gadgets-definition]] before editing. |
 * |____________________________________________________________________________________________________ |
 *
 */
( function ( mw, $ ) {
	'use strict';

	var itemId = mw.config.get( 'wbEntityId' );
	if ( !itemId || mw.config.get( 'wgNamespaceNumber' ) !== 0 ||
		!mw.config.get( 'wbIsEditView' ) ||
		!mw.config.get( 'wgIsProbablyEditable' )
	) {
		return;
	}

	var i18nData = require( './slurpInterwiki-i18n.json' );

	var chain = mw.language.getFallbackLanguageChain(),
		len = chain.length,
		messages = {},
		i;
	for ( i = len - 1; i >= 0; i-- ) {
		if ( i18nData.hasOwnProperty( chain[ i ] ) ) {
			$.extend( messages, i18nData[ chain[ i ] ] );
		}
	}
	mw.messages.set( messages );

	var specialWikis = {
		'commonswiki': 'commons',
		'metawiki': 'meta',
		'mediawikiwiki': 'mediawiki',
		'specieswiki': 'species',
		'wikidatawiki': 'wikidata'
	};

	var notification = mw.util.getParamValue( 'slurpnotif' );
	if ( notification !== null ) {
		var notifications = notification.split( '|' );
		for ( i in notifications ) {
			mw.notify( mw.message( notifications[ i ] ) );
		}
	}

	/**
	 * Return the language code from the ID of the Wiki
	 */
	function getLanguageCodeFromWikiId( wiki ) {
		var splited = wiki.match( /^(.*)wik[it][a-z]*$/i );
		if ( !splited[ 1 ] ) {
			return '';
		} else {
			return splited[ 1 ].replace( /_/g, '-' );
		}
	}

	/**
	 * Return the site group from the ID of the Wiki
	 */
	function getGroupFromWikiId( wiki ) {
		if ( wiki.match( /^.*wikivoyage$/i ) ) {
			return 'wikivoyage';
		} else if ( wiki.match( /^.*wikisource$/i ) ) {
			return 'wikisource';
		} else if ( wiki.match( /^.*wikiquote$/i ) ) {
			return 'wikiquote';
		} else if ( wiki.match( /^.*wikinews$/i ) ) {
			return 'wikinews';
		} else if ( wiki.match( /^.*wikibooks$/i ) ) {
			return 'wikibooks';
		} else if ( wiki.match( /^.*wikiversity$/i ) ) {
			return 'wikiversity';
		} else if ( wiki.match( /^.*wiktionary$/i ) ) {
			return 'wiktionary';
		} else if ( wiki.match( /^.*wiki$/i ) ) {
			return 'wikipedia';
		} else { // TODO other groups
			return '';
		}
	}

	/**
	 * Called on error
	 */
	function onError() {
		$.removeSpinner( 'slurpInterwiki' );
		$( '#slurpInterwiki-button-import' ).button( 'option', 'disabled', false );
	}

	/**
	 * Called on API error
	 */
	function onApiError( jqXHR, textStatus, errorThrown ) {
		alert( mw.msg( 'error-api' ) );
		onError();
	}

	/**
	 * Return the item
	 * @param success function called on success
	 */
	function getItem( success ) {
		new mw.Api().get( {
			formatversion: 2,
			action: 'wbgetentities',
			ids: itemId
		} )
		.done( function ( data ) {
			if ( data.success && data.entities[ itemId ] ) {
				success( data.entities[ itemId ] );
			} else {
				onApiError();
			}
		} )
		.fail( onApiError );
	}

	/**
	 * Return the existings links of a wiki
	 * @param success function called on success
	 */
	function getItemLinks( success ) {
		getItem( function ( item ) {
			success( item.sitelinks );
		} );
	}

	/**
	 * Return the existings links in a page of a wiki
	 * @param language string language of the wiki.
	 * @param group string wiki group like 'wikipedia'
	 * @param page string name of the page
	 * @param success function called on success
	 */
	function getLanguageLinks( language, group, page, success ) {
		$.ajax( {
			url: '//' + language + '.' + group + '.org/w/api.php',
			data: {
				format: 'json',
				formatversion: 2,
				action: 'query',
				titles: page,
				prop: 'langlinks',
				lllimit: 400
			},
			dataType: 'jsonp',
			cache: true // Do not add the parameter _ which generates the warning "Unrecognized parameter: '_'"
		} )
		.done( function ( data ) {
			if ( data.query.pages ) {
				$.each( data.query.pages, function ( index, page ) {
					if ( page.missing ) {
						alert( mw.msg( 'error-page' ) );
						onError();
					} else {
						success( page.langlinks || [] );
					}
				} );
			} else {
				onApiError();
			}
		} )
		.fail( onApiError );
	}

	/**
	 * Set the item links
	 * @param item array the item
	 * @param success function called on success
	 * @param summary string
	 */
	function setItem( item, success, summary ) {
		new mw.Api().postWithToken( 'csrf', {
			formatversion: 2,
			action: 'wbeditentity',
			id: itemId,
			data: JSON.stringify( item ),
			summary: summary
		} )
		.done( success )
		.fail( function ( code, data ) {
			function link( match, $1, $2 ) {
				return mw.html.element( 'a', { href: mw.util.getUrl( $1 ) }, $2 );
			}

			if ( data.error && data.error.info ) {
				var errorInfo = data.error.info
				.replace( /\[\[(([^\]\|]+))\]\]/g, link )
				.replace( /\[\[([^\]\|]+)\|([^\]]*)\]\]/g, link )
				.replace( /\wiki:/g, ':' ) // Correcting wiki link
				.replace( /\*/g, '<br>*' ); // To have each errors in a line
				$( '#slurpInterwiki-error' ).html( mw.message( 'error-save', errorInfo ).parse() );
				onError();
			} else {
				onApiError();
			}
		} );
	}

	/**
	 * Update interLanguages links from the wiki selected in the box. Works only for Wikipedias.
	 */
	function work() {
		$.createSpinner( {
			size: 'large',
			type: 'block',
			id: 'slurpInterwiki'
		} ).appendTo( 'div#slurpInterwiki' );
		$( '#slurpInterwiki-button-import' ).button( 'option', 'disabled', true );

		getItem( function ( item ) {
			var links = {};
			if ( item.sitelinks ) {
				links = item.sitelinks;
			}

			var wiki = $( '#slurpInterwiki-lang' ).val();
			if ( !links[ wiki ] ) {
				alert( mw.msg( 'error-pageNotFound' ) );
				onError();
				return;
			}
			var page = links[ wiki ].title;
			var langCode = getLanguageCodeFromWikiId( wiki );
			var group = getGroupFromWikiId( wiki );
			if ( specialWikis[ wiki ] || langCode === '' || group === '' ) {
				alert( mw.msg( 'error-wiki' ) );
				onError();
				return [];
			}
			getLanguageLinks( langCode, group, page, function ( interlangLinks ) {
				getItem( function ( item ) {
					var itemValue = {};
					var changed = [];

					var itemLinks = {};
					if ( item.sitelinks ) {
						itemLinks = item.sitelinks;
					}
					var linkChanged = false;
					$.each( interlangLinks, function ( i, link ) {
						var regex = /^(.*)old$/i;
						if ( regex.test( interlangLinks[ i ].lang ) && link.lang !== 'be-x-old' ) {
							mw.log( 'Refused wiki: ' + link.lang );
						} else {
							var site = link.lang.replace( /-/g, '_' );
							site = site === 'nb' ? 'no' : site;
							site += ( group === 'wikipedia' ) ? 'wiki' : group;
							if ( !itemLinks[ site ] && link.title.indexOf( '#' ) === -1 ) {
								itemLinks[ site ] = {
									'site': site,
									'title': link.title
								};
								linkChanged = true;
							}
						}
					} );
					if ( linkChanged ) {
						itemValue.sitelinks = itemLinks;
						changed.push( 'sitelinks' );
					}

					// Update labels
					if ( $( '#slurpInterwiki-updateLabels' ).prop( 'checked' ) ) {
						var labels = {};
						if ( item.labels ) {
							labels = item.labels;
						}
						var labelChanged = false;
						$.each( itemLinks, function ( site, link ) {
							if ( specialWikis[ link.site ] ) {
								return;
							}

							var langs = [ getLanguageCodeFromWikiId( link.site ) ];
							if ( langs[ 0 ] === 'simple' ) {
								return;
							}
							switch ( langs[ 0 ] ) {
								case 'simple':
									langs = [ 'en' ];
									break;
								/*case 'en':
								case 'simple':
									langs = [ 'en', 'en-ca', 'en-gb' ];
									break;
								case 'zh':
									langs = [ 'zh', 'zh-cn', 'zh-tw', 'zh-sg', 'zh-mo', 'zh-hans', 'zh-hant', 'zh-hk' ];
									break;
								case 'pt':
									langs = [ 'pt', 'pt-br' ];
									break;
								case 'de':
									langs = [ 'de', 'de-ch' ];
									break;*/
								case 'no':
									langs = [ 'nb' ];
									break;
								case 'als':
									langs = [ 'gsw' ];
									break;
								case 'crh':
									langs = [ 'crh-latn' ];
									break;
								case 'bat-smg':
									langs = [ 'sgs' ];
									break;
								case 'be-x-old':
									langs = [ 'be-tarask' ];
									break;
								case 'bh':
									langs = [ 'bho' ];
									break;
								case 'fiu-vro':
									langs = [ 'vro' ];
									break;
								case 'roa-rup':
									langs = [ 'rup' ];
									break;
								case 'zh-classical':
									langs = [ 'lzh' ];
									break;
								case 'zh-min-nan':
									langs = [ 'nan' ];
									break;
								case 'zh-yue':
									langs = [ 'yue' ];
									break;
							}
							$.each( langs, function ( j, language ) {
								if ( !labels[ language ] ) {
									var value = itemLinks[ site ].title; // .replace( /\(.*\)/g, '' )
									switch ( language ) {
										case 'es':
										case 'pt':
										case 'pt-br':
											value = value.replace( /^Anexo:/, '' );
											break;
										case 'cs':
											value = value.replace( /^Příloha:/, '' );
											break;
										case 'de-ch':
											value = value.replace( /ß/g, 'ss' );
											break;
										case 'fa':
											value = itemLinks[ site ].title; // In Farsi () are important
											break;
									}
									labels[ language ] = {
										'language': language,
										'value': value
									};
									labelChanged = true;
								}
							} );
						} );
						if ( labelChanged ) {
							itemValue.labels = labels;
							changed.push( 'labels' );
						}
					}

					// Push changes
					if ( changed.length !== 0 ) {
						// Get list of notification and comment
						var notif = [];
						var comment = '';
						if ( $.inArray( 'sitelinks', changed ) !== -1 ) {
							notif.push( 'success-links' );
							comment += 'Update of interwikis from ' + wiki + '.';
						}
						if ( $.inArray( 'labels', changed ) !== -1 ) {
							notif.push( 'success-labels' );
							comment += ' Update of labels.';
						}

						setItem( itemValue, function () {
							location.href = mw.util.getUrl( mw.config.get( 'wgPageName' ) ) + '?slurpnotif=' + notif.join( '|' );
						}, comment );
					} else {
						mw.notify( mw.message( 'nolinkstoupdate' ) );
						$( 'div#slurpInterwiki' ).dialog( 'close' );
						onError();
					}
				} );
			} );
		} );
	}

	/**
	 * Show the dialog
	 */
	function show() {
		getItemLinks( function ( links ) {
			var formFind = $( '#slurpInterwiki-lang' );
			formFind.empty();
			var languagesLabel = mw.config.get( 'wgULSLanguages' ) || {};
			$.each( links, function ( i, link ) {
				if ( specialWikis[ link.site ] ) {
					return;
				}

				var languageCode = getLanguageCodeFromWikiId( link.site );
				var label = link.site;
				if ( languageCode !== '' ) {
					label = languageCode + '.' + getGroupFromWikiId( link.site );
					if ( languagesLabel[ languageCode ] ) {
						label += ' (' + languagesLabel[ languageCode ] + ')';
					}
				}
				formFind.append(
					$( '<option>' )
					.attr( 'value', links[ i ].site )
					.text( label )
				);
			} );
			$( '#slurpInterwiki-error' ).html( '' );
			$( 'div#slurpInterwiki' ).dialog( 'open' );
		} );
		return false;
	}

	/**
	 * Create the dialog and add a link in toolBox
	 */
	function init() {
		$( '<div>' ).attr( {
			id: 'slurpInterwiki',
			title: mw.msg( 'box-title' )
		} ).append(
			$( '<form>' ).append(
				$( '<p>' ).append(
					$( '<label for="slurpInterwiki-lang">' )
					.text( mw.msg( 'box-lang' ) ),
					$( '<select name="slurpInterwiki-lang" id="slurpInterwiki-lang">' )
				),
				$( '<p>' ).append(
					$( '<input checked type="checkbox" name="slurpInterwiki-updateLabels" id="slurpInterwiki-updateLabels">' ),
					' ',
					$( '<label for="slurpInterwiki-updateLabels">' )
					.text( mw.msg( 'box-updateLabels' ) )
				),
				$( '<p>' ).text( mw.msg( 'box-warning' ) ),
				$( '<p class="error" id="slurpInterwiki-error">' )
			)
		)
		.appendTo( '#content' )
		.dialog( {
			autoOpen: false,
			modal: true,
			width: 500,
			buttons: [ {
				id: 'slurpInterwiki-button-import',
				text: mw.msg( 'import' ),
				click: work
			} ]
		} );

		var portletLink = mw.util.addPortletLink( 'p-tb', '#', mw.msg( 'box-title' ), 't-slurpInterwiki', mw.msg( 'box-title' ) );
		$( portletLink ).click( show );
	}

	$( init );
} ( mediaWiki, jQuery ) );