Jump to content

User:WMFOffice/OfficeBanGadget.js

From Meta, a Wikimedia project coordination wiki

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));