User:WMFOffice/OfficeBanGadget.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// License: GPL-v3 or later
function officeBanGadget() {
if (
mw.config.get( 'wgCanonicalNamespace' ) != 'Special' ||
mw.config.get( 'wgCanonicalSpecialPageName' ) != 'Blankpage' ||
mw.config.get( 'wgPageName' ).search( '/OfficeBan' ) == -1
) {
return;
}
function report(text) {
$('.mw-body-content').append($('<p></p>').css('color', '#d33').css('font-weight', 'bold').text(text));
}
function handleWiki(apiUrl, username, dbname) {
let api = new mw.ForeignApi( apiUrl );
let loggedIn = true;
api.get( {
action: 'query',
meta: 'userinfo',
assert: 'user',
} ).done( function ( data ) {
console.log( data );
} ).fail( () => {
report('You are not logged in in ' + dbname + '. Skipping this wiki.')
loggedIn = false;
} );
if ( !loggedIn ) {
return;
}
report('Removing from metorship program in ' + dbname);
api.get( {
action: 'growthmanagementorlist',
assert: 'user',
geaction: 'remove',
summary: 'Remove globally banned user from mentorship list',
username: username
} ).then( function ( data ) {
report('Removed from metorship program in ' + dbname);
} );
let wikiTemplates = {
enwiki: '{{WMF-legal banned user}}',
commonswiki: '{{WMF-legal banned user}}',
wikidatawiki: '{{WMF-legal banned user}}',
enwiki: '{{WMF-legal banned user}}',
ruwiki: '{{WMF-legal banned user}}',
eswiki: '{{WMF-legal banned user}}',
zhwiki: '{{WMF-legal banned user}}',
ptwiki: '{{WMF-legal banned user}}',
itwiki: '{{WMF-legal banned user}}',
dewiki: '{{Global_gebannter_Benutzer}}',
frwiki: '{{Utilisateur banni globalement par la Wikimedia Foundation}}',
kowiki: '{{위키미디어 재단에 의해 추방된 사용자}}',
arwiki: '{{مستخدم مطرود لأسباب قانونية}}',
bnwiki: '{{উইকিমিডিয়া ফাউন্ডেশন-আইনি কর্তৃক নিষিদ্ধ হওয়া ব্যবহারকারী}}',
fawiki: '{{WMF-legal banned user}}'
};
let defaultText = `__NOINDEX__ <table style="border: 1px solid #aaa; margin: 4px 10%; border-collapse: collapse; background: #f9f9f9;" class="plainlinks" role="presentation"><tr><td style="border:none; padding:2px 0 2px 0.9em;">[[File:Wikimedia Foundation logo - vertical.svg|45px|alt=Wikimedia Foundation Logo]]</td><td style="border:none; padding: 0.25em 0.9em; text-align:center;">'''Consistent with the Terms of Use, {{#ifexpr:floor({{NAMESPACENUMBER}}/2)=1|{{BASEPAGENAME}}|this user}} has been banned by the Wikimedia Foundation from editing Wikimedia sites.''' <br /> Please address any questions to ca[[File:At sign.svg|x15px|middle|link=|alt=@]]wikimedia.org.</td></tr></table> {{#ifeq:{{NAMESPACENUMBER}}|3|[[Category:Opted-out of message delivery]]}}[[Category:Wikimedians banned by the WMF]]`;
if (dbname != 'metawiki') {
api.get( {
action: 'query',
assert: 'user',
prop: 'revisions',
titles: 'User:' + username,
rvprop: 'user'
} ).done( function ( data ) {
// Only replace if the user page exists
if ( !data.query.pages["-1"] ) {
let text = wikiTemplates[dbname] || defaultText;
api.edit( 'User:' + username, function ( revision ) {
return {
text: text,
summary: 'This user has been globally banned'
};
} )
.then( function () {
report('The userpage has been replaced in '+ dbname + '.');
} );
}
} );
api.get( {
action: 'query',
assert: 'user',
prop: 'revisions',
titles: 'User talk:' + username,
rvprop: 'user'
} ).done( function ( data ) {
// Only replace if the user talk page exists
if ( !data.query.pages["-1"] ) {
let text = wikiTemplates[dbname] || defaultText;
api.edit( 'User talk:' + username, function ( revision ) {
return {
text: text,
summary: 'This user has been globally banned'
};
} )
.then( function () {
report('The user talk page has been replaced in '+ dbname + '.');
} );
}
} );
}
api.get( {
action: 'query',
assert: 'user',
list: 'users',
ususers: username,
usprop: 'groups'
} ).done( function ( data ) {
let groups = data.query.users[0].groups;
groups = groups.filter( i => !( [ '*', 'user'].includes(i) ));
if (groups) {
let metaApi = new mw.Api();
metaApi.postWithToken(
'userrights',
{
'action': 'userrights',
'user': username + '@' + dbname,
'remove': groups.join('|'),
'reason': 'WMF banned user',
}
).then( () => {
report('These local rights in ' + dbname + ' have been removed: ' + groups.join('|'));
});
}
} );
}
function carryOutTheBan(username) {
if (!username.trim()) {
report('Username is empty. Aborting.');
return;
}
if ( confirm("You are about to office ban " + username + '. Are you sure?') != true) {
report('Confirmation has not been done. Aborting.');
return;
}
report('Starting the office ban...');
report('Locking the user...');
let api = new mw.Api();
api.get(
{
'action': 'query',
'meta': 'globaluserinfo',
'guiuser': username,
'guiprop': 'groups',
'formatversion': '2'
}
).done( function (data ) {
if ( data.query.globaluserinfo.locked ) {
api.postWithToken(
'setglobalaccountstatus',
{
'action': 'setglobalaccountstatus',
'locked': 'unlock',
'user': username,
'reason': 'Switching to [[WMF Global Ban Policy|WMF Global Ban]]'
}
).then( () => {
report('The account has been unlocked to be locked again.');
api.postWithToken(
'setglobalaccountstatus',
{
'action': 'setglobalaccountstatus',
'locked': 'lock',
'user': username,
'reason': 'Re-lock - Globally or WMF banned user: [[m:WMF Global Ban Policy|Foundation Global Ban]] - do not reinstate. Questions can be directed to ca@wikimedia.org'
}
).then( () => {
report('The account has been locked.');
});
});
} else {
api.postWithToken(
'setglobalaccountstatus',
{
'action': 'setglobalaccountstatus',
'locked': 'lock',
'user': username,
'reason': 'Globally or WMF banned user: [[m:WMF Global Ban Policy|Foundation Global Ban]] - do not reinstate. Questions can be directed to ca@wikimedia.org'
}
).then( () => {
report('The account has been locked.');
});
}
});
report('Replacing the user page with template in meta...');
api.get( {
action: 'query',
assert: 'user',
prop: 'revisions',
titles: 'User:' + username,
rvprop: 'user'
} ).done( function ( data ) {
// Only replace if the user page exists
if ( !data.query.pages["-1"] ) {
api.edit( 'User:' + username, function ( revision ) {
return {
text: '{{WMF-legal banned user}}',
summary: 'This user has been globally banned'
};
} )
.then( function () {
report('The userpage has been replaced.');
} );
} else {
api.create( 'User:' + username, {summary: 'This user has been globally banned'}, '{{WMF-legal banned user}}')
.then( function () {
report('The userpage has been created.');
} );
}
} );
api.get( {
action: 'query',
assert: 'user',
prop: 'revisions',
titles: 'User talk:' + username,
rvprop: 'user'
} ).done( function ( data ) {
// Only replace if the user talk page exists
if ( !data.query.pages["-1"] ) {
api.edit( 'User talk:' + username, function ( revision ) {
return {
text: '{{WMF-legal banned user}}',
summary: 'This user has been globally banned'
};
} )
.then( function () {
report('The user talk page has been replaced.');
} );
} else {
api.create( 'User talk:' + username, {summary: 'This user has been globally banned'}, '{{WMF-legal banned user}}')
.then( function () {
report('The user talk page has been created.');
} );
}
} );
api.get(
{
'action': 'query',
'meta': 'globaluserinfo',
'guiuser': username,
'guiprop': 'groups|merged|editcount'
}
).done( function (data ) {
let groups = data.query.globaluserinfo.groups || [];
groups = groups.filter( i => !( [ '*', 'user'].includes(i) ));
if (groups) {
api.post(
{
'action': 'globaluserrights',
'user': username,
'remove': groups.join('|'),
'reason': 'WMF banned user'
}
).then( () => {
report('These global rights have been removed: ' + groups.join('|'));
});
}
let topWikis = data.query.globaluserinfo.merged.sort((a, b) => b.editcount - a.editcount ).map((i) => i.wiki);
let wikisWithRights = data.query.globaluserinfo.merged.filter((i) => i.groups ).map((i) => i.wiki);
let wikisToHandle = [...new Set([...topWikis ,...wikisWithRights])];
api.get(
{
'action': 'sitematrix',
'smsiteprop': 'dbname|url'
}
).done( function (data ) {
data.sitematrix.specials.forEach(site => {
if ( wikisToHandle.includes(site.dbname)) {
handleWiki( site.url + '/w/api.php', username, site.dbname);
}
});
// Sitematrix doesn't return an array, it returns an object with numeric keys
// but those keys are actually string. Fun.
for (const property_name in data.sitematrix) {
let numeric_value = Number(property_name);
if ( String(numeric_value) === property_name ) {
data.sitematrix[property_name].site.forEach( i => {
if ( wikisToHandle.includes(i.dbname)) {
report('Handling ' + i.dbname + ' ...')
handleWiki( i.url + '/w/api.php', username, i.dbname);
}
})
}
}
});
});
report('Adding to WMF banned list...');
api.edit( 'WMF Global Ban Policy/List', function ( revision ) {
let content = revision.content;
content = content.replace('\n{{div col end}}', '');
content += '\n* [[m:Special:CentralAuth/' + username + '|' + username + ']], since ' + new Date().toISOString().slice(0, 10);
content += '\n{{div col end}}';
return {
text: content,
summary: '+ ' + username
};
} )
.then( function () {
report('Added to the global ban list');
} );
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}
report('Removing from meta NDA noticeboard...');
api.edit( 'Access to nonpublic personal data policy/Noticeboard', function ( revision ) {
let content = revision.content;
let reg = new RegExp('\n\\{\\{\\/user\\|\\d+\\|' + escapeRegExp(username) + '}}' );
content = content.replace(reg, '');
return {
text: content,
summary: 'Strike globally banned user'
};
} )
.then( function () {
report('Removed from meta NDA noticeboard');
} );
}
function loadForm() {
let container = $('<div></div>').css('max-width', '300px').css('display', 'grid').css('row-gap', '10px');
container.append(
$('<div class="cdx-text-input"><input id="usernameInput" class="cdx-text-input__input" type="text" placeholder="Username (wihtout User: prefix)"/></div>')
);
$('#usernameInput').on('keyup', function (event) {
if (event.originalEvent.keyCode == 13) {
$('.cdx-button').attr('disabled', true);
carryOutTheBan(document.getElementById("usernameInput").value);
}
});
let configButton = $('<button class="cdx-button cdx-button--action-destructive cdx-button--weight-primary">Ban user</button>');
// TODO: Add support for more usernames for socks
configButton.on('click', () => {
$('.cdx-button').attr('disabled', true);
carryOutTheBan(document.getElementById("usernameInput").value);
});
container.append(configButton);
$('.mw-body-content').html(container);
}
mw.loader.using('codex-styles').then( loadForm() );
};
(function ($, mw) {
officeBanGadget();
}(jQuery, mediaWiki));