User:NatigKrolik/Rollback.js

/** * Rollback.js by User:NatigKrolik * experimental * provides the 'rollback' link to non-rollbackers * THIS SOFTWARE IS PROVIDED "AS IS", YOU OWN ANY RESPONSIBILITY WHILE USING IT * !!! not intended for use by vandals !!! */ /* global $, mw */ $( document ).ready( function {	'use strict';

var api, $undoLink, $ntitle, specialPage, userGroups, SHOW_ROLLBACK_EDIT_COUNT, modules, diffOnRb, botMode;

$undoLink = $( 'ul#pagehistory li:first-child span.mw-history-undo' ); $ntitle = $( '#mw-diff-ntitle2' ); specialPage = mw.config.get( 'wgCanonicalSpecialPageName' ); if (		$undoLink.length === 0 &&		( $ntitle.length === 0 || $( '#differences-nextlink' ).length > 0 ) &&		[			'Contributions',			'Watchlist',			'Recentchanges',			'Recentchangeslinked'		].indexOf( specialPage ) === -1	) { return;// no [rollback] button to insert }	// TODO: auto-patrol reverted changes

userGroups = mw.config.get( 'wgUserGroups' ); if ( userGroups.indexOf( 'rollbacker' ) !== -1 || userGroups.indexOf( 'sysop' ) !== -1 ) { return;// user is rollbacker, no need for these buttons }

// https://www.mediawiki.org/wiki/Manual:$wgShowRollbackEditCount SHOW_ROLLBACK_EDIT_COUNT = 10;

modules = [ 'mediawiki.api' ]; diffOnRb = ( mw.user.options.get( 'norollbackdiff' ) !== 1 ); if ( diffOnRb ) { modules.push( 'mediawiki.action.history.diff' ); }	botMode = mw.util.getParamValue( 'bot' );

function loadMessages( messages ) { return api.get( {			action: 'query',			formatversion: 2,			meta: 'allmessages',			amlang: mw.config.get( 'wgUserLanguage' ),			ammessages: messages		} ) .done( function ( data ) {			$.each( data.query.allmessages, function ( i, e ) { mw.messages.set( e.name, e.content ); } );		} );	}

function showLatestDiff( pageTitle ) { api.get( {			action: 'query',			formatversion: 2,			prop: 'revisions',			titles: pageTitle,			rvdiffto: 'prev',			rvlimit: 1		} ).done( function ( rev ) {			$( '#mw-content-text' ).append( ' '			);		} );	}

function pushHistory( url ) { window.history.pushState( { path: window.location.href }, '', url ); }

function onGotRevisions( rbl, pageTitle, lastEditor, undoRevid, data ) { var page, revision, rbSummary, rbSuccess, rbPostConfig; $.each( data.query.allmessages, function ( i, e ) {			mw.messages.set( e.name, e.content );		} ); page = data.query.pages[ 0 ]; if ( page.revisions === undefined || page.revisions.length === 0 ) { // the last editor of the page is its single one pushHistory( rbl.href ); $( '#firstHeading' ).text( mw.message( 'rollbackfailed' ).text ); $( '#p-views ul>li' ).removeClass( 'selected' ); $( '#contentSub' ).empty; $( '#mw-content-text' ).text( mw.message( 'cantrollback' ).text ); return; }		revision = page.revisions[ 0 ]; rbSummary = mw.message( 'revertpage', revision.user, lastEditor ).text; rbSuccess = mw.message( 'rollback-success', lastEditor, revision.user ).text; rbPostConfig = { action: 'edit', undo: undoRevid, undoafter: revision.revid, title: pageTitle, summary: rbSummary, minor: 1 };		if ( botMode !== null ) { rbPostConfig.bot = botMode; }		api.postWithToken( 'csrf', rbPostConfig ) .done( function {			pushHistory( rbl.href );			$( '#p-views ul>li' ).removeClass( 'selected' );			$( '#contentSub' ).empty;			$( '#firstHeading' ).text( mw.message( 'actioncomplete' ).text );			$( '#mw-content-text' ).empty.append( $( ' ' ).text( rbSuccess ) )			.append( $( ' ' ).attr( 'id', 'mw-returnto' ).html(					mw.message( 'returnto', '' + pageTitle.replace( /_/g, ' ' ) + '' ).text				) );			if ( diffOnRb ) {				showLatestDiff( pageTitle );			}		} ) .fail( function ( code, data ) {			pushHistory( rbl.href );			$( '#firstHeading' ).text( mw.message( 'rollbackfailed' ).text );			$( '#mw-content-text' ).text( data && data.error && data.error.info ? data.error.info : '' );		} ); }

function onRbLinkClick( rbl, pageTitle, lastEditor, undoRevid ) { api.get( {			// require the revision id and the user name			// of the last revision not made by the last user			action: 'query',			formatversion: 2,			prop: 'revisions',			rvlimit: 1,			titles: pageTitle,			rvexcludeuser: lastEditor,			rvprop: 'ids|user',			meta: 'allmessages',			amlang: mw.config.get( 'wgContentLanguage' ),			ammessages: 'revertpage'		} ) .done( onGotRevisions.bind( null, rbl, pageTitle, lastEditor, undoRevid ) ); }

function getRbLink( pageTitle, lastEditor, undoRevid, rbLinkHrefConfig, rbLinkText ) { return $( '' ) .attr( {			href: mw.util.wikiScript + '?' + $.param( rbLinkHrefConfig ),			title: mw.message( 'tooltip-rollback' ).text		} ) .text( rbLinkText ) .click( function ( event ) {			event.preventDefault;			onRbLinkClick( this, pageTitle, lastEditor, undoRevid );		} ) .appendTo( $( ' ' ).addClass( 'mw-rollback-link' ) ).parent; }

function getRbLinkFromRevisions( pageTitle, lastEditor, data ) { var page, rbEdits, undoRevid, rbLinkText, rbLinkHrefConfig; if (			data === undefined ||			data.query === undefined ||			data.query.pages === undefined ||			data.query.pages.length !== 1		) { return null; }		page = data.query.pages[ 0 ]; if ( page.revisions === undefined || page.revisions.length === 0 ) { return null; }		rbEdits = null; undoRevid = page.revisions[ 0 ].revid; $.each( page.revisions, function ( i, rev ) {			if ( rev.user.replace( / /g, '_' ) !== lastEditor.replace( / /g, '_' ) ) {				rbEdits = i;				return false;			}		} ); rbLinkText = mw.message( 'rollbacklink' ).text; if ( rbEdits === null ) { if ( page.revisions.length === SHOW_ROLLBACK_EDIT_COUNT + 1 ) { rbLinkText = mw.message( 'rollbacklinkcount-morethan', SHOW_ROLLBACK_EDIT_COUNT ).text; } else { return null; }		} else { rbLinkText = mw.message( 'rollbacklinkcount', rbEdits ).text; }		rbLinkHrefConfig = { title: pageTitle, action: 'rollback', user: lastEditor, token: csrfToken.get( 'editToken' ) };		if ( botMode !== null ) { rbLinkHrefConfig.bot = botMode; }		return getRbLink( pageTitle, lastEditor, undoRevid, rbLinkHrefConfig, rbLinkText ); }

function createRbLink( pageTitle, lastEditor ) { return api.get( {			// get the latest SHOW_ROLLBACK_EDIT_COUNT + 1 editors			action: 'query',			formatversion: 2,			prop: 'revisions',			rvlimit: SHOW_ROLLBACK_EDIT_COUNT + 1,			titles: pageTitle,			rvprop: 'ids|user'		} ) .then( function ( data ) {			var rbl = getRbLinkFromRevisions( pageTitle, lastEditor, data );			if ( rbl === null ) {				return $.Deferred.reject;			}			return rbl;		} ); }

function getUserFromLink( $link ) { if ( $link.hasClass( 'mw-anonuserlink' ) ) { // Works also for shortened IPv6 addresses return $link.attr( 'title' ).split( '/' )[ 1 ]; }		return $link.text; }

function getPageFromLink( $li ) { var $diffLink = $li.find( 'a[href*="&diff="]' ); if ( $diffLink.length === 1 ) { return mw.util.getParamValue( 'title', $diffLink.attr( 'href' ) ); }		return null; }

function historyMode { var $history = $( 'ul#pagehistory' ); if ( $history.length === 0 ) { return true; }		createRbLink(			mw.config.get( 'wgPageName' ),			getUserFromLink( $history.find( 'li:first-child span.history-user a.mw-userlink' ).first )		) .done( function ( $rbLink ) {			$rbLink.insertBefore( $undoLink ).after( ' | ' );		} ); }

function diffMode { if ( $ntitle.length === 0 ) { return true; }		createRbLink(			mw.config.get( 'wgPageName' ),			getUserFromLink( $ntitle.find( 'a.mw-userlink' ).first )		) .done( function ( $rbLink ) {			$rbLink.prepend( '[' ).append( ']' ).appendTo( $ntitle ).before( '  ' );		} ); }

function contributionsMode { var lastEditor; if ( specialPage !== 'Contributions' ) { return true; }		lastEditor = mw.util.getParamValue( 'target' ); if ( lastEditor === null ) { lastEditor = mw.config.get( 'wgPageName' ).split( '/' )[ 1 ]; }		$( '.ns-special ul li' ).each( function {			var pageTitle,				$li = $( this ),				$uctop = $li.find( '.mw-uctop' );			if ( $uctop.length === 1 && $li.find( '.newpage' ).length === 0 ) {				// only top edits, excluding new pages				pageTitle = getPageFromLink( $li );				if ( pageTitle === null ) {					pageTitle = $li.find( '.mw-contributions-title' ).text;				}				createRbLink( pageTitle, lastEditor )				.done( function ( $rbLink ) { $rbLink.prepend( ' [' ).append( ']' ).insertAfter( $uctop ); } );			}		} );	}

function changesListMode { var seen; if ( [ 'Watchlist', 'Recentchanges', 'Recentchangeslinked' ].indexOf( specialPage ) === -1 ) { return true; }		seen = Object.create( null ); $( '.ns-special ul li' ).each( function {			var $li = $( this ),				pageTitle = getPageFromLink( $li );

if ( pageTitle === null ) { pageTitle = $li.find( '.mw-changeslist-title' ).text; }

if ( seen[ pageTitle ] !== undefined ) { // not the latest edit to this page return; }			seen[ pageTitle ] = true;

createRbLink( pageTitle, getUserFromLink( $li.find( '.mw-userlink' ) ) ) .done( function ( $rbLink ) {				$rbLink.prepend( ' [' ).append( ']' ).appendTo( $li );			} ); } );	}

function onMessagesLoaded { // The chain stops as soon as one of the functions takes care of adding rollback links return historyMode && diffMode && contributionsMode && changesListMode; }

function onModulesLoaded { api = new mw.Api; loadMessages( [			'rollback-success',			'rollbacklink',			'rollbacklinkcount',			'rollbacklinkcount-morethan',			'action-rollback',			'tooltip-rollback',			'cantrollback',			'actioncomplete',			'rollbackfailed',			'returnto'		] ) .done( onMessagesLoaded ); }	mw.loader.using( modules, onModulesLoaded ); } ); //