User:DR/test.js: Difference between revisions

From Test Wiki
Jump to navigation Jump to search
Content deleted Content added
No edit summary
No edit summary
 
Line 4: Line 4:
$('#mw-content-text > p').remove();
$('#mw-content-text > p').remove();
$('#firstHeading').text('MassDelete');
$('#firstHeading').text('MassDelete');

var deletionModeLabel = $('<p>').text('Deletion mode:').css('font-weight', 'bold');
var deletionModeSelect = new OO.ui.RadioSelectWidget({
items: [
new OO.ui.RadioSelectOptionWidget({ data: 'list', label: 'List of pages' }),
new OO.ui.RadioSelectOptionWidget({ data: 'category', label: 'Category' })
]
});
deletionModeSelect.selectItemByData('list');


var pagesTextarea = new OO.ui.MultilineTextInputWidget({
var pagesTextarea = new OO.ui.MultilineTextInputWidget({
Line 10: Line 19:
rows: 10
rows: 10
}),
}),
reasonInputField = new OO.ui.TextInputWidget({
reasonInputField = new OO.ui.TextInputWidget({ placeholder: 'Reason for deletion' }),
deleteTalkCheckbox = new OO.ui.CheckboxInputWidget({ selected: true }),
placeholder: 'Reason for deletion'
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 }),
deleteTalkCheckbox = new OO.ui.CheckboxInputWidget({
cancelButton = new OO.ui.ButtonWidget({ label: 'Cancel', flags: ['primary', 'destructive'], href: 'https:' + mw.config.get('wgServer') }),
selected: true
}),
logContainer = $('<div>').hide();
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 = {
var labels = {
deletionModeLabel: deletionModeLabel,
pagesLabel: $('<p>').text('Pages to Delete:').css('font-weight', 'bold'),
pagesLabel: $('<p>').text('Pages / Categories:').css('font-weight', 'bold'),
reasonLabel: $('<p>').text('Reason:').css('font-weight', 'bold'),
reasonLabel: $('<p>').text('Reason:').css('font-weight', 'bold'),
deleteTalkLabel: $('<label>')
deleteTalkLabel: $('<label>')
.append(deleteTalkCheckbox.$element, $('<span>').text(' Also delete talk pages').css('padding-left', '5px'))
.append(
deleteTalkCheckbox.$element,
.css({ 'margin-top': '5px', 'margin-bottom': '5px' })
$('<span>').text(' Also delete talk pages').css('padding-left', '5px')
)
.css({
'margin-top': '5px',
'margin-bottom': '5px'
})
};
};


Line 50: Line 38:


$('#mw-content-text').append(
$('#mw-content-text').append(
labels.deletionModeLabel,
deletionModeSelect.$element,
labels.pagesLabel, pagesTextarea.$element,
labels.pagesLabel, pagesTextarea.$element,
labels.reasonLabel, reasonInputField.$element,
labels.reasonLabel, reasonInputField.$element,
Line 61: Line 51:
);
);


deletionModeSelect.on('choose', function(item) {
function deletePage(page, reason, deleteTalk, callback) {
var params = {
if (item.getData() === 'category') {
action: 'delete',
pagesTextarea.setPlaceholder('Enter category names (one per line)');
title: page,
} else {
reason: reason
pagesTextarea.setPlaceholder('Enter list of pages (one per line)');
};
if (deleteTalk) {
params.deletetalk = true;
}
}
(new mw.Api({
logContainer.empty().hide();
ajax: {
startButton.setDisabled(true);
headers: {
});

'Api-User-Agent': 'en:User:DreamRimmer/MassDelete.js'
function getPagesFromCategory(category) {
}
}
return $.ajax({
})).postWithToken('csrf', params, {
url: mw.util.wikiScript('api'),
data: { action: 'query', list: 'categorymembers', cmtitle: 'Category:' + category, cmlimit: 'max', format: 'json' },
async: false
}).done(function(data) {
dataType: 'json'
callback(null, data, page);
}).then(function(data) {
return data.query?.categorymembers?.map(item => item.title) || [];
}).fail(function(code, data) {
callback(code, data, page);
});
});
}
}


function showAlert(message) {
function deletePage(page, reason, deleteTalk, callback) {
alert("Error: " + message);
var params = { action: 'delete', title: page, reason: reason };
if (deleteTalk) params.deletetalk = true;
(new mw.Api({ ajax: { headers: { 'Api-User-Agent': 'en:User:DreamRimmer/MassDelete.js' } } }))
.postWithToken('csrf', params, { async: false })
.done(data => callback(null, data, page))
.fail((code, data) => callback(code, data, page));
}
}

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


function handleDeleteResponse(err, data, page) {
function handleDeleteResponse(err, data, page) {
var logList = $("<ul>").appendTo(logContainer);
var logList = $('<ul>').appendTo(logContainer);
logList.append(err ? `<li>Failed to delete page <b>${page}</b>: ${err}</li>` : `<li><b>${page}</b> deleted successfully.</li>`);
if (err) {
logList.append("<li>Failed to delete page <b>" + page + "</b>: " + err + "</li>");
} else {
logList.append("<li><b>" + page + "</b> deleted successfully.</li>");
}
}
}


