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>