User:Void/findInactiveSysops.js: Difference between revisions

From TestWiki
Content added Content deleted
(revert last update as it's not loading)
(Undid revision 24616 by RhinosF1 (talk), please let me know what issues you get instead of (or at least in addition to) reverting code changes; also update ZppixBot to MirahezeBots)
Tag: Undo
Line 1: Line 1:
/* jslint strict:false */
//<nowiki>
//<nowiki>
//This script automates the process of finding inactive sysops.
//This script automates the process of finding inactive sysops.
Line 4: Line 5:
//Output is in the format of "[[User:Example|Example]] || [[Special:PermanentLink/1234|timestamp]] || [[Special:Log/Example|timestamp]]"
//Output is in the format of "[[User:Example|Example]] || [[Special:PermanentLink/1234|timestamp]] || [[Special:Log/Example|timestamp]]"
mw.loader.using(['oojs-ui', 'mediawiki.util']).done( function () {
mw.loader.using(['oojs-ui', 'mediawiki.util']).done( function () {
var scriptActivationLink = mw.util.addPortletLink(
'p-tb',
'#',
'Find Inactive Sysops',
'pt-testscript',
'Replaces the contents of the current page with a list of inactive sysops',
null,
'#pt-adminlinks'
);


var scriptActivationLink = mw.util.addPortletLink(
$( scriptActivationLink ).click( function () {
var progressBar = new OO.ui.ProgressBarWidget();
'p-tb',
var progressField = new OO.ui.FieldLayout(
'#',
progressBar,
'Find Inactive Sysops',
{
'pt-testscript',
label: "Fetching users...",
'Replaces the contents of the current page with a list of inactive sysops',
align: 'top'
null,
}
'#pt-adminlinks'
);
);


var textPanel = new OO.ui.PanelLayout( {
$( scriptActivationLink ).click( function () {
expanded: false,
var doThis = confirm('Do you want to run the script?');
framed: false,
if( doThis ) {
padded: false,
} );

var canSave = false;
function InactiveUsersProc( config ) {
InactiveUsersProc.super.call( this, config );
}
OO.inheritClass( InactiveUsersProc, OO.ui.ProcessDialog );

InactiveUsersProc.static.name = "Inactive Users Process";
InactiveUsersProc.static.title = "List of Inactive Users:";
InactiveUsersProc.static.actions = [
{
action: "save",
label: "Update",
flags: "primary"
},
{
label: "Cancel",
flags: "safe"
}
];

InactiveUsersProc.prototype.initialize = function() {
InactiveUsersProc.super.prototype.initialize.apply( this, arguments );

this.content = new OO.ui.FieldsetLayout();
this.content.addItems([ progressField ]);
this.content.$element.css( 'padding', '1em' );

this.$body.append( this.content.$element );
};
InactiveUsersProc.prototype.getActionProcess = function( action ) {
var dialog = this;
if( action && canSave ) {
return new OO.ui.Process( function() {
if( action == "save" ) {
updList();
}

dialog.close( {
action: action
} );
});
}
return InactiveUsersProc.super.prototype.getActionProcess.call( this, action );
};
InactiveUsersProc.prototype.getBodyHeight = function() {
return this.content.$element.outerHeight( true );
};
var windowManager = new OO.ui.WindowManager();
var inactiveUsersProc = new InactiveUsersProc( {
size: "large"
} );

windowManager.addWindows([ inactiveUsersProc ]);
$('#bodyContent').append(windowManager.$element);
windowManager.openWindow(inactiveUsersProc);

var checked = 0;
var inactive = [];

// Get user list
$.getJSON(
$.getJSON(
mw.util.wikiScript( 'api' ),
//Get userlist
{
mw.util.wikiScript( 'api' ),
{
format: 'json',
format: 'json',
action: 'query',
action: 'query',
list: 'allusers',
augroup: 'bot|sysop|bureaucrat|consul|autopatrolled|rollbacker|interface-admin|flow-bot',
list: 'allusers',
aulimit: 150 // Unlikely to be more than 150 privilged users
augroup: 'bot|sysop|bureaucrat|consul|autopatrolled|rollbacker|interface-admin|flow-bot',
}
aulimit: 150, //Unlikely to be more than 150 privilged users
}
).done( function ( data ) {
).done( function ( data ) {
try {
try {
var users = data.query.allusers;
var users = data.query.allusers;
var userlist = [];
var userlist = [];
users.forEach( function( object ) {
userlist.push( object.name );
});
filterUsers( userlist );
}
catch ( e ) {
console.log( "Content request error: " + e.message );
console.log( "Content request response: " + JSON.stringify( data ) );
}
} ).fail( function () {
console.log( "While getting the userlist, there was an AJAX error." );
} );
}
} );


users.forEach( function( object ) {
function filterUsers ( userlist ) {
var userstring = userlist.toString();
userlist.push( object.name );
});
var exempt = ["John", "Void", "NDKilla", "Reception123", "Revi", "Southparkfan", "Abuse filter", "Void-bot", "Voidwalker", "Paladox", "Zppix", "ZppixBot", "RhinosF1", "MediaWiki message delivery", "ZI Jony", "Bonnedav", "Babel AutoCreate", "HeartsDo"];
filterUsers( userlist );
for ( i = 0; i < exempt.length; i++ ) {
}
userstring = userstring.replace( exempt[i] + ",", "" );
catch ( e ) {
}
console.log( "Content request error: " + e.message );
var reducedList = userstring.split( "," );
console.log( "Content request response: " + JSON.stringify( data ) );
var tribsData;
}
var logsData;
} ).fail( function () {
//Visual output onto the page you activate the script on
console.log( "While getting the userlist, there was an AJAX error." );
$( "#mw-content-text" ).replaceWith( "The following users are inactive:" + "<ul id=\"inactiveList\" style=\"list-style-type:none;list-style-image:none;\"></ul>" );
} );
for ( x = 0; x < reducedList.length; x++ ) {
checkUser( reducedList[x] );
}
var updButton = new OO.ui.ButtonWidget( {
label: 'Update Inactive Administrators?',
id: 'upd-inactive-bttn'
} );
$("#inactiveList").after( updButton.$element.click( function() { updList(); } ) );
}


function checkUser( user ) {
function filterUsers ( userlist ) {
var userstring = userlist.toString();
$.getJSON(
var exempt = ["John", "Void", "NDKilla", "Reception123", "Revi", "Southparkfan", "Abuse filter", "Void-bot", "Voidwalker", "Paladox", "Zppix", "MirahezeBots", "RhinosF1", "MediaWiki message delivery", "ZI Jony", "Bonnedav", "Babel AutoCreate", "HeartsDo"];
//Get contribs and log entries
mw.util.wikiScript( 'api' ),
{
format: 'json',
action: 'query',
list: 'logevents|usercontribs',
leprop: 'timestamp',
ledir: 'older',
leuser: user,
lelimit: 1, //We only need the most recent log action/edit
uclimit: 1,
ucuser: user,
ucdir: 'older',
ucprop: 'timestamp|ids'
}
).done( function ( data ) {
try {
tribsData = data.query.usercontribs;
logsData = data.query.logevents;
var activeLogs, activeTribs, active;
var tribsInfo;


for ( i = 0; i < exempt.length; i++ ) {
if( typeof( logsData[0].timestamp ) != "undefined" ) {
userstring = userstring.replace( exempt[i] + ",", "" );
activeLogs = compareDates( logsData[0].timestamp, "logs" );
}
}
else {
activeLogs = false;
}
if( typeof( tribsData[0] ) != "undefined" ) {
tribsInfo = tribsData[0].revid + "|" + tribsData[0].timestamp;
activeTribs = compareDates( tribsInfo, "tribs" );
}
else {
activeTribs = false;
}
if( activeLogs === false && activeTribs === false ) {
console.log( user + " is inactive" );
listInactiveUsers( user, tribsData, logsData );
}
}
catch ( e ) {
console.log( "Content request error: " + e.message );
console.log( "Content request response: " + JSON.stringify( data ) );
}
} ).fail(/*console.log( "While getting the userlist, there was an AJAX error." )*/);
}


var reducedList = userstring.split( "," );
function compareDates ( data, dataType ) {
for ( x = 0; x < reducedList.length; x++ ) {
//Gets current date in yyyymmdd
checkUser( reducedList[x], reducedList.length );
var today = new Date();
}
var dd = today.getDate();
}
var mm = today.getMonth() + 1; //January is 0!
var yyyy = today.getFullYear();
//Set back 3 months
mm -= 3;
if( mm < 0 ) {
mm += 12;
yyyy -= 1;
}
if( dd < 10 ) {
dd = '0' + dd;
}


function checkUser( user, total ) {
if( mm<10 ) {
$.getJSON(
mm = '0' + mm;
mw.util.wikiScript( 'api' ),
}
{
today = '' + yyyy + mm + dd; //This is a string
format: 'json',
action: 'query',
list: 'logevents|usercontribs',
leprop: 'timestamp',
ledir: 'older',
leuser: user,
lelimit: 1, //We only need the most recent log action/edit
uclimit: 1,
ucuser: user,
ucdir: 'older',
ucprop: 'timestamp|ids'
}
).done( function ( data ) {
try {
var tribsData = data.query.usercontribs;
var logsData = data.query.logevents;
var activeLogs, activeTribs, active, tribsInfo;


if( typeof( logsData[0].timestamp ) != "undefined" ) {
var date;
activeLogs = compareDates( logsData[0].timestamp, "logs" );
var isActive;
} else {
if( dataType === "logs" ) {
activeLogs = false;
date = data.slice( 0, data.indexOf('T') );
}
date = date.replace( "-", "" );
}
else if( dataType === "tribs" ) {
date = data.slice( data.indexOf('|') + 1, data.indexOf('T') );
date = date.replace( "-", "" );
}
if ( date < today ) {
isActive = false;
}
else {
isActive = true;
}
return isActive;
}


if( typeof( tribsData[0] ) != "undefined" ) {
function listInactiveUsers( userName, tribsArray, logsArray ) {
tribsInfo = tribsData[0].revid + "|" + tribsData[0].timestamp;
var userLink = "[[User:<a href=\"https://publictestwiki.com/wiki/Special:Contribs/" + userName + "\">" + userName + "</a>|" + userName + "]]";
activeTribs = compareDates( tribsInfo, "tribs" );
var tribsInfo;
} else {
if( tribsArray[0] !== undefined ) {
activeTribs = false;
tribsInfo = tribsArray[0].timestamp;
}
tribsInfo = tribsInfo.slice( 0, tribsInfo.indexOf("T") );
tribsInfo = tribsArray[0].revid + "|" + tribsInfo;
tribsInfo = "[[Special:PermanentLink/" + tribsInfo + "]]";
}
else {
tribsInfo = 'None';
}
var logsInfo = logsArray[0].timestamp;
logsInfo = logsInfo.slice( 0, logsInfo.indexOf("T") );
logsInfo = "[[Special:Log/" + userName + "|" + logsInfo + "]]";
$("#inactiveList").append( "<li>" + userLink + " || " + tribsInfo + " || " + logsInfo + "</li>" );
}


if( activeLogs === false && activeTribs === false ) {
function updList() {
inactive.push([user, tribsData, logsData]);
var list = $("#inactiveList")[0].innerText;
}
list = '|-\n|' + list;

while( list.indexOf("\n[[User:") > 0 ) {
checked++;
list = list.substr( 0, list.indexOf( "\n[[User:" ) ) + '\n|-\n|' + list.substr( list.indexOf( "\n[[User:" ) + 1 );
progressBar.setProgress( parseInt(checked / total) * 100 );
}

$.getJSON(
if( checked == total ) {
mw.util.wikiScript( 'api' ),
{
listInactiveUsers();
format: 'json',
}
action: 'query',
} catch ( e ) {
console.log( "Content request error: " + e.message );
pageids: 238,
console.log( "Content request response: " + JSON.stringify( data ) );
prop: 'revisions',
}
rvprop: 'content'
}
} );
}
).done( function ( data ) {
try {
function compareDates ( data, dataType ) {
var wtext = data.query.pages[238].revisions[0]['*'];
//Gets current date in yyyymmdd
wtext = wtext.substr( 0, wtext.indexOf("|-") ) + list + '\n' + wtext.substr( wtext.indexOf( "|}" ) );
var today = new Date();
$.ajax( {
var dd = today.getDate();
url: mw.util.wikiScript( 'api' ),
var mm = today.getMonth() + 1; //January is 0!
type: 'POST',
var yyyy = today.getFullYear();
dataType: 'json',

data: {
//Set back 3 months
mm -= 3;
if( mm < 0 ) {
mm += 12;
yyyy -= 1;
}

if( dd < 10 ) {
dd = '0' + dd;
}

if( mm<10 ) {
mm = '0' + mm;
}

today = '' + yyyy + mm + dd; //This is a string
var date;
var isActive;

if( dataType === "logs" ) {
date = data.slice( 0, data.indexOf('T') );
date = date.replace( "-", "" );
} else if( dataType === "tribs" ) {
date = data.slice( data.indexOf('|') + 1, data.indexOf('T') );
date = date.replace( "-", "" );
}

return date >= today;
}

function listInactiveUsers() {
sorted = inactive.sort( function( a, b ) {
return a[0].localeCompare( b[0] );
} );

for( var i in sorted ) {
var userName = sorted[i][0];
var tribsArray = sorted[i][1];
var logsArray = sorted[i][2];
var userLink = "[[User:<a href=\"https://publictestwiki.com/wiki/Special:Contribs/" + userName + "\">" + userName + "</a>|" + userName + "]]";

var tribsInfo;
if( tribsArray[0] !== undefined ) {
tribsInfo = tribsArray[0].timestamp;
tribsInfo = tribsInfo.slice( 0, tribsInfo.indexOf("T") );
tribsInfo = tribsArray[0].revid + "|" + tribsInfo;
tribsInfo = "[[Special:PermanentLink/" + tribsInfo + "]]";
} else {
tribsInfo = 'None';
}

var logsInfo = logsArray[0].timestamp;
logsInfo = logsInfo.slice( 0, logsInfo.indexOf("T") );
logsInfo = "[[Special:Log/" + userName + "|" + logsInfo + "]]";

textPanel.$element.append( "<li>" + userLink + " || " + tribsInfo + " || " + logsInfo + "</li>" );
}
inactiveUsersProc.content.clearItems();
inactiveUsersProc.content.addItems([textPanel]);
inactiveUsersProc.updateSize();
canSave = true;
}
function updList() {
var list = textPanel.$element[0].innerText;
list = '|-\n|' + list;
while( list.indexOf("\n[[User:") > 0 ) {
list = list.substr( 0, list.indexOf( "\n[[User:" ) ) + '\n|-\n|' + list.substr( list.indexOf( "\n[[User:" ) + 1 );
}

// Get contents of TestWiki:Inactivity/Inactive administrators
$.getJSON(
mw.util.wikiScript( 'api' ),
{
format: 'json',
format: 'json',
action: 'edit',
action: 'query',
title: 'TestWiki:Inactivity/Inactive administrators',
pageids: 238,
text: wtext,
prop: 'revisions',
summary: '[[User:Void/findInactiveSysops.js|Automatically]] updating list',
rvprop: 'content',
token: mw.user.tokens.get( 'csrfToken' ),
rvslots: 'main'
}
}
} ).done( function() {
).done( function ( data ) {
try {
alert( "Updated TestWiki:Inactivity/Inactive administrators" );
var wtext = data.query.pages[238].revisions[0].slots.main['*'];
var reload = new OO.ui.ButtonWidget( { label: 'Load original page' } );
wtext = wtext.substr( 0, wtext.indexOf("|-") ) + list + '\n' + wtext.substr( wtext.indexOf( "|}" ) );
$('#upd-inactive-bttn').replaceWith( reload.$element.click( function() { window.location.reload(); } ) );
} ).fail( function ( e, data ) {
console.log( e.message );
console.log( JSON.stringify( data ) );
} );
}
catch( e ) {
// ignore
}
} ).fail( function ( e, data ) {
console.log( e.message );
console.log( JSON.stringify( data ) );
} );
}


$.ajax( {
url: mw.util.wikiScript( 'api' ),
type: 'POST',
dataType: 'json',
data: {
format: 'json',
action: 'edit',
title: 'TestWiki:Inactivity/Inactive administrators',
text: wtext,
summary: '[[User:Void/findInactiveSysops.js|Automatically]] updating list',
token: mw.user.tokens.get( 'csrfToken' ),
}
} ).done( function() {
alert( "Updated TestWiki:Inactivity/Inactive administrators" );
} ).fail( function ( e, data ) {
console.log( e.message );
console.log( JSON.stringify( data ) );
} );
}
catch( e ) {
// ignore
}
} ).fail( function ( e, data ) {
console.log( e.message );
console.log( JSON.stringify( data ) );
} );
}
} );
});
});
//</nowiki>
//</nowiki>

Revision as of 21:43, 7 July 2020

/* jslint strict:false */
//<nowiki>
//This script automates the process of finding inactive sysops.
//Warning: the script will replace the contents of the page you are currently viewing when you click the link at the top right of the page.
//Output is in the format of "[[User:Example|Example]] || [[Special:PermanentLink/1234|timestamp]] || [[Special:Log/Example|timestamp]]"
mw.loader.using(['oojs-ui', 'mediawiki.util']).done( function () {
	var scriptActivationLink = mw.util.addPortletLink(
	    'p-tb',
	    '#',
	    'Find Inactive Sysops',
	    'pt-testscript',
	    'Replaces the contents of the current page with a list of inactive sysops',
	    null,
	    '#pt-adminlinks'
	);

	$( scriptActivationLink ).click( function () {
		var progressBar = new OO.ui.ProgressBarWidget();
		var progressField = new OO.ui.FieldLayout(
			progressBar,
			{
				label: "Fetching users...",
				align: 'top'
			}
		);

		var textPanel = new OO.ui.PanelLayout( {
			expanded: false,
			framed: false,
			padded: false,
		} );

		var canSave = false;
		
		function InactiveUsersProc( config ) {
			InactiveUsersProc.super.call( this, config );
		}
		OO.inheritClass( InactiveUsersProc, OO.ui.ProcessDialog );

		InactiveUsersProc.static.name = "Inactive Users Process";
		InactiveUsersProc.static.title = "List of Inactive Users:";
		InactiveUsersProc.static.actions = [
			{
				action: "save",
				label: "Update",
				flags: "primary"
			},
			{
				label: "Cancel",
				flags: "safe"
			}
		];

		InactiveUsersProc.prototype.initialize = function() {
			InactiveUsersProc.super.prototype.initialize.apply( this, arguments );

			this.content = new OO.ui.FieldsetLayout();
			this.content.addItems([ progressField ]);
			this.content.$element.css( 'padding', '1em' );

			this.$body.append( this.content.$element );
		};
		
		InactiveUsersProc.prototype.getActionProcess = function( action ) {
			var dialog = this;
			if( action && canSave ) {
				return new OO.ui.Process( function() {
					if( action == "save" ) {
						updList();
					}

					dialog.close( {
						action: action
					} );
				});
			}
			return InactiveUsersProc.super.prototype.getActionProcess.call( this, action );
		};
		
		InactiveUsersProc.prototype.getBodyHeight = function() {
			return this.content.$element.outerHeight( true );
		};
		
		var windowManager = new OO.ui.WindowManager();
		var inactiveUsersProc = new InactiveUsersProc( {
			size: "large"
		} );

		windowManager.addWindows([ inactiveUsersProc ]);
		$('#bodyContent').append(windowManager.$element);
		windowManager.openWindow(inactiveUsersProc);

		var checked = 0;
		var inactive = [];

		// Get user list
		$.getJSON(
	        mw.util.wikiScript( 'api' ),
	        {
	            format: 'json',
	            action: 'query',
	            list: 'allusers',
	            augroup: 'bot|sysop|bureaucrat|consul|autopatrolled|rollbacker|interface-admin|flow-bot',
	            aulimit: 150 // Unlikely to be more than 150 privilged users
	        }
	    ).done( function ( data ) {
	        try {
	        	var users = data.query.allusers;
	        	var userlist = [];

	        	users.forEach( function( object ) {
	        		userlist.push( object.name );
	        	});
	        	filterUsers( userlist );
	        }
	    	catch ( e ) {
	        	console.log( "Content request error: " + e.message );
	        	console.log( "Content request response: " + JSON.stringify( data ) );
	    	}
		} ).fail( function () {
	    	console.log( "While getting the userlist, there was an AJAX error." );
		} );

		function filterUsers ( userlist ) {
			var userstring = userlist.toString();
			var exempt = ["John", "Void", "NDKilla", "Reception123", "Revi", "Southparkfan", "Abuse filter", "Void-bot", "Voidwalker", "Paladox", "Zppix", "MirahezeBots", "RhinosF1", "MediaWiki message delivery", "ZI Jony", "Bonnedav", "Babel AutoCreate", "HeartsDo"];

			for ( i = 0; i < exempt.length; i++ ) {
				userstring = userstring.replace( exempt[i] + ",", "" );
			}

			var reducedList = userstring.split( "," );
			for ( x = 0; x < reducedList.length; x++ ) {
				checkUser( reducedList[x], reducedList.length );
			}
		}

		function checkUser( user, total ) {
			$.getJSON(
		        mw.util.wikiScript( 'api' ),
		        {
		            format: 'json',
		            action: 'query',
		            list: 'logevents|usercontribs',
		            leprop: 'timestamp',
		            ledir: 'older',
		            leuser: user,
		            lelimit: 1, //We only need the most recent log action/edit
		            uclimit: 1,
		            ucuser: user,
		            ucdir: 'older',
		            ucprop: 'timestamp|ids'
		        }
		    ).done( function ( data ) {
		        try {
		        	var tribsData = data.query.usercontribs;
		        	var logsData = data.query.logevents;
		           	var activeLogs, activeTribs, active, tribsInfo;

		           	if( typeof( logsData[0].timestamp ) != "undefined" ) {
		           		activeLogs = compareDates( logsData[0].timestamp, "logs" );
		           	} else {
		           		activeLogs = false;
		           	}

		           	if( typeof( tribsData[0] ) != "undefined" ) {
		           		tribsInfo = tribsData[0].revid + "|" + tribsData[0].timestamp;
		           		activeTribs = compareDates( tribsInfo, "tribs" );
		           	} else {
		           		activeTribs = false;
		           	}

		           	if( activeLogs === false && activeTribs === false ) {
		           		inactive.push([user, tribsData, logsData]);
		           	}

		           	checked++;
		           	progressBar.setProgress( parseInt(checked / total) * 100 );

		           	if( checked == total ) {
		           		listInactiveUsers();
		           	}
		        } catch ( e ) {
		        	console.log( "Content request error: " + e.message );
		        	console.log( "Content request response: " + JSON.stringify( data ) );
		    	}
		    } );
		}
		
		function compareDates ( data, dataType ) {
			//Gets current date in yyyymmdd
			var today = new Date();
			var dd = today.getDate();
			var mm = today.getMonth() + 1; //January is 0!
			var yyyy = today.getFullYear();

			//Set back 3 months
			mm -= 3;
			if( mm < 0 ) {
				mm += 12;
				yyyy -= 1;
			}

			if( dd < 10 ) {
		    	dd = '0' + dd;
			}

			if( mm<10 ) {
			    mm = '0' + mm;
			}

			today = '' + yyyy + mm + dd; //This is a string
		
			var date;
			var isActive;

			if( dataType === "logs" ) {
				date = data.slice( 0, data.indexOf('T') );
				date = date.replace( "-", "" );
			} else if( dataType === "tribs" ) {
				date = data.slice( data.indexOf('|') + 1, data.indexOf('T') );
				date = date.replace( "-", "" );
			}

			return date >= today;
		}

		function listInactiveUsers() {
			sorted = inactive.sort( function( a, b ) {
				return a[0].localeCompare( b[0] );
			} );

			for( var i in sorted ) {
				var userName = sorted[i][0];
				var tribsArray = sorted[i][1];
				var logsArray = sorted[i][2];
				var userLink = "[[User:<a href=\"https://publictestwiki.com/wiki/Special:Contribs/" + userName + "\">" + userName + "</a>|" + userName + "]]";

				var tribsInfo;
				if( tribsArray[0] !== undefined ) {
					tribsInfo = tribsArray[0].timestamp;
					tribsInfo = tribsInfo.slice( 0, tribsInfo.indexOf("T") );
					tribsInfo = tribsArray[0].revid + "|" + tribsInfo;
					tribsInfo = "[[Special:PermanentLink/" + tribsInfo + "]]";
				} else {
					tribsInfo = 'None';
				}

				var logsInfo = logsArray[0].timestamp;
				logsInfo = logsInfo.slice( 0, logsInfo.indexOf("T") );
				logsInfo = "[[Special:Log/" + userName + "|" + logsInfo + "]]";

				textPanel.$element.append( "<li>" + userLink + " || " + tribsInfo + " || " + logsInfo + "</li>" );
			}
			inactiveUsersProc.content.clearItems();
			inactiveUsersProc.content.addItems([textPanel]);
			inactiveUsersProc.updateSize();
			canSave = true;
		}
		
		function updList() {
			var list = textPanel.$element[0].innerText;
			list = '|-\n|' + list;
			while( list.indexOf("\n[[User:") > 0 ) {
				list = list.substr( 0, list.indexOf( "\n[[User:" ) ) + '\n|-\n|' + list.substr( list.indexOf( "\n[[User:" ) + 1 );
			}

			// Get contents of TestWiki:Inactivity/Inactive administrators
			$.getJSON(
				mw.util.wikiScript( 'api' ),
		        {
		            format: 'json',
		            action: 'query',
		            pageids: 238,
		            prop: 'revisions',
		            rvprop: 'content',
		            rvslots: 'main'
		        }
		    ).done( function ( data ) {
		        try {
		           	var wtext = data.query.pages[238].revisions[0].slots.main['*'];
		           	wtext = wtext.substr( 0, wtext.indexOf("|-") ) + list + '\n' + wtext.substr( wtext.indexOf( "|}" ) );

		           	$.ajax( {
		       			url: mw.util.wikiScript( 'api' ),
				        type: 'POST',
				        dataType: 'json',
				        data: {
				            format: 'json',
				            action: 'edit',
				            title: 'TestWiki:Inactivity/Inactive administrators',
				            text: wtext,
				            summary: '[[User:Void/findInactiveSysops.js|Automatically]] updating list',
				            token: mw.user.tokens.get( 'csrfToken' ),
				        }
		   			} ).done( function() {
		   				alert( "Updated TestWiki:Inactivity/Inactive administrators" );
		   			} ).fail( function ( e, data ) {
		   				console.log( e.message );
		   				console.log( JSON.stringify( data ) );
		   			} );
		        }
		        catch( e ) {
		          	// ignore
		        }
		    } ).fail( function ( e, data ) {
		    	console.log( e.message );
		    	console.log( JSON.stringify( data ) );
		    } );
		}
	} );
});
//</nowiki>