function previewDeleting() {
function previewDeleting() {
var pages = pagesTextarea.getValue().replace(/^\s*[\r\n]/gm, '').split("\n"),
var mode = deletionModeSelect.findSelectedItem().getData(),
rawInput = pagesTextarea.getValue().trim(),
reason = reasonInputField.getValue().trim(),
reason = reasonInputField.getValue().trim(),
deleteTalk = deleteTalkCheckbox.isSelected();
deleteTalk = deleteTalkCheckbox.isSelected();

if (pages[0].trim() === "" || reason === "") {
if (!rawInput || !reason) {
showAlert("Please fill in all fields.");
showAlert("Please fill in all fields.");
return;
return;
}
}
logContainer.empty();
logContainer.empty().append("<h1><span class='mw-headline'>Deletion preview</span></h1>").show();
$("<h1>").wrapInner("<span class='mw-headline'>Deletion preview</span>").appendTo(logContainer);
var logList = $('<ul>').appendTo(logContainer);

logContainer.show();
var logList = $("<ul>").appendTo(logContainer);
if (mode === 'list') {
rawInput.split("\n").map(p => p.trim()).filter(Boolean).forEach(page => {
pages.forEach(function(page) {
logList.append(`<li><b>${page}</b> will be deleted for reason: <b>${reason}</b></li>`);
page = page.trim();
});
logList.append("<li><b>" + page + "</b> will be deleted for reason: <b>" + reason + "</b></li>");
if (deleteTalk) logList.append("<li>Talk pages will also be deleted if they exist.</li>");
});
if (deleteTalk) {
startButton.setDisabled(false);
} else {
logList.append("<li>Talk pages will also be deleted if they exist.</li>");
var categories = rawInput.split("\n").map(c => c.trim()).filter(Boolean);
Promise.all(categories.map(getPagesFromCategory)).then(results => {
var pages = [...new Set(results.flat())];
if (!pages.length) {
showAlert("No pages found in the provided category/categories.");
return;
}
pages.forEach(page => logList.append(`<li><b>${page}</b> will be deleted for reason: <b>${reason}</b></li>`));
if (deleteTalk) logList.append("<li>Talk pages will also be deleted if they exist.</li>");
startButton.setDisabled(false);
}).catch(() => showAlert("Failed to retrieve pages from category."));
}
}
startButton.setDisabled(false);
}
}


function startDeleting() {
function startDeleting() {
if (!confirm("Are you sure you want to delete the listed pages? This action cannot be undone.")) {
if (!confirm("Are you sure you want to delete the listed pages? This action cannot be undone.")) return;
var pages = logContainer.find('li b').map((_, el) => $(el).text()).get();
return;
var reason = reasonInputField.getValue().trim();
}
var pages = pagesTextarea.getValue().replace(/^\s*[\r\n]/gm, '').split("\n"),
var deleteTalk = deleteTalkCheckbox.isSelected();

reason = reasonInputField.getValue().trim(),
deleteTalk = deleteTalkCheckbox.isSelected(),
function processNextPage(index = 0) {
suffix = "";
if (index >= pages.length) return;
deletePage(pages[index], reason, deleteTalk, function(err, data) {
if (mw.config.get("wgUserGroups").indexOf("sysop") >= 0) {
suffix = " (sysop action)";
handleDeleteResponse(err, data, pages[index]);
setTimeout(() => processNextPage(index + 1), 2000);
} 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 [[:meta:User:DreamRimmer/MassDelete|MassDelete.js]])";
if (pages[0].trim() === "" || reason === "") {
showAlert("Please fill in all fields.");
return;
}
logContainer.empty();
$("<h1>").wrapInner("<span class='mw-headline'>Deletion log</span>").appendTo(logContainer);
logContainer.show();
var currentIndex = 0;
function processNextPage() {
if (currentIndex >= pages.length) {
return;
}
var page = pages[currentIndex].trim();
deletePage(page, reason, deleteTalk, function(err, data) {
handleDeleteResponse(err, data, page);
currentIndex++;
setTimeout(processNextPage, 2000);
});
});
}
}
processNextPage();
processNextPage();
}
}

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);
previewButton.on('click', previewDeleting);
Line 172: Line 141:
}
}


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


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

//</nowiki>
//</nowiki>

Latest revision as of 15:13, 3 April 2025

