User:Danski454/RMcloser.js

/* * Some of this code comes from RMCDbot and the UI is based on Evad37's XFDcloser */ var $box; var form, interfaceWindow; var moves = []; var badMoves = []; var overwritePages = []; var associatedPagesChecked = 0; var basePages = 0; $.when(	window.Morebits || mw.loader.getScript('https://en.wikipedia.org/w/index.php?title=MediaWiki:Gadget-morebits.js&action=raw&ctype=text/javascript'),	$.ready,	importStylesheet( 'MediaWiki:Gadget-morebits.css' ) ).then(function{	//var form, interfaceWindow;	var moveGroups = {};	var loaded = false;	var noSubpageNamespaces = [0, 6, 8, 14, 446];	/*var moves = [];	var badMoves = [];	var overwritePages = [];	var associatedPagesChecked = 0;	var basePages = 0;*/	var defaultConfig = {		relistSummary: "Relisted requested move using RMcloser",		loadDiff: true,		outcomes: {			"moved": {				wikitext:"moved ",				summary: "Closed requested move as moved using RMcloser"			},			"not moved": {				wikitext:"not moved ",				summary: "Closed requested move as not moved using RMcloser"			},			"no consensus": { wikitext:"no consensus ", summary: "Closed requested move as no consensus using RMcloser" },			"withdrawn": { wikitext:"withdrawn ", summary: "Closed requested move as withdrawn using RMcloser" }		},		customOutcomeSummary: "Closed requested move using RMcloser", customSuffix: null, overwriteMode: "auto" //off, swap, delete };	function makeLink(target, text=null){ console.log('makeLink'); if (!text) { text = target; }		return '' + text + ''; }	function display{ console.log('display'); if (moves.length === 0) { //no moves form.append({				type: 'div',				label: 'Unable to get pages'			}); } else if (badMoves.indexOf(':*') !== -1) { //something has gone wrong form.append({				type: 'div',				label: 'Unable to validate moves'			}); } else { var baseGroups = []; for (var i = 0; i < moves.length; i++) { if (moveGroups[moves[i][2]]) { moveGroups[moves[i][2]].moves.push(moves[i]); if (overwritePages.indexOf(moves[i][1]) !== -1) { //we are overwriting a redirect moveGroups[moves[i][2]].overwrite = true; }				} else { moveGroups[moves[i][2]] = { moves: [moves[i]], overwrite: overwritePages.indexOf(moves[i][1]) !== -1, bad: badMoves.indexOf(moves[i][2]) !== -1 };					if (/^(:\/|t\/|t:)/.test(moves[i][2])) { var parent = moves[i][2].slice(2); moveGroups[moves[i][2]].parent = parent; if (!moveGroups[parent]) { moveGroups[parent] = { moves: [], overwrite: overwritePages.indexOf(parent) !== -1, bad: badMoves.indexOf(parent) !== -1, children: [moves[i][2]] };							baseGroups.push(parent); } else { moveGroups[parent].children.push(moves[i][2]); }					} else { baseGroups.push(moves[i][2]); moveGroups[moves[i][2]].children = []; }				}			}			var movefield = form.append({				type: 'field',				label: 'Moves'			}); var moveList = []; for (var j = 0; j < baseGroups.length; j++) { var source = moveGroups[baseGroups[j]].moves[0][0]; var target = moveGroups[baseGroups[j]].moves[0][1]; var flag = ''; if (moveGroups[baseGroups[j]].bad) { flag = ' [cannot move]'; } else if (moveGroups[baseGroups[j]].overwrite) { if ((getConfig('overwriteMode') === 'delete' || getConfig('overwriteMode') === 'auto') &&						Morebits.userIsInGroup("sysop")) { flag = ' [will delete target]'; } else { flag = ' [swap]'; }				}				moveList.push({					name: 'moves-' + baseGroups[j],					label: source + ' → ' + target + flag,					checked: !moveGroups[baseGroups[j]].bad,					disabled: moveGroups[baseGroups[j]].bad				}); if (moveGroups[baseGroups[j]].children.length) { moveList[moveList.length-1].subgroup = { type: 'checkbox', list: [] };					for (var k = 0; k < moveGroups[baseGroups[j]].children.length; k++) { var cflag = ''; if (moveGroups[moveGroups[baseGroups[j]].children[k]].bad) { cflag = ' [cannot move]'; } else if (moveGroups[moveGroups[baseGroups[j]].children[k]].overwrite) { if ((getConfig('overwriteMode') === 'delete' || getConfig('overwriteMode') === 'auto') &&								Morebits.userIsInGroup("sysop")) { cflag = ' [will delete target]'; } else { cflag = ' [swap]'; }						}						if (moveGroups[baseGroups[j]].children[k].charAt(1) === '/') { //subpages var pages = moveGroups[moveGroups[baseGroups[j]].children[k]].moves.length.toString; if (moveGroups[baseGroups[j]].children[k].charAt(0) === 't') { pages = pages + ' talk'; }							var subpageMoves = []; for (var l = 0; l < moveGroups[moveGroups[baseGroups[j]].children[k]].moves.length; l++) { var move = moveGroups[moveGroups[baseGroups[j]].children[k]].moves[l]; subpageMoves.push(move[0] + ' → ' + move[1]); }							subpageMoves = subpageMoves.join('\n'); moveList[moveList.length-1].subgroup.list.push({								name: 'moves-' + moveGroups[baseGroups[j]].children[k],								label: pages + ' subpage(s)' + cflag,								checked: !moveGroups[moveGroups[baseGroups[j]].children[k]].bad,								disabled: moveGroups[moveGroups[baseGroups[j]].children[k]].bad,								subgroup: {									type: 'div',									label: subpageMoves								}							}); } else { //talk var talk = moveGroups['t:' + baseGroups[j]].moves[0][0]; var talkTarget = moveGroups['t:' + baseGroups[j]].moves[0][1]; moveList[moveList.length-1].subgroup.list.push({								name: 'moves-t:' + baseGroups[j],								label: talk + ' → ' + talkTarget + cflag,								checked: !moveGroups['t:' + baseGroups[j]].bad,								disabled: moveGroups['t:' + baseGroups[j]].bad							}); }					}				}			}			movefield.append({				type: 'checkbox',				name: 'moves',				list: moveList			}); }		form.append({			type: 'submit',			label: 'Close Discussion'		}); var result = form.render; interfaceWindow.setContent(result); interfaceWindow.display; }	function getValidMoves{ console.log('getValidMoves'); var sysop = Morebits.userIsInGroup("sysop"); var te = sysop || Morebits.userIsInGroup("templateeditor"); var extconfirmed = sysop || Morebits.userIsInGroup("extendedconfirmed"); var overwrite = (sysop || Morebits.userIsInGroup("extendedmover")) && getConfig("overwriteMode") !== "off"; checkTitles(moves, true, extconfirmed, sysop, te, overwrite, display); }	function getBaseMove(title, index = 0){ console.log('getBaseMove'); var f = moves.filter(function(t){return t[index] === title;}); if (f.length > 0) { return f[0][2]; }		return null; }	function startClose(e){ console.log('startClose'); }	function closeDiscussion(outcome, comment){ console.log('closeDiscussion'); var outcomes = getConfig("outcomes"); var suffix = getConfig("customSuffix"); var top, summary; var bottom = ""; if (suffix === null) { if (mw.config.get("wgUserGroups").indexOf("sysop") !== -1) { suffix = "";//sysops don't need a suffix } else if (mw.config.get("wgUserGroups").indexOf("extendedmover") !== -1) { suffix = " "; } else { suffix = " "; }		}		if (outcomes[outcome]) { top = "" + outcomes[outcome].wikitext + comment + suffix + ""; summary = outcomes[outcome].summary; } else { top = "" + comment + suffix + ""; summary = getConfig("customOutcomeSummary"); }		var re = /((=+)[^\n]+\2\s*\n(?:[^\n]|\n[^=])*\n?)\{\{\s?(?:requested move\/dated?|movereq)\s?\}\}\s*((?:[^=]|[^\n=]=|\2=+)+)/i; $.ajax({			url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 			"/api.php?action=query&format=json&prop=revisions&titles=" + 			mw.config.get("wgPageName") + "&rvprop=ids%7Ccontent&rvslots=main",			dataType: "json",			success: function(data){				for (var page in data.query.pages) {					var text = data.query.pages[page].revisions[0].slots.main["*"];					text = text.replace(re, "$1" + top + "$3\n" + bottom + "\n");					$.ajax({ url: mw.util.wikiScript('api'), type: 'POST', dataType: 'json', data: { format: 'json', action: 'edit', title: mw.config.get('wgPageName'), text: text, summary: summary, token: mw.user.tokens.get('editToken') }					})					.done(function(data){ if (data && data.edit && data.edit.result && data.edit.result == 'Success') { if (getConfig("loadDiff")) { if (data.edit.oldrevid !== null && data.edit.newrevid !== undefined){ window.location.href = mw.config.get('wgScript') + '?diff=' + data.edit.newrevid + '&oldid=' + data.edit.oldrevid;//load diff } else { alert("Unable to load diff"); }							}						} else { alert('Edit failed'); }					})					.fail(function{ alert('Edit failed'); });				}			},			error: function{				alert('Edit failed');			}		}); }	function confirmRelist{ console.log('confirmRelist'); if (confirm("Press OK to relist")) { relist; }	}	function getConfig(option){ console.log('getConfig'); if (typeof(window.RMcloserConfig) === "object" && window.RMcloserConfig			&& window.RMcloserConfig[option] !== undefined) { if (defaultConfig[option] === undefined) { // if user did not set an option, error would occur console.warn("Config option '" + option + "' has no default setting."); }			return window.RMcloserConfig[option]; } else if (defaultConfig[option] !== undefined) { return defaultConfig[option]; } else { throw Error("Config option '" + option + "' has no default setting and has not been set by the user."); }	}	function relist{ console.log('relist'); $("#RMcloser-relist-button").off("click"); $.ajax({			url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 			"/api.php?action=query&format=json&prop=revisions&titles=" + 			mw.config.get("wgPageName") + "&rvprop=ids%7Ccontent&rvslots=main",			dataType: "json",			success: function(data){				for (var page in data.query.pages) {					var text = data.query.pages[page].revisions[0].slots.main["*"];					//regex based on RMCD bot's source code					var re = /\{\{\s?(requested[_ ]move\/dated?|movereq)\s?\}\}.*?([0-2]\d):([0-5]\d),\s(\d{1,2})\s(\w*)\s(\d{4})\s\([A-Z]{3}\)[^\n\r]*/is;					text = text.replace(re, "$&");					$.ajax({ url: mw.util.wikiScript('api'), type: 'POST', dataType: 'json', data: { format: 'json', action: 'edit', title: mw.config.get('wgPageName'), text: text, summary: getConfig("relistSummary"), token: mw.user.tokens.get('editToken') }					})					.done(function(data){ if (data && data.edit && data.edit.result && data.edit.result == 'Success') { if (getConfig("loadDiff")) { if (data.edit.oldrevid !== null && data.edit.newrevid !== undefined){ window.location.href = mw.config.get('wgScript') + '?diff=' + data.edit.newrevid + '&oldid=' + data.edit.oldrevid;//load diff } else { alert("Unable to load diff"); }							}						} else { alert('Edit failed'); }					})					.fail(function{ alert('Edit failed'); });				}			},			error: function{				alert('Edit failed');			}		}); }	function getAssociatedPages(title, target){ console.log('getAssociatedPages'); var titleObj = new mw.Title(title); var ns = titleObj.getNamespaceId; var pageName = titleObj.getMainText; if (ns < 0) { associatedPagesChecked += 2; if (associatedPagesChecked >= 0) { getValidMoves; } return; }		if (noSubpageNamespaces.indexOf(ns) === -1) { $.ajax({				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 				"/api.php?action=query&format=json&list=allpages&requestid=" + 				encodeURIComponent(title) + "%7C" + encodeURIComponent(target) + 				"&apprefix=" + encodeURIComponent(pageName) + "%2F&apnamespace=" + 				ns.toString + "&aplimit=101",				dataType: "json",				success: function(data){					if (data.query.allpages.length > 100) {						//too many subpages						associatedPagesChecked++;						if (associatedPagesChecked >= 0) { getValidMoves; }						return;					}					var [base, target] = data.requestid.split("|");					for (var i = 0; i < data.query.allpages.length; i++) {						moves.push([data.query.allpages[i].title, data.query.allpages[i].title.replace(base, target), ":/" + base]);					}					associatedPagesChecked++;					if (associatedPagesChecked >= 0) { getValidMoves; }				}			}); } else associatedPagesChecked++; if (ns % 2 === 0) { var titleTalk = titleObj.getTalkPage.getPrefixedText; var targetTalk = new mw.Title(target).getTalkPage.getPrefixedText; $.ajax({				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 				"/api.php?action=query&format=json&list=allpages&requestid=" + 				encodeURIComponent(titleTalk) + "%7C" + encodeURIComponent(targetTalk) + 				"%7C" + encodeURIComponent(title) + "&prop=info&titles=" +				encodeURIComponent(titleTalk) +				"&apprefix=" + encodeURIComponent(pageName) + "%2F&apnamespace=" + 				(ns+1).toString + "&aplimit=101",				dataType: "json",				success: function(data){					if (data.query.allpages.length > 100) {						//too many subpages						associatedPagesChecked++;						if (associatedPagesChecked >= 0) { getValidMoves; }						return;					}					var [base, target, subject] = data.requestid.split("|");					for (var i = 0; i < data.query.allpages.length; i++) {						moves.push([data.query.allpages[i].title, data.query.allpages[i].title.replace(base, target), "t/" + subject]);					}					for (var id in data.query.pages) {						if (id !== "-1") {							//talk page itself exists							moves.push([base, target, "t:" + subject]);						}					}					associatedPagesChecked++;					if (associatedPagesChecked >= 0) { getValidMoves; }				}			}); } else associatedPagesChecked++; if (associatedPagesChecked >= 0) { getValidMoves; } }	function generateButtons{ console.log('generateButtons'); if (loaded) return; loaded = true; $("#RMcloser-close-button").after('loading').remove; interfaceWindow = new Morebits.simpleWindow(			Math.min(900, Math.floor(window.innerWidth*0.8)),			Math.floor(window.innerHeight*0.9)		); interfaceWindow.setTitle( 'RMcloser' ); interfaceWindow.addFooterLink('script documentation', 'User:Danski454/RMcloser'); form = new Morebits.quickForm(startClose); var closurefield = form.append({			type: 'field',			label: 'Closure'		}); var outcomes = getConfig('outcomes'); var outcomesList = [{value:'', label:'other', selected: true}]; for (var o in outcomes) { outcomesList.push({value:o, label:o}); }		closurefield.append({			type: 'select',			label: 'Outcome',			name: 'type',			list: outcomesList		}); closurefield.append({			type: 'textarea',			name: 'comment',			label: 'Additional comments',			rows: 2,			cols: 50		}); if($box.next.is("p")){ //single item var match = $box.next.text.match(/(.+) → (.+) –/); if (match) { associatedPagesChecked = -2;//count up to zero, one for talk, one for subject moves.push([match[1], match[2], match[1]]); getAssociatedPages(match[1], match[2]) } else { display; }		} else if ($box.next.is("ul")){ associatedPagesChecked = $box.next.children.length * -2; $box.next.children.each(function{				var match = $(this).text.match(/(.+) → (.+)/);				if (match) {					moves.push([match[1], match[2], match[1]]);					getAssociatedPages(match[1], match[2])				} else {					associatedPagesChecked += 2;					if (associatedPagesChecked >= 0) { getValidMoves; }				}			}); } else { display; }	}	function start{ console.log('start'); mw.loader.load( '//en.wikipedia.org/w/index.php?title=User:Danski454/RMcloser.css&action=raw&ctype=text/css', 'text/css' ); if (mw.config.get("wgNamespaceNumber") % 2 === 0) { return;//we are not on a talk page }		var boxes = 0; $(".tmbox-move").each(function(index){			var $this = $(this);			var $b = $this.find("td.mbox-text b");			if($b.text.match(/^It has been proposed in this section that /)) {				boxes++;				$box = $this;			}		}); if (boxes !== 1) { //we do not have exactly one RM, exiting return; }		if (mw.config.get("wgUserGroups").indexOf("autoconfirmed") === -1 && 			mw.config.get("wgUserGroups").indexOf("confirmed") === -1 && 			mw.config.get("wgUserGroups").indexOf("sysop") === -1) { //must be autoconfirmed console.warn("You must be autoconfirmed to use RMcloser"); return; }		//add button $box.find(".mbox-text").children(":last").children.append(' • close • relist'); $("#RMcloser-close-button").click(generateButtons); $("#RMcloser-relist-button").click(confirmRelist); }	function checkTitles(titlepairs, tboverride, extconfirmed, sysop, te, overwrite, next){ console.log('checkTitles'); var titles = []; var sources = []; var targets = []; var checked = 0; var protdone = false; var redirsToCheck = []; for (var i = 0; i < titlepairs.length; i++) { var source = titlepairs[i][0]; var target = titlepairs[i][1]; if (titles.indexOf(source) !== -1) { badMoves.push(titlepairs[i][2]); console.log(448); checked += 2; continue; }			if (titles.indexOf(target) !== -1) { console.log(452); badMoves.push(titlepairs[i][2]); checked += 2; continue; }			titles.push(source); sources.push(sources); targets.push(target); titles.push(target); /*$.ajax({				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 				"/api.php?action=titleblacklist&format=json&tbtitle=" + 				encodeURIComponent(target) + "&tbaction=move&tbnooverride=1",				dataType: "json",				success: function(data){					checked++;					if (data.error) {						console.log("Could not get titleblacklist data for " + target);					} else if (data.titleblacklist.result === "blacklisted") {						if (tboverride) {							console.log(target + " matches the titleblacklist");						} else {							console.warn(target + " matches the titleblacklist");							//issues.push({title:target, role:"target", message:target + " matches the titleblacklist"});						}					}					if (checked >= titlepairs.length * 2 && protdone) {						next;					}				},				error: function {					checked++;					console.log("Could not get titleblacklist data for " + target);					if (checked >= titlepairs.length * 2 && protdone) {						next;					}				}			}); $.ajax({				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 				"/api.php?action=titleblacklist&format=json&tbtitle=" + 				encodeURIComponent(source) + "&tbaction=edit&tbnooverride=1",				dataType: "json",				success: function(data){					checked++;					if (data.error) {						console.log("Could not get titleblacklist data for " + source);						return;					} else if (data.titleblacklist.result === "blacklisted") {						if (tboverride) {							console.log(source + " matches the titleblacklist");						} else {							console.warn(source + " matches the titleblacklist");							//issues.push({title:source, role:"source", message:source + " matches the titleblacklist"});						}					}					if (checked >= titlepairs.length * 2 && protdone) {						next;					}				},				error: function {					checked++;					console.log("Could not get titleblacklist data for " + source);					if (checked >= titlepairs.length * 2 && protdone) {						next;					}				}			});// */ }		var loops = 0; while (titles.length){ loops++; var titlesToCheck; if (titles.length > 50) { titlesToCheck = titles.slice(0, 50); titles.splice(0, 50); } else { titlesToCheck = titles; titles = []; }			titlesToCheck = encodeURIComponent(titlesToCheck.join("|")); $.ajax({				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 				"/api.php?action=query&format=json&prop=info&titles=" + 				titlesToCheck + "&inprop=protection",				dataType: "json",				success: function(data){					var pages = data.query.pages;					for(var page in pages){						if (page[0] === "-" && sources.indexOf(pages[page].title) !== -1) {							//source does not exist							console.log(543);							badMoves.push(getBaseMove(pages[page].title));						}						if (page[0] !== "-" && targets.indexOf(pages[page].title) !== -1) {							redirsToCheck.push(pages[page].title);						}						for (var i = 0; i < pages[page].protection.length; i++) {							if (pages[page].protection[i].type === "move" || pages[page].protection[i].type === "create") {								if ((pages[page].protection[i].level === "sysop" && !sysop) || (pages[page].protection[i].level === "templateeditor" && !te) || (pages[page].protection[i].level === "extendedconfirmed" && !extconfirmed)) {									console.log(554);									badMoves.push(getBaseMove(pages[page].title) || getBaseMove(pages[page].title, 1));								}							}						}					}					loops--;					if (!loops && !titles.length) {						checkRedirects(overwrite, next, titlepairs, redirsToCheck);					}				},				error: function {					loops--;					console.warn("Could not get protection/existence data for many pages.");					badMoves.push(':*');					if (!loops && !titles.length) {						checkRedirects(overwrite, next, titlepairs, redirsToCheck);					}				}			}); }	}	function checkRedirects(overwrite, next, titlepairs, titles){ console.log('checkRedirects'); var latestRevisions = {}; var todo = titles.length; if (titles.length === 0) { //nothing to do			next; }		while(titles.length){ var titlesToCheck; if (titles.length > 50) { titlesToCheck = titles.slice(0, 50); titles.splice(0, 50); } else { titlesToCheck = titles; titles = []; }			titlesToCheck = encodeURIComponent(titlesToCheck.join("|")); console.log('AJAX1'); $.ajax({				url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 				"/api.php?action=query&format=json&prop=revisions&titles=" + 				titlesToCheck + "&rvprop=ids%7Ccontent&rvslots=main",				dataType: "json",				success: function(data){					console.log('success1');					var pages = data.query.pages;					for(var page in pages){						latestRevisions[page] = pages[page];						console.log('AJAX2');						$.ajax({ url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + "/api.php?action=query&format=json&prop=revisions&pageids=" + page + "&rvprop=ids%7Ccontent&rvslots=main&rvlimit=1&rvdir=newer", dataType: "json", success: function(data){ console.log('success2'); var pages = data.query.pages; var redirectRegExp = /^\s*#REDIRECT\s*\[\[ *([^\][<>|{}#\n]+?) *(?:#[^\][<>|{}\n]*)?]]/i; for(var page in pages){ console.log(todo); if (!(pages[page].revisions[0].slots.main["*"].match(redirectRegExp) && latestRevisions[page].revisions[0].slots.main["*"].match(redirectRegExp))){ todo--; //either current or first revision is not a redir console.log(611); badMoves.push(getBaseMove(pages[page].title, 1)); } else if (pages[page].revisions[0].revid !== latestRevisions[page].revisions[0].revid && !overwrite) { // page has non trivial history we cannot overwrite todo--; console.log(616); badMoves.push(getBaseMove(pages[page].title, 1)); } else { var target = latestRevisions[page].revisions[0].slots.main["*"].match(redirectRegExp)[1].replace("_", " "); target = target.replace(/^./, target.charAt(0).toUpperCase); var correct = false; for (var i = 0; i < titlepairs.length; i++) { if (titlepairs[i][0] === target && titlepairs[i][1] === pages[page].title){ correct = true; break; }										}										if (!correct) { //does not redirect to source todo--; console.log(629); badMoves.push(getBaseMove(pages[page].title, 1)); } else if (pages[page].revisions[0].revid !== latestRevisions[page].revisions[0].revid) { overwritePages.push(pages[page].title); console.log('AJAX3'); $.ajax({												url: mw.config.get("wgServer") + mw.config.get("wgScriptPath") + 												"/api.php?action=query&format=json&prop=revisions&pageids=" + 												page + "&rvprop=ids&rvslots=main&rvlimit=1&rvtag=mw-removed-redirect",												dataType: "json",												success: function(data){													console.log('success3');													var pages = data.query.pages;													for(var x in pages){														if(pages[x].revisions && pages[x].revisions.length){															console.log(642);															badMoves.push(getBaseMove(pages[page].title, 1));														}													}													todo--;													if (todo === 0) {														next;													}												},												error: function {													todo--;													console.warn("Could not get a page's history.");													if (todo === 0) {														next;													}												}											}); } else { todo--; }									}									if (todo === 0) { next; }								}							},							error: function { todo--; console.warn("Could not get a page's history."); if (todo === 0) { next; }							}						});					}				},				error: function {					console.log(678);					badMoves.push(':*');					todo-=50;					if (todo <= 0) {						next;					}				}			}); }	}	//everything is ready start; }); //