User:DR/test.js: Difference between revisions
From Test Wiki
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( |
|||
.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) { |
|||
if (item.getData() === 'category') { |
|||
pagesTextarea.setPlaceholder('Enter category names (one per line)'); |
|||
} else { |
|||
pagesTextarea.setPlaceholder('Enter list of pages (one per line)'); |
|||
}; |
|||
if (deleteTalk) { |
|||
params.deletetalk = true; |
|||
} |
} |
||
( |
logContainer.empty().hide(); |
||
startButton.setDisabled(true); |
|||
}); |
|||
'Api-User-Agent': 'en:User:DreamRimmer/MassDelete.js' |
|||
function getPagesFromCategory(category) { |
|||
} |
|||
return $.ajax({ |
|||
url: mw.util.wikiScript('api'), |
|||
data: { action: 'query', list: 'categorymembers', cmtitle: 'Category:' + category, cmlimit: 'max', format: 'json' }, |
|||
async: false |
|||
dataType: 'json' |
|||
}).then(function(data) { |
|||
return data.query?.categorymembers?.map(item => item.title) || []; |
|||
}).fail(function(code, data) { |
|||
callback(code, data, page); |
|||
}); |
}); |
||
} |
} |
||
function |
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) { |
function handleDeleteResponse(err, data, page) { |
||
var logList = $( |
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 |
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(); |
||
$( |
var logList = $('<ul>').appendTo(logContainer); |
||
logContainer.show(); |
|||
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>"); |
|||
}); |
|||
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 |
var deleteTalk = deleteTalkCheckbox.isSelected(); |
||
reason = reasonInputField.getValue().trim(), |
|||
function processNextPage(index = 0) { |
|||
if (index >= pages.length) return; |
|||
deletePage(pages[index], reason, deleteTalk, function(err, data) { |
|||
if (mw.config.get("wgUserGroups").indexOf("sysop") >= 0) { |
|||
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( |
$.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( |
$.when(mw.loader.using('oojs-ui-core'), $.ready).then(initializeMassDelete); |
||
initializeMassDelete(); |
|||
}); |
|||
} |
} |
||
}); |
}); |
||
//</nowiki> |
//</nowiki> |
||