MediaWiki:Synchronizer.js

/** * Synchronizer is a tool for synchronizing Lua modules, templates and other pages across Wikimedia wikis * Documentation: https://www.mediawiki.org/wiki/Synchronizer * Author: User:Sophivorus * License: CC-BY-SA-3.0 */ var Synchronizer = {

init: function { Synchronizer.makeTable; },

makeTable: function { var $div = $( '#synchronizer' ); if ( mw.config.get( 'wgWikibaseItemId' ) ) { var $table = $( ' ' ); var $row = $( ' ' ); var $headerWiki = $( ' ' ).text( 'Wiki' ); var $headerLink = $( ' ' ).text( 'Link' ); var $headerStatus = $( ' ' ).text( 'Status' ); var $headerAction = $( ' ' ).text( 'Action' ); $row.append( $headerWiki, $headerLink, $headerStatus, $headerAction ); $table.append( $row ); $div.html( $table ); Synchronizer.getLocalData; } else { var page = mw.config.get( 'wgPageName' ); $div.addClass( 'error' ).text( 'No Wikidata item associated to ' + page ); }	},

getLocalData: function { var local = { wiki: mw.config.get( 'wgWikiID' ), title: mw.config.get( 'wgPageName' ), status: 'Local' };		new mw.Api.get( {			formatversion: 2,			action: 'query',			prop: 'revisions',			rvprop: 'timestamp|sha1|content',			rvslots: 'main',			titles: local.title		} ).done( function ( data ) {			var revision = data.query.pages[0].revisions[0];			local.time = new Date( revision.timestamp );			local.sha1 = revision.sha1;			local.content = revision.slots.main.content;

// We need to do a separate API call to get 500 revisions // because "content" is considered an "expensive" property // so if we request all together we only get 50 revisions new mw.Api.get( {				formatversion: 2,				action: 'query',				prop: 'revisions',				rvprop: 'sha1',				rvlimit: 'max',				rvslots: 'main',				titles: local.title			} ).done( function ( data ) {				//console.log( data );				var revisions = data.query.pages[0].revisions;				local.hashes = revisions.map( function ( revision ) { return revision.sha1; } );				Synchronizer.makeRow( local );				Synchronizer.getRemoteData( local );			} ); } );	},

getRemoteData: function ( local ) { var wikidataID = mw.config.get( 'wgWikibaseItemId' ); $.get( '//www.wikidata.org/wiki/Special:EntityData/' + wikidataID + '.json' ).done( function ( data ) {			var entities = data.entities;			var entity = entities[ wikidataID ];			var siteLinks = entity.sitelinks;			var siteLink;			for ( var key in siteLinks ) {				if ( key === local.wiki ) {					continue;				}				siteLink = siteLinks[ key ];				var remote = {					status: 'Loading',					wiki: siteLink.site,					title: siteLink.title,					url: siteLink.url,					api: siteLink.url.replace( /\/wiki\/.+/, '/w/api.php' ) // Hacky but efficient				};				Synchronizer.makeRow( remote, local );				Synchronizer.updateStatus( remote, local );			}		} ); },

updateStatus: function ( remote, local ) { new mw.ForeignApi( remote.api ).get( {			formatversion: 2,			action: 'query',			prop: 'revisions',			rvprop: 'timestamp|sha1',			rvslots: 'main',			titles: remote.title		} ).done( function ( data ) {			//console.log( data );			var revision = data.query.pages[0].revisions[0];			var sha1 = revision.sha1;			var time = new Date( revision.timestamp );			if ( sha1 === local.sha1 ) {				remote.status = 'Updated';			} else if ( local.hashes.includes( sha1 ) ) {				remote.status = 'Outdated';			} else {				remote.status = 'Forked';			}			Synchronizer.makeRow( remote, local );		} ); },

update: function ( remote, local ) { remote.status = 'Updating'; Synchronizer.makeRow( remote, local ); return new mw.ForeignApi( remote.api ).edit( remote.title, function ( revision ) {			var link1 =  + local.wiki + ':' + local.title + ;			var link2 = 'Synchronizer';			var summary = 'Update from ' + link1 + ' using ' + link2;			return {				text: local.content,				summary: summary,				assert: 'user'			};		} ).done( function {			remote.status = 'Updated';			remote.content = local.content;			Synchronizer.makeRow( remote, local );		} ); },

makeRow: function ( remote, local ) { var color, $button; switch ( remote.status ) { case 'Local': color = '#aff'; break; case 'Updated': color = '#afa'; break; case 'Outdated': color = '#ffa'; $button = $( ' ' ).text( 'Update' ).click( function  {					Synchronizer.update( remote, local );				} ); break; case 'Forked': color = '#faa'; $button = $( ' ' ).text( 'Update' ).click( function  {					var confirm = window.confirm( 'Carelessly updating a forked page may break things. Are you sure you want to continue?' );					if ( confirm ) {						Synchronizer.update( remote, local );					}				} ); break; }		var $link = $( '' ).text( remote.title ).attr( 'href', remote.url ); var $td1 = $( ' ' ).text( remote.wiki ); var $td2 = $( ' ' ).html( $link ); var $td3 = $( ' ' ).text( remote.status ).css( 'background-color', color ); var $td4 = $( ' ' ).html( $button ); var $table = $( '#synchronizer table' ); var $row = $table.find( '.' + remote.wiki ); if ( $row.length ) { $row.empty.append( $td1, $td2, $td3, $td4 ); } else { $row = $( ' ' ).addClass( remote.wiki ).append( $td1, $td2, $td3, $td4 ); $table.append( $row ); }	} };

mw.loader.using( 'mediawiki.ForeignApi', Synchronizer.init );