User:DreamRimmer/MassDelete.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.
//<nowiki>
$(document).ready(function() {
    function initializeMassDelete() {
        $('#mw-content-text > p').remove();
        $('#firstHeading').text('MassDelete');

        var pagesTextarea = new OO.ui.MultilineTextInputWidget({
                placeholder: 'Enter list of pages (one per line)',
                autosize: true,
                rows: 15
            }),
            reasonInputField = new OO.ui.TextInputWidget({
                placeholder: 'Reason for deletion'
            }),
            deleteTalkCheckbox = new OO.ui.CheckboxInputWidget({
                selected: true
            }),
            previewButton = new OO.ui.ButtonWidget({
                label: 'Preview Deletion',
                flags: ['primary']
            }),
            startButton = new OO.ui.ButtonWidget({
                label: 'Start Deletion',
                icon: 'alert',
                flags: ['primary', 'progressive'],
                disabled: true
            }),
            cancelButton = new OO.ui.ButtonWidget({
                label: 'Cancel',
                flags: ['primary', 'destructive'],
                href: 'https:' + mw.config.get('wgServer')
            }),
            logContainer = $("<div>").hide();

        var labels = {
            pagesLabel: $('<p>').text('Pages to Delete:').css('font-weight', 'bold'),
            reasonLabel: $('<p>').text('Reason:').css('font-weight', 'bold'),
            deleteTalkLabel: $('<label>')
                .append(
                    deleteTalkCheckbox.$element,
                    $('<span>').text(' Delete associated talk pages (if any exist)').css('padding-left', '5px')
                )
                .css({
                    'margin-top': '5px',
                    'margin-bottom': '5px'
                })
        };

        $('#mw-content-text').append(
            labels.pagesLabel, pagesTextarea.$element,
            labels.reasonLabel, reasonInputField.$element,
            '<br/>',
            labels.deleteTalkLabel,
            '<br/>',
            $('<div>').css({
                'display': 'flex',
                'gap': '10px',
                'align-items': 'center',
                'margin-top': '10px'
            }).append(
                previewButton.$element,
                startButton.$element,
                cancelButton.$element
            ),
            '<br/>',
            logContainer
        );

        var logList;

        function deletePage(page, reason, deleteTalk) {
            var params = {
                action: 'delete',
                title: page,
                reason: reason
            };
            if (deleteTalk) {
                params.deletetalk = true;
            }
            return new mw.Api({
                ajax: {
                    headers: {
                        'Api-User-Agent': 'meta:User:DreamRimmer/MassDelete.js'
                    }
                }
            }).postWithToken('csrf', params).then(function(data) {
                return { status: 'success', data: data, page: page };
            }).catch(function(code, data) {
                if (code === 'delete-error-associated-doesnotexist') {
                    return { status: 'skipped', data: { skipped: true, silent: true }, page: page };
                } else {
                    return { status: 'error', code: code, data: data, page: page };
                }
            });
        }

        function showAlert(message) {
            alert("Error: " + message);
        }

        function handleDeleteResponse(err, data, page) {
            if (data && data.silent) {
                return;
            }
            var logItem = $("<li>");
            if (err) {
                logItem.html("Failed to delete page <b>" + page + "</b>: " + err);
            } else {
                logItem.html("<b>" + page + "</b> deleted successfully.");
            }
            logList.append(logItem);
        }

        function previewDeleting() {
            var pages = pagesTextarea.getValue().replace(/^\s*[\r\n]/gm, '').split("\n"),
                reason = reasonInputField.getValue().trim(),
                deleteTalk = deleteTalkCheckbox.isSelected();
            if (pages[0].trim() === "" || reason === "") {
                showAlert("Please fill in all fields.");
                return;
            }
            logContainer.empty();
            $("<h1>").wrapInner("<span class='mw-headline'>Deletion preview</span>").appendTo(logContainer);
            logContainer.show();
            var previewLogList = $("<ul>").appendTo(logContainer);
            pages.forEach(function(page) {
                page = page.trim();
                if (page) {
                    previewLogList.append("<li><b>" + page + "</b> will be deleted for reason: <b>" + reason + "</b></li>");
                }
            });
            if (deleteTalk) {
                previewLogList.append("<li>Talk pages will also be deleted if they exist.</li>");
            }
            startButton.setDisabled(false);
        }

        function startDeleting() {
            if (!confirm("Are you sure you want to delete the listed pages? This action cannot be undone.")) {
                return;
            }
            var pages = pagesTextarea.getValue().replace(/^\s*[\r\n]/gm, '').split("\n").map(function(page) {
                return page.trim();
            }).filter(function(page) {
                return page !== '';
            });
            var reason = reasonInputField.getValue().trim();
            var deleteTalk = deleteTalkCheckbox.isSelected();
            var suffix = "";
            if (mw.config.get("wgUserGroups").indexOf("sysop") >= 0) {
                suffix = " (sysop action)";
            } else if (mw.config.get("wgGlobalGroups").indexOf("steward") >= 0) {
                suffix = " ([[m:stewards|steward]] action)";
            } else if (mw.config.get("wgGlobalGroups").indexOf("global-sysop") >= 0) {
                suffix = " ([[m:GS|global sysop]] action)";
            }
            reason += suffix + " (using [[m:User:DreamRimmer/MassDelete.js|MassDelete.js]])";
            if (pages.length === 0 || reason === "") {
                showAlert("Please fill in all fields.");
                return;
            }
            logContainer.empty();
            $("<h1>").wrapInner("<span class='mw-headline'>Deletion log</span>").appendTo(logContainer);
            logList = $("<ul>").appendTo(logContainer);
            logContainer.show();

            pages.reduce(function(promise, page) {
                return promise.then(function() {
                    return deletePage(page, reason, deleteTalk).then(function(result) {
                        if (result.status === 'success' || result.status === 'skipped') {
                            handleDeleteResponse(null, result.data, result.page);
                        } else if (result.status === 'error') {
                            handleDeleteResponse(result.code, result.data, result.page);
                        }
                        return result;
                    });
                });
            }, $.Deferred().resolve());
        }

        pagesTextarea.on('change', function() {
            previewButton.setDisabled(pagesTextarea.getValue().trim() === '' || reasonInputField.getValue().trim() === '');
            startButton.setDisabled(true);
        });

        reasonInputField.on('change', function() {
            previewButton.setDisabled(pagesTextarea.getValue().trim() === '' || reasonInputField.getValue().trim() === '');
            startButton.setDisabled(true);
        });

        previewButton.on('click', previewDeleting);
        startButton.on('click', startDeleting);
    }

    $.when(mw.loader.using('mediawiki.util'), $.ready).then(function() {
        mw.util.addPortletLink(
            'p-tb',
            mw.util.getUrl('Special:BlankPage/MassDelete'),
            'MassDelete'
        );
    });

    if (mw.config.get('wgCanonicalSpecialPageName') === 'Blankpage' && mw.config.get('wgTitle').split('/', 2)[1] === 'MassDelete') {
        $.when(mw.loader.using('oojs-ui-core'), $.ready).then(function() {
            initializeMassDelete();
        });
    }
});
//</nowiki>