Jump to content

User:Yahya/GS Action.js

From Meta, a Wikimedia project coordination wiki
This is an archived version of this page, as edited by Yahya (talk | contribs) at 20:20, 8 August 2024 (update). It may differ significantly from the current version.
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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.
$(function () {
    "use strict";

    function isGlobalSysopWiki() {
        var api = new mw.Api();
        var deferred = $.Deferred();

        api.get({
            action: 'query',
            list: 'wikisets',
            wsfrom: 'Opted-out of global sysop wikis',
            wsprop: 'wikisincluded',
            wslimit: 1
        }).done(function (data) {
            var isGSWiki = false;
            var wikisincluded = data.query.wikisets[0].wikisincluded;
            var wiki;

            for (wiki in wikisincluded) {
                if (wikisincluded[wiki] === mw.config.get('wgDBname')) {
                    isGSWiki = true;
                    break;
                }
            }

            deferred.resolve(isGSWiki);
        }).fail(function () {
            deferred.reject(false);
        });

        return deferred.promise();
    }

    function addToToolbar() {
        var portletLink = mw.util.addPortletLink(
            'p-tb',
            '#',
            'GS Actions',
            't-admin-actions',
            'Click to open administrative actions dialog',
            null
        );
        $(portletLink).click(function (e) {
            e.preventDefault();
            openAdminActionsDialog();
        });
    }

    const reasons = {
        delete: [
            "Spam",
            "Vandalism",
            "No useful content",
            "Nonsense",
            "Test page",
            "Not written in this project's language",
            "Requested by the author",
            "Outside of this project's scope",
            "Empty",
            "Broken redirect"
        ],
        protect: [
            "Persistent vandalism",
            "Persistent spamming",
            "Persistent copyright violation"
        ],
        block: [
            "Spam",
            "Vandalism",
            "Intimidating behaviour/harassment",
            "Cross-wiki vandalism",
            "Spambot",
            "Spam-only account",
            "Open proxy"
        ]
    };

    const protectionLevels = {
        sysop: 'Sysop',
        autoconfirmed: 'Autoconfirmed',
    };

    const protectionDurations = {
        'infinite': 'Infinite',
        '24 hours': '24 hours',
        '1 week': '1 week',
        '1 month': '1 month',
        '3 month': '3 month',
        'custom': 'Custom'
    };

    const blockDurations = {
        '24 hours': '24 hours',
        '1 week': '1 week',
        '1 month': '1 month',
        '3 month': '3 month',
        'indefinite': 'indefinite',
        'custom': 'Custom'
    };

    function AdminActionsDialog(config) {
        AdminActionsDialog.super.call(this, config);
    }
    OO.inheritClass(AdminActionsDialog, OO.ui.ProcessDialog);

    AdminActionsDialog.static.name = 'adminActionsDialog';
    AdminActionsDialog.static.title = 'Admin Actions';
    AdminActionsDialog.static.actions = [
        { action: 'submit', label: 'Submit', flags: ['primary', 'progressive'] },
        { label: 'Cancel', flags: 'safe' }
    ];

    AdminActionsDialog.prototype.initialize = function () {
        AdminActionsDialog.super.prototype.initialize.apply(this, arguments);
        var fieldset = new OO.ui.FieldsetLayout({
            label: 'Choose an action and reason'
        });

        var actionSelectField = new OO.ui.DropdownWidget({
            menu: {
                items: [
                    new OO.ui.MenuOptionWidget({ data: 'delete', label: 'Delete' }),
                    new OO.ui.MenuOptionWidget({ data: 'protect', label: 'Protect' }),
                    mw.config.get('wgNamespaceNumber') === 2 ? new OO.ui.MenuOptionWidget({ data: 'block', label: 'Block' }) : null
                ].filter(Boolean)
            }
        });

        var reasonSelectField = new OO.ui.DropdownWidget({
            disabled: true
        });

        var protectionLevelField = new OO.ui.DropdownWidget({
            disabled: true
        });

        var protectionDurationField = new OO.ui.DropdownWidget({
            disabled: true
        });

        var blockDurationField = new OO.ui.DropdownWidget({
            disabled: true
        });

        var customProtectionLevelInput = new OO.ui.TextInputWidget({
            disabled: true
        });

        var customDurationInput = new OO.ui.TextInputWidget({
            disabled: true
        });

        actionSelectField.getMenu().on('select', function (item) {
            if (item) {
                var action = item.getData();
                reasonSelectField.setDisabled(false);
                reasonSelectField.getMenu().clearItems();

                const groups = mw.config.get('wgGlobalGroups', []);
                let suffix = '';
                if (groups.includes('global-sysop')) {
                    suffix = " ([[m:GS|global sysop]] action)";
                } else if (groups.includes('steward')) {
                    suffix = " ([[m:steward|steward]] action)";
                }

                reasonSelectField.getMenu().addItems(reasons[action].map(function (reason) {
                    return new OO.ui.MenuOptionWidget({ data: reason + suffix, label: reason + suffix });
                }));

                if (action === 'protect') {
                    protectionLevelField.setDisabled(false);
                    protectionLevelField.getMenu().clearItems();
                    protectionLevelField.getMenu().addItems(Object.keys(protectionLevels).map(function (level) {
                        return new OO.ui.MenuOptionWidget({ data: level, label: protectionLevels[level] });
                    }));
                    protectionDurationField.setDisabled(false);
                    protectionDurationField.getMenu().clearItems();
                    protectionDurationField.getMenu().addItems(Object.keys(protectionDurations).map(function (duration) {
                        return new OO.ui.MenuOptionWidget({ data: duration, label: protectionDurations[duration] });
                    }));
                } else {
                    protectionLevelField.setDisabled(true);
                    protectionDurationField.setDisabled(true);
                }

                if (action === 'block') {
                    blockDurationField.setDisabled(false);
                    blockDurationField.getMenu().clearItems();
                    blockDurationField.getMenu().addItems(Object.keys(blockDurations).map(function (duration) {
                        return new OO.ui.MenuOptionWidget({ data: duration, label: blockDurations[duration] });
                    }));
                } else {
                    blockDurationField.setDisabled(true);
                }

                customProtectionLevelInput.setDisabled(true);
                customDurationInput.setDisabled(true);
            }
        });

        protectionLevelField.getMenu().on('select', function (item) {
            if (item) {
                var level = item.getData();
                if (level === 'custom') {
                    customProtectionLevelInput.setDisabled(false);
                } else {
                    customProtectionLevelInput.setDisabled(true);
                }
            }
        });

        protectionDurationField.getMenu().on('select', function (item) {
            if (item) {
                var duration = item.getData();
                if (duration === 'custom') {
                    customDurationInput.setDisabled(false);
                } else {
                    customDurationInput.setDisabled(true);
                }
            }
        });

        blockDurationField.getMenu().on('select', function (item) {
            if (item) {
                var duration = item.getData();
                if (duration === 'custom') {
                    customDurationInput.setDisabled(false);
                } else {
                    customDurationInput.setDisabled(true);
                }
            }
        });

        fieldset.addItems([
            actionSelectField,
            reasonSelectField,
            protectionLevelField,
            customProtectionLevelInput,
            protectionDurationField,
            blockDurationField,
            customDurationInput
        ]);
        
        this.content = new OO.ui.PanelLayout({
            padded: true,
            expanded: false
        });
        this.content.$element.append(fieldset.$element);
        this.$body.append(this.content.$element);
        this.actionSelectField = actionSelectField;
        this.reasonSelectField = reasonSelectField;
        this.protectionLevelField = protectionLevelField;
        this.protectionDurationField = protectionDurationField;
        this.blockDurationField = blockDurationField;
        this.customProtectionLevelInput = customProtectionLevelInput;
        this.customDurationInput = customDurationInput;
    };

    AdminActionsDialog.prototype.getActionProcess = function (action) {
        if (action === 'submit') {
            var selectedAction = this.actionSelectField.getMenu().findSelectedItem().getData();
            var selectedReason = this.reasonSelectField.getMenu().findSelectedItem().getData();
            var selectedProtectionLevel = this.protectionLevelField.getMenu().findSelectedItem()?.getData();
            var selectedProtectionDuration = this.protectionDurationField.getMenu().findSelectedItem()?.getData();
            var selectedBlockDuration = this.blockDurationField.getMenu().findSelectedItem()?.getData();
            var customProtectionLevel = this.customProtectionLevelInput.getValue();
            var customDuration = this.customDurationInput.getValue();

            var protectionLevel = selectedProtectionLevel === 'custom' ? customProtectionLevel : selectedProtectionLevel;
            var protectionDuration = selectedProtectionDuration === 'custom' ? customDuration : selectedProtectionDuration;
            var blockDuration = selectedBlockDuration === 'custom' ? customDuration : selectedBlockDuration;

            return new OO.ui.Process(function () {
                performAction(selectedAction, selectedReason, protectionLevel, protectionDuration, blockDuration);
                this.close();
            }, this);
        }
        return AdminActionsDialog.super.prototype.getActionProcess.call(this, action);
    };

    function performAction(action, reason, protectionLevel, protectionDuration, blockDuration) {
        var api = new mw.Api();
        var title = mw.config.get('wgPageName');

        if (action === 'delete') {
            api.postWithToken('csrf', {
                action: 'delete',
                title: title,
                reason: reason
            }).done(function () {
                mw.notify('Page deleted for reason: ' + reason, { type: 'success' });
            }).fail(function (code, error) {
                mw.notify('Failed to delete page: ' + error.info, { type: 'error' });
            });
        } else if (action === 'protect') {
            var protections = 'edit=' + protectionLevel + '|move=' + protectionLevel;
            api.postWithToken('csrf', {
                action: 'protect',
                title: title,
                protections: protections,
                expiry: protectionDuration,
                reason: reason
            }).done(function (data) {
                if (data && data.protect) {
                    mw.notify('Page protected for reason: ' + reason, { type: 'success' });
                } else {
                    mw.notify('Unexpected response from API. Protection may have failed.', { type: 'warn' });
                    console.log('API response:', data);
                }
            }).fail(function (code, error) {
                mw.notify('Failed to protect page: ' + error.info, { type: 'error' });
                console.error('Protection error:', code, error);
            });
        } else if (action === 'block') {
            api.postWithToken('csrf', {
                action: 'block',
                user: title.replace(/User:/, ''),
                expiry: blockDuration,
                reason: reason,
                autoblock: true
            }).done(function () {
                mw.notify('User blocked for reason: ' + reason, { type: 'success' });
            }).fail(function (code, error) {
                mw.notify('Failed to block user: ' + error.info, { type: 'error' });
            });
        }
    }

    function openAdminActionsDialog() {
        var windowManager = new OO.ui.WindowManager();
        $('body').append(windowManager.$element);
        var dialog = new AdminActionsDialog();
        windowManager.addWindows([dialog]);
        windowManager.openWindow(dialog);
    }

    mw.loader.using('oojs-ui-windows').then(function () {
        isGlobalSysopWiki().then(function (isGSWiki) {
            if (isGSWiki) {
                addToToolbar();
            } else {
                console.log('This script is only meant to run on global sysop wikis.');
            }
        });
    });
});