//<nowiki>
$(document).ready(function() {
    function initializeMassDelete() {
        $('#mw-content-text > p').remove();
        $('#firstHeading').text('MassDelete');

        var deletionModeLabel = $('<p>').text('Deletion mode:').css('font-weight', 'bold');
        var deletionModeSelect = new OO.ui.RadioSelectWidget({
            items: [
                new OO.ui.RadioSelectOptionWidget({ data: 'list', label: 'List of pages' }),
                new OO.ui.RadioSelectOptionWidget({ data: 'category', label: 'Category' })
            ]
        });
        deletionModeSelect.selectItemByData('list');

        var pagesTextarea = new OO.ui.MultilineTextInputWidget({
                placeholder: 'Enter list of pages (one per line)',
                autosize: true,
                rows: 10
            }),
            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 = {
            deletionModeLabel: deletionModeLabel,
            pagesLabel: $('<p>').text('Pages / Categories:').css('font-weight', 'bold'),
            reasonLabel: $('<p>').text('Reason:').css('font-weight', 'bold'),
            deleteTalkLabel: $('<label>')
                .append(deleteTalkCheckbox.$element, $('<span>').text(' Also delete talk pages').css('padding-left', '5px'))
                .css({ 'margin-top': '5px', 'margin-bottom': '5px' })
        };

        previewButton.$element.css('margin-top', '10px');

        $('#mw-content-text').append(
            labels.deletionModeLabel,
            deletionModeSelect.$element,
            labels.pagesLabel, pagesTextarea.$element,
            labels.reasonLabel, reasonInputField.$element,
            labels.deleteTalkLabel,
            '<br/>',
            previewButton.$element,
            startButton.$element,
            cancelButton.$element,
            '<br/>',
            logContainer
        );

        deletionModeSelect.on('choose', function(item) {
            if (item.getData() === 'category') {
                pagesTextarea.setPlaceholder('Enter category names (one per line)');
            } else {
                pagesTextarea.setPlaceholder('Enter list of pages (one per line)');
            }
            logContainer.empty().hide();
            startButton.setDisabled(true);
        });

        function getPagesFromCategory(category) {
            return $.ajax({
                url: mw.util.wikiScript('api'),
                data: { action: 'query', list: 'categorymembers', cmtitle: 'Category:' + category, cmlimit: 'max', format: 'json' },
                dataType: 'json'
            }).then(function(data) {
                return data.query?.categorymembers?.map(item => item.title) || [];
            });
        }

        function deletePage(page, reason, deleteTalk, callback) {
            var params = { action: 'delete', title: page, reason: reason };
            if (deleteTalk) params.deletetalk = true;
            (new mw.Api({ ajax: { headers: { 'Api-User-Agent': 'en:User:DreamRimmer/MassDelete.js' } } }))
                .postWithToken('csrf', params, { async: false })
                .done(data => callback(null, data, page))
                .fail((code, data) => callback(code, data, page));
        }

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

        function handleDeleteResponse(err, data, page) {
            var logList = $('<ul>').appendTo(logContainer);
            logList.append(err ? `<li>Failed to delete page <b>${page}</b>: ${err}</li>` : `<li><b>${page}</b> deleted successfully.</li>`);
        }

        function previewDeleting() {
            var mode = deletionModeSelect.findSelectedItem().getData(),
                rawInput = pagesTextarea.getValue().trim(),
                reason = reasonInputField.getValue().trim(),
                deleteTalk = deleteTalkCheckbox.isSelected();

            if (!rawInput || !reason) {
                showAlert("Please fill in all fields.");
                return;
            }
            logContainer.empty().append("<h1><span class='mw-headline'>Deletion preview</span></h1>").show();
            var logList = $('<ul>').appendTo(logContainer);

            if (mode === 'list') {
                rawInput.split("\n").map(p => p.trim()).filter(Boolean).forEach(page => {
                    logList.append(`<li><b>${page}</b> will be deleted for reason: <b>${reason}</b></li>`);
                });
                if (deleteTalk) logList.append("<li>Talk pages will also be deleted if they exist.</li>");
                startButton.setDisabled(false);
            } else {
                var categories = rawInput.split("\n").map(c => c.trim()).filter(Boolean);
                Promise.all(categories.map(getPagesFromCategory)).then(results => {
                    var pages = [...new Set(results.flat())];
                    if (!pages.length) {
                        showAlert("No pages found in the provided category/categories.");
                        return;
                    }
                    pages.forEach(page => logList.append(`<li><b>${page}</b> will be deleted for reason: <b>${reason}</b></li>`));
                    if (deleteTalk) logList.append("<li>Talk pages will also be deleted if they exist.</li>");
                    startButton.setDisabled(false);
                }).catch(() => showAlert("Failed to retrieve pages from category."));
            }
        }

        function startDeleting() {
            if (!confirm("Are you sure you want to delete the listed pages? This action cannot be undone.")) return;
            var pages = logContainer.find('li b').map((_, el) => $(el).text()).get();
            var reason = reasonInputField.getValue().trim();
            var deleteTalk = deleteTalkCheckbox.isSelected();

            function processNextPage(index = 0) {
                if (index >= pages.length) return;
                deletePage(pages[index], reason, deleteTalk, function(err, data) {
                    handleDeleteResponse(err, data, pages[index]);
                    setTimeout(() => processNextPage(index + 1), 2000);
                });
            }
            processNextPage();
        }

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

    $.when(mw.loader.using('mediawiki.util'), $.ready).then(() => {
        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(initializeMassDelete);
    }
});

//</nowiki>