User:JJPMaster/rollback.js

async function getNonRolledBackEdit(page) {
	try {
	    var params = {
	        action: 'query',
	        prop: 'revisions',
	        rvprop: "user|ids|comment",
	        titles: page,
	        rvlimit: 'max',
	        format: 'json'
	    },
	    api = new mw.Api();
	
	    const data = await api.get( params );
	    var pages = data.query.pages,
	        p;
	    for ( p in pages ) {
	        var lastEditor = Object.values(pages)[0].revisions[0].user;
	        var otherIndex = pages[ p ].revisions.findIndex(i => i.user != lastEditor) - 1;
	        var lastEditByOther = pages[ p ].revisions[otherIndex + 1] ? pages[ p ].revisions[otherIndex + 1].revid : undefined;
	        var lastEdits = pages[ p ].revisions.slice(0, otherIndex + 1);
	        var numberOfEdits = lastEdits.length;
	        var lastEdit = Object.values(pages)[0].revisions[0].revid;
	        
	        return [lastEditor, lastEditByOther, lastEdit, numberOfEdits];
	    }
	}
	catch {
		alert("Luna: It appears that only one user has edited this page. Rollback cannot be applied.");
		return;
	}
}
async function getRevisionAuthor(revid) {
	var params = {
        action: 'query',
        prop: 'revisions',
        titles: mw.config.get("wgPageName"),
        rvprop: "user",
        rvstartid: revid,
        rvlimit: '1',
        format: 'json'
    },
    api = new mw.Api();
    const data = await api.get(params);
    return Object.values(data.query.pages)[0].revisions[0].user;
}
async function doUndo(reason, from, to, page = mw.config.get("wgPageName")) {
	var anyOtherEdits = (await getNonRolledBackEdit(page))[1];
	var lastEdit = await getNonRolledBackEdit(page);
	if (typeof anyOtherEdits != "undefined") {
		var params = {
	        action: 'edit',
	        title: page,
	        undo: from,
	        undoafter: to,
	        summary: `Reverting ${lastEdit[3]} edit${lastEdit[3] == 1 ? "" : "s"} by [[Special:Contributions/${lastEdit[0]}|${lastEdit[0]}]]${reason ? ": " + reason : ""}`,
	        tags: "Luna",
	        format: 'json'
	    },
	    api = new mw.Api();
	    const data = await api.postWithToken( "csrf", params );
	    console.log(data);
	}
}
async function doRollback(reason, page = mw.config.get("wgPageName")) {
	var lastEdit = await getNonRolledBackEdit(page);
	if(myRights.indexOf("rollback") < 0) {
		try {
			await doUndo(reason, lastEdit[2], lastEdit[1], page);
			mw.notify("Luna: Rollback complete.");
		}
		catch (e) {
			mw.notify("Luna: Rollback failed. See the console for details.");
			console.error(e);
		}
	}
	else { 
		try {
			params = {
		        action: 'rollback',
		        title: page,
		    	user: lastEdit[0],
		        summary: `Rollback ${lastEdit[3]} edit${lastEdit[3] == 1 ? "" : "s"} by [[Special:Contributions/${lastEdit[0]}|${lastEdit[0]}]]${reason ? ": " + reason : ""}`,
		        tags: "Luna",
		        format: 'json'
		    },
		    api = new mw.Api();
		    const data = await api.postWithToken( "rollback", params );
			mw.notify("Luna: Rollback complete.");
			console.log(data);
		}
		catch {
			try {
				await doUndo(reason, lastEdit[2], lastEdit[1], page);
				mw.notify("Luna: Rollback complete.");
			}
			catch (e) {
				mw.notify("Luna: Rollback failed. See the console for details.");
				console.error(e);
			}
		}
	}
}
async function doRestore(reason, revid, page = mw.config.get("wgPageName")) {
	var cur = (await getNonRolledBackEdit(page))[2];
	var author = await getRevisionAuthor(revid);
	var params, api;
	params = {
        action: 'edit',
        title: page,
        undo: cur,
        undoafter: revid,
        summary: `Restoring revision ${revid} by [[Special:Contributions/${author}|${author}]]${reason ? ": " + reason : ""}`,
        tags: "Luna",
        format: 'json'
    },
    api = new mw.Api();
    try {
	    const data = await api.postWithToken( "csrf", params );
	    mw.notify("Luna: Reversion complete.");
	    console.log(data);
    }
    catch (e) {
    	mw.notify("Luna: Reversion failed. See the console for details.");
		console.error(e);
    }
}
var spanTag = function( myClass, content ) {
    var span = document.createElement( 'span' );
    $(span).addClass(myClass);
    span.appendChild( document.createTextNode( content ) );
    return span;
};

function addRollbackLinks(selector) {
	var list = $("#bodyContent").find(selector);
    var revNode = document.createElement('strong');
    var revLink = document.createElement('a');
    revLink.style.textDecoration = "none";
    revLink.appendChild( spanTag( "luna-rollback-link", 'rollback' ) );
    revNode.appendChild(revLink);

    list.each(function(key, current) {
        var href = $(current).find('.mw-changeslist-diff').attr('href') ? $(current).find('.mw-changeslist-diff').attr('href') : `${mw.config.get("wgScript")}?title=${mw.config.get("wgPageName")}`;
        current.appendChild(document.createTextNode(' '));
        var tmpNode = revNode.cloneNode(true);
        var pageTitle = new URLSearchParams(new URL("http:" + mw.config.get("wgServer") + href).search).get("title");
        $(tmpNode).click(() => {
        	var rzn = prompt("Why would you like to revert this edit?");
        	if (rzn != null) {
        		doRollback(rzn, pageTitle);
        	}
        });
        current.appendChild(tmpNode);
    });
}
function addVandalLinks(selector) {
	var list = $("#bodyContent").find(selector);
    var revVandNode = document.createElement('strong');
    var revVandLink = document.createElement('a');
    revVandLink.style.textDecoration = "none";
    revVandLink.appendChild( spanTag( "luna-vand-link", 'vandalism' ) );
    revVandNode.appendChild(revVandLink);

    list.each(function(key, current) {
        var href = $(current).find('.mw-changeslist-diff').attr('href') ? $(current).find('.mw-changeslist-diff').attr('href') : `${mw.config.get("wgScript")}?title=${mw.config.get("wgPageName")}`;
        current.appendChild(document.createTextNode(' '));
        tmpNode = revVandNode.cloneNode(true);
        var pageTitle = new URLSearchParams(new URL("http:" + mw.config.get("wgServer") + href).search).get("title");
        $(tmpNode).click(() => doRollback("Vandalism", pageTitle));
        current.appendChild(tmpNode);
    });
}
function addRestoreLinks(selector) {
	var list = $("#bodyContent").find(selector);
    var resNode = document.createElement('strong');
    var resLink = document.createElement('a');
    resLink.style.textDecoration = "none";
    resLink.appendChild( spanTag( "luna-restore-link", 'restore' ) );
    resNode.appendChild(resLink);

    list.each(function(key, current) {
        var href = $(current).find('.mw-changeslist-diff').attr('href') ? $(current).find('.mw-changeslist-diff').attr('href') : `${mw.config.get("wgScript")}?title=${mw.config.get("wgPageName")}`;
        var oldid = !!$(current).data('mw-revid') ? $(current).data('mw-revid') : $(current).find('[data-mw-revid]').data('mw-revid');
        current.appendChild(document.createTextNode(' '));
        var tmpNode = resNode.cloneNode(true);
        var pageTitle = new URLSearchParams(new URL("http:" + mw.config.get("wgServer") + href).search).get("title");
        $(tmpNode).click(() => {
        	var rzn = prompt("Why would you like to restore this version?");
        	if (rzn != null) {
        		doRestore(rzn, oldid, pageTitle);
        	}
        });
        current.appendChild(tmpNode);
    });
}
function addAppropriateLinks() {
	(async () => {
		if(await waitForPreferences()) {
			switch (mw.config.get('wgCanonicalSpecialPageName')) {
			case "Contributions":
				if(!Luna.getPref("luna-hide-rb-ct"))
					addRollbackLinks("ul li:has(span.mw-uctop)");
				if(!Luna.getPref("luna-hide-vand-ct"))
					addVandalLinks("ul li:has(span.mw-uctop)");
				if(!Luna.getPref("luna-hide-res-ct"))
					addRestoreLinks("ul.mw-contributions-list li:not(:has(span.mw-uctop))");
				break;
			case "Recentchanges":
				if(!Luna.getPref("luna-hide-rb-rc"))
					addRollbackLinks(".mw-changeslist .mw-changeslist-last.mw-changeslist-src-mw-edit");
				if(!Luna.getPref("luna-hide-vand-rc"))
					addVandalLinks(".mw-changeslist .mw-changeslist-last.mw-changeslist-src-mw-edit");
				if(!Luna.getPref("luna-hide-res-rc"))
					addRestoreLinks(".mw-changeslist :not(.mw-changeslist-last).mw-changeslist-src-mw-edit");
				break;
			case "Recentchangeslinked":
				if(!Luna.getPref("luna-hide-rb-rcl"))
					addRollbackLinks(".mw-changeslist .mw-changeslist-last.mw-changeslist-src-mw-edit");
				if(!Luna.getPref("luna-hide-vand-rcl"))
					addVandalLinks(".mw-changeslist .mw-changeslist-last.mw-changeslist-src-mw-edit");
				if(!Luna.getPref("luna-hide-res-rcl"))
					addRestoreLinks(".mw-changeslist :not(.mw-changeslist-last).mw-changeslist-src-mw-edit");
				break;
			case "Watchlist":
				if(!Luna.getPref("luna-hide-rb-wl"))
					addRollbackLinks(".mw-changeslist .mw-changeslist-last.mw-changeslist-src-mw-edit");
				if(!Luna.getPref("luna-hide-vand-wl"))
					addVandalLinks(".mw-changeslist .mw-changeslist-last.mw-changeslist-src-mw-edit");
				if(!Luna.getPref("luna-hide-res-wl"))
					addRestoreLinks(".mw-changeslist :not(.mw-changeslist-last).mw-changeslist-src-mw-edit");
				break;
			default:
				if (mw.config.get("wgIsProbablyEditable")) {
					if (mw.config.get("wgAction") == "history") {
						if(!Luna.getPref("luna-hide-rb-ht"))
							addRollbackLinks("#pagehistory li:first");
						if(!Luna.getPref("luna-hide-vand-ht"))
							addVandalLinks("#pagehistory li:first");
						if(!Luna.getPref("luna-hide-res-ht"))
							addRestoreLinks("#pagehistory li:not(:first)");
					}
					else if (mw.config.get('wgDiffOldId') && (mw.config.get('wgDiffOldId') !== mw.config.get('wgDiffNewId'))) {
						if(!Luna.getPref("luna-hide-rb-df"))
							addRollbackLinks("#mw-diff-ntitle2");
						if(!Luna.getPref("luna-hide-vand-df"))
							addVandalLinks("#mw-diff-ntitle2");
						if(!Luna.getPref("luna-hide-res-df"))
							addRestoreLinks("#mw-diff-otitle2");
					}
					else if ($("#mw-revision-info")) {
						if(!Luna.getPref("luna-hide-res-oi"))
							addRestoreLinks("#mw-revision-info");
					}
				}
			}
		}
		else {
			console.error("Luna: Preferences never fully loaded.");
		}
	})();
}

if (mw.config.get("wgCanonicalSpecialPageName") == "Recentchanges" || mw.config.get("wgCanonicalSpecialPageName") == "Recentchangeslinked" || mw.config.get("wgCanonicalSpecialPageName") == "Watchlist") {
	mw.hook('wikipage.content').add((item) => {
		if (item.is('div')) {
			addAppropriateLinks();
		}
	});
}
else if (mw.config.get('wgDiffNewId') || mw.config.get('wgDiffOldId')) {
	mw.hook('wikipage.diff').add(() => {
		addAppropriateLinks();
	});
}
else {
	addAppropriateLinks();
}

if (mw.config.get('wgIsProbablyEditable') && mw.config.get('wgArticleId') > 0) {
	$(mw.util.addPortletLink('luna-actions', '#', 'Quick rollback', 'luna-qrb', 'Quickly revert all edits made by the last editor')).click(() => {
		doRollback(prompt("Why would you like to revert this edit?"), mw.config.get("wgPageName"));
	});
}