MediaWiki:Gadget-MassRollback.js: Difference between revisions
From Test Wiki
Content deleted Content added
No edit summary Tags: Mobile edit Mobile web edit |
Update Tags: Mobile edit Mobile web edit |
||
| Line 1: | Line 1: | ||
// |
// Author: [[User:BZPN]] |
||
(function($, mw) { |
|||
$(document).ready(function () { |
|||
'use strict'; |
|||
if (mw.config.get('wgCanonicalSpecialPageName') !== 'Contributions') { |
|||
return; |
|||
} |
|||
const MassRollback = { |
|||
// Dodaj nowy rozwijany box |
|||
if ($('#rollback-box').length === 0) { |
|||
init: function() { |
|||
if (mw.config.get('wgCanonicalSpecialPageName') !== 'Contributions') { |
|||
<div id="rollback-box" class="mw-htmlform-ooui-wrapper oo-ui-layout oo-ui-panelLayout oo-ui-panelLayout-padded oo-ui-panelLayout-framed"> |
|||
return; |
|||
<form id="rollback-form" class="mw-htmlform mw-htmlform-ooui oo-ui-layout oo-ui-formLayout"> |
|||
} |
|||
<fieldset class="oo-ui-layout oo-ui-labelElement oo-ui-fieldsetLayout mw-collapsibleFieldsetLayout mw-collapsible mw-made-collapsible"> |
|||
this.createUI(); |
|||
<legend role="button" class="oo-ui-fieldsetLayout-header mw-collapsible-toggle mw-collapsible-toggle-expanded" aria-expanded="true" tabindex="0"> |
|||
this.bindEvents(); |
|||
<span class="oo-ui-iconElement-icon oo-ui-iconElement-noIcon"></span> |
|||
this.fetchUserStats(); |
|||
<span class="oo-ui-labelElement-label">Contributions rollback</span> |
|||
}, |
|||
<span class="oo-ui-widget oo-ui-widget-enabled oo-ui-iconElement-icon oo-ui-icon-expand oo-ui-iconElement oo-ui-labelElement-invisible oo-ui-iconWidget">Expand</span> |
|||
<span class="oo-ui-widget oo-ui-widget-enabled oo-ui-iconElement-icon oo-ui-icon-collapse oo-ui-iconElement oo-ui-labelElement-invisible oo-ui-iconWidget">Collapse</span> |
|||
createUI: function() { |
|||
</legend> |
|||
const $container = $(` |
|||
<div class="oo-ui-fieldsetLayout-group mw-collapsible-content" style="display: block;"> |
|||
<div id="mass-rollback-container" style="border: 1px solid #ccc; padding: 15px; margin-bottom: 20px; border-radius: 4px; background: #f9f9f9; display: none;"> |
|||
<div class="oo-ui-widget oo-ui-widget-enabled"> |
|||
<h3 style="margin-top: 0;">Mass Rollback Tool</h3> |
|||
<select id="rollback-reason" class="oo-ui-inputWidget-input oo-ui-dropdownWidget"> |
|||
<div id="stats-section" style="margin-bottom: 15px;"> |
|||
<h4>User statistics</h4> |
|||
<p>Total edits: <strong id="total-edits">-</strong></p> |
|||
<p>First edit: <span id="first-edit">-</span></p> |
|||
<p>Latest edit: <span id="last-edit">-</span> <button id="refresh-stats" style="padding: 3px 8px; background-color: #aaa; color: #fff; border: none; border-radius: 3px; cursor: pointer;">Refresh</button></p> |
|||
<option value="Revert">Revert</option> |
|||
</div> |
|||
<input type="text" id="rollback-summary" placeholder="Reason (optional)" class="oo-ui-inputWidget-input oo-ui-textInputWidget-input" style="margin-top: 10px; padding: 5px; width: 100%;"/> |
|||
<hr style="margin: 15px 0;"> |
|||
<div class="oo-ui-widget oo-ui-widget-enabled mw-htmlform-submit-buttons"> |
|||
<div id="filters-section" style="margin-bottom: 15px;"> |
|||
<button id="rollback-selected-button" class="oo-ui-inputWidget-input oo-ui-buttonElement-button oo-ui-buttonElement-framed oo-ui-flaggedElement-progressive" style="padding: 5px 10px; background-color: #007bff; color: #fff; border: none; cursor: pointer; border-radius: 4px;">Revert selected edits</button> |
|||
<h4>Filter Edits</h4> |
|||
<button id="rollback-all-button" class="oo-ui-inputWidget-input oo-ui-buttonElement-button oo-ui-buttonElement-framed oo-ui-flaggedElement-destructive" style="padding: 5px 10px; background-color: #ff4136; color: #fff; border: none; cursor: pointer; border-radius: 4px;">Revert all edits</button> |
|||
<div style="margin-bottom: 10px;"> |
|||
<button id="cancel-rollback" class="oo-ui-inputWidget-input oo-ui-buttonElement-button oo-ui-buttonElement-framed" style="padding: 5px 10px; background-color: #aaa; color: #fff; border: none; cursor: pointer; border-radius: 4px;">Cancel</button> |
|||
</ |
<label style="margin-right: 5px;">Date From:</label> |
||
<input type="date" id="start-date" style="padding: 4px;"> |
|||
<label style="margin: 0 5px;">To:</label> |
|||
<input type="date" id="end-date" style="padding: 4px;"> |
|||
</div> |
</div> |
||
< |
<div style="margin-bottom: 10px;"> |
||
</ |
<label style="margin-right: 5px;">Namespace:</label> |
||
<select id="namespace-filter" multiple style="padding: 4px; min-width: 150px;"> |
|||
</div> |
|||
<option value="all">All</option> |
|||
`); |
|||
<option value="0">Main</option> |
|||
<option value="1">Talk</option> |
|||
<option value="2">User</option> |
|||
<option value="3">User talk</option> |
|||
<option value="4">Project</option> |
|||
<option value="5">Project talk</option> |
|||
<option value="6">File</option> |
|||
<option value="7">File talk</option> |
|||
<option value="8">MediaWiki</option> |
|||
<option value="9">MediaWiki talk</option> |
|||
<option value="10">Template</option> |
|||
<option value="11">Template talk</option> |
|||
<option value="12">Help</option> |
|||
<option value="13">Help talk</option> |
|||
<option value="14">Category</option> |
|||
<option value="15">Category talk</option> |
|||
<option value="100">Portal</option> |
|||
<option value="101">Portal talk</option> |
|||
</select> |
|||
</div> |
|||
<div style="margin-bottom: 10px;"> |
|||
<label style="margin-right: 5px;">Edit Size:</label> |
|||
<select id="size-filter" style="padding: 4px;"> |
|||
<option value="">Any</option> |
|||
<option value="small">Small (< 50 bytes)</option> |
|||
<option value="medium">Medium (50-500 bytes)</option> |
|||
<option value="large">Large (> 500 bytes)</option> |
|||
</select> |
|||
</div> |
|||
<div style="margin-bottom: 10px;"> |
|||
<label style="margin-right: 5px;">Sort Order:</label> |
|||
<select id="sort-order" style="padding: 4px;"> |
|||
<option value="desc">Latest first</option> |
|||
<option value="asc">Oldest first</option> |
|||
</select> |
|||
</div> |
|||
<div style="margin-bottom: 10px;"> |
|||
<button id="clear-filters" style="padding: 5px 10px; background-color: #aaa; color: #fff; border: none; border-radius: 3px; cursor: pointer;">Clear filters</button> |
|||
<button id="show-filtered" style="padding: 5px 10px; background-color: #007bff; color: #fff; border: none; border-radius: 3px; cursor: pointer; margin-left: 5px;">Show filtered edits</button> |
|||
</div> |
|||
</div> |
|||
<hr style="margin: 15px 0;"> |
|||
<div id="reason-section" style="margin-bottom: 15px;"> |
|||
<h4>Rollback reason</h4> |
|||
<select id="rollback-reason" style="padding: 4px;"> |
|||
<option value=""></option> |
|||
<option value="vandalism">Vandalism</option> |
|||
<option value="spam">Spam</option> |
|||
<option value="test">Test rollback</option> |
|||
<option value="teste">Test edit</option> |
|||
<option value="rules-violation">Violation</option> |
|||
<option value="other">Other</option> |
|||
</select> |
|||
<input type="text" id="custom-reason" placeholder="Enter custom reason" style="display:none; padding: 4px; margin-top: 10px; width: 100%;"> |
|||
</div> |
|||
<div id="actions-section" style="text-align: center;"> |
|||
<button id="rollback-selected" style="padding: 5px 10px; background-color: #007bff; color: #fff; border: none; border-radius: 3px; cursor: pointer; margin-right: 5px;">Rollback selected</button> |
|||
<button id="rollback-all" style="padding: 5px 10px; background-color: #ff4136; color: #fff; border: none; border-radius: 3px; cursor: pointer;">Rollback all</button> |
|||
</div> |
|||
</div> |
|||
<div id="filtered-edits-container" style="display:none; border: 1px solid #ccc; padding: 15px; margin-bottom:20px; border-radius: 4px; background: #f9f9f9;"> |
|||
<h4>Filtered edits</h4> |
|||
<div style="margin-bottom: 10px;"> |
|||
<input type="checkbox" id="select-all"> <label for="select-all">Select All</label> |
|||
</div> |
|||
<table id="filtered-edits-table" style="width: 100%; border-collapse: collapse;"> |
|||
<thead> |
|||
<tr style="background: #e9e9e9;"> |
|||
<th style="padding: 5px; border: 1px solid #ccc;">Revid</th> |
|||
<th style="padding: 5px; border: 1px solid #ccc;">Title</th> |
|||
<th style="padding: 5px; border: 1px solid #ccc;">Timestamp</th> |
|||
<th style="padding: 5px; border: 1px solid #ccc;">Namespace</th> |
|||
<th style="padding: 5px; border: 1px solid #ccc;">Size</th> |
|||
<th style="padding: 5px; border: 1px solid #ccc;">Select</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<!-- Wstawiane dynamicznie --> |
|||
</tbody> |
|||
</table> |
|||
<div style="text-align: center; margin-top: 10px;"> |
|||
<button id="rollback-selected-filtered" style="padding: 5px 10px; background-color: #007bff; color: #fff; border: none; border-radius: 3px; cursor: pointer;">Rollback selected filtered edits</button> |
|||
</div> |
|||
</div> |
|||
`); |
|||
const $toggleButton = $(` |
|||
<button id="mass-rollback-toggle" style="padding: 5px 10px; margin-bottom: 10px; background-color: #007bff; color: #fff; border: none; border-radius: 3px; cursor: pointer;"> |
|||
Show/Hide MassRollback |
|||
</button> |
|||
`); |
|||
$toggleButton.on('click', function() { |
|||
$('#mass-rollback-container').slideToggle(); |
|||
}); |
|||
$('#mw-content-text').prepend($toggleButton).prepend($container); |
|||
}, |
|||
bindEvents: function() { |
|||
$('#mw-content-text').prepend($rollbackBox); |
|||
$('#rollback-reason').on('change', function() { |
|||
$('#custom-reason').toggle($(this).val() === 'other'); |
|||
}); |
|||
$('#clear-filters').on('click', function() { |
|||
// Synchronize dropdown with input field |
|||
$('# |
$('#start-date').val(''); |
||
$('#end-date').val(''); |
|||
$('#namespace-filter').val(['all']); |
|||
$('#size-filter').val(''); |
|||
$('# |
$('#sort-order').val('desc'); |
||
} |
}); |
||
}); |
|||
$('#refresh-stats').on('click', this.fetchUserStats.bind(this)); |
|||
// 1 |
|||
$('#show-filtered').on('click', this.displayFilteredEdits.bind(this)); |
|||
$('#rollback-box .mw-collapsible-toggle').click(function () { |
|||
$('#rollback-selected-filtered').on('click', this.rollbackSelectedFromFiltered.bind(this)); |
|||
var $content = $(this).closest('fieldset').find('.mw-collapsible-content'); |
|||
$('#rollback-selected').on('click', this.rollbackSelected.bind(this)); |
|||
var isExpanded = $(this).attr('aria-expanded') === 'true'; |
|||
$('#rollback-all').on('click', this.rollbackAll.bind(this)); |
|||
$(document).on('change', '#select-all', function() { |
|||
$ |
const isChecked = $(this).is(':checked'); |
||
$( |
$('#filtered-edits-table tbody input[type="checkbox"]').prop('checked', isChecked); |
||
}); |
|||
$(this).find('.oo-ui-icon-expand').show(); |
|||
} else { |
|||
$content.slideDown(); |
|||
$(this).attr('aria-expanded', 'true'); |
|||
$(this).find('.oo-ui-icon-expand').hide(); |
|||
$(this).find('.oo-ui-icon-collapse').show(); |
|||
} |
|||
}); |
|||
// Dodaj checkbox przy edycjach w widoku kontrybucji |
|||
// Rollback selected |
|||
$(' |
$('li[data-mw-revid]').each(function() { |
||
const $li = $(this); |
|||
const revid = $li.data('mw-revid'); |
|||
const parentid = $li.data('mw-prev-revid'); |
|||
const title = $li.find('.mw-contributions-title').text().trim(); |
|||
const $checkbox = $('<input>', { |
|||
type: 'checkbox', |
|||
class: 'rollback-checkbox', |
|||
'data-revid': revid, |
|||
'data-parentid': parentid, |
|||
'data-title': title, |
|||
style: 'margin-right: 5px;' |
|||
}); |
|||
$li.prepend($checkbox); |
|||
}); |
|||
}, |
|||
fetchUserStats: function() { |
|||
var selectedRevisions = $('input[type="checkbox"].rollback-checkbox:checked').map(function () { |
|||
const userName = mw.config.get('wgRelevantUserName'); |
|||
const api = new mw.Api(); |
|||
api.get({ |
|||
action: 'query', |
|||
list: 'usercontribs', |
|||
ucuser: userName, |
|||
uclimit: 'max', |
|||
ucprop: 'timestamp' |
|||
}).done(function(data) { |
|||
const contributions = data.query.usercontribs; |
|||
$('#total-edits').text(contributions.length); |
|||
if (contributions.length > 0) { |
|||
const timestamps = contributions.map(c => new Date(c.timestamp)).sort((a, b) => a - b); |
|||
$('#first-edit').text(timestamps[0].toLocaleDateString()); |
|||
$('#last-edit').text(timestamps[timestamps.length - 1].toLocaleDateString()); |
|||
} |
|||
}); |
|||
}, |
|||
getNamespaceName: function(ns) { |
|||
if (selectedRevisions.length === 0) { |
|||
const nsMapping = { |
|||
alert('Nie zaznaczono żadnych edycji do cofnięcia.'); |
|||
0: 'Main', |
|||
1: 'Talk', |
|||
2: 'User', |
|||
3: 'User talk', |
|||
4: 'Project', |
|||
5: 'Project talk', |
|||
6: 'File', |
|||
7: 'File talk', |
|||
8: 'MediaWiki', |
|||
9: 'MediaWiki talk', |
|||
10: 'Template', |
|||
11: 'Template talk', |
|||
12: 'Help', |
|||
13: 'Help talk', |
|||
14: 'Category', |
|||
15: 'Category talk', |
|||
100: 'Portal', |
|||
101: 'Portal talk' |
|||
}; |
|||
return nsMapping[ns] || ns; |
|||
}, |
|||
applyFilters: function(contributions) { |
|||
const startDate = $('#start-date').val() ? new Date($('#start-date').val()) : null; |
|||
var formattedSummary = `Reverted edit by [[User:${userName}|${userName}]] using [[Gadget:MassRollback|gadget]]` + (summary ? `: ${summary}` : ''); |
|||
const endDate = $('#end-date').val() ? new Date($('#end-date').val()) : null; |
|||
let namespaceFilter = $('#namespace-filter').val() || []; |
|||
const sizeFilter = $('#size-filter').val(); |
|||
const sortOrder = $('#sort-order').val(); |
|||
if (namespaceFilter.includes('all')) { |
|||
namespaceFilter = []; |
|||
} |
|||
undo: contrib.revid, |
|||
let filtered = contributions.filter(contrib => { |
|||
const contribDate = new Date(contrib.timestamp); |
|||
if (startDate && contribDate < startDate) return false; |
|||
if (endDate && contribDate > endDate) return false; |
|||
if (namespaceFilter.length > 0 && !namespaceFilter.includes(contrib.ns.toString())) return false; |
|||
if (sizeFilter) { |
|||
const size = contrib.size || 0; |
|||
switch (sizeFilter) { |
|||
case 'small': return size < 50; |
|||
case 'medium': return size >= 50 && size <= 500; |
|||
case 'large': return size > 500; |
|||
} |
|||
} |
|||
return true; |
|||
}); |
}); |
||
filtered.sort((a, b) => { |
|||
return sortOrder === 'asc' |
|||
? new Date(a.timestamp) - new Date(b.timestamp) |
|||
: new Date(b.timestamp) - new Date(a.timestamp); |
|||
}).catch(function (error) { |
|||
console.error('An error occured when reverting changes:', error); |
|||
alert('Wystąpił błąd podczas wycofywania edycji.'); |
|||
}); |
}); |
||
}); |
|||
return filtered; |
|||
}, |
|||
$('#rollback-all-button').click(function () { |
|||
var userName = mw.config.get('wgRelevantUserName'); |
|||
var summary = $('#rollback-summary').val(); |
|||
var api = new mw.Api(); |
|||
displayFilteredEdits: function() { |
|||
const userName = mw.config.get('wgRelevantUserName'); |
|||
const api = new mw.Api(); |
|||
api.get({ |
api.get({ |
||
action: 'query', |
action: 'query', |
||
| Line 120: | Line 273: | ||
ucuser: userName, |
ucuser: userName, |
||
uclimit: 'max', |
uclimit: 'max', |
||
ucprop: 'ids|title' |
ucprop: 'ids|title|timestamp|size|ns|parentid' |
||
}).done((data) => { |
|||
const allContributions = data.query.usercontribs; |
|||
}).done(function (data) { |
|||
const filtered = this.applyFilters(allContributions); |
|||
const $tbody = $('#filtered-edits-table tbody'); |
|||
$tbody.empty(); |
|||
if ( |
if (filtered.length === 0) { |
||
$tbody.append('<tr><td colspan="6" style="text-align:center; padding:5px;">No edits match the selected filters.</td></tr>'); |
|||
} else { |
|||
filtered.forEach(contrib => { |
|||
const row = ` |
|||
<tr> |
|||
<td style="padding: 5px; border: 1px solid #ccc;">${contrib.revid}</td> |
|||
<td style="padding: 5px; border: 1px solid #ccc;">${contrib.title}</td> |
|||
<td style="padding: 5px; border: 1px solid #ccc;">${new Date(contrib.timestamp).toLocaleString()}</td> |
|||
<td style="padding: 5px; border: 1px solid #ccc;">${this.getNamespaceName(contrib.ns)}</td> |
|||
<td style="padding: 5px; border: 1px solid #ccc;">${contrib.size || 0}</td> |
|||
<td style="padding: 5px; border: 1px solid #ccc; text-align: center;"> |
|||
<input type="checkbox" class="filtered-rollback-checkbox" data-revid="${contrib.revid}" data-parentid="${contrib.parentid}" data-title="${contrib.title}"> |
|||
</td> |
|||
</tr> |
|||
`; |
|||
$tbody.append(row); |
|||
}); |
|||
} |
} |
||
$('#filtered-edits-container').css("display", "block").slideDown(); |
|||
}); |
|||
}, |
|||
confirmAction: function(message, count) { |
|||
var rollbackPromises = contributions.map(function (contrib) { |
|||
return new Promise((resolve) => { |
|||
var formattedSummary = `Reverted edit by [[User:${userName}|${userName}]] using [[Gadget:MassRollback|gadget]]` + (summary ? `: ${summary}` : ''); |
|||
const $modal = $(` |
|||
<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; |
|||
background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 9999;"> |
|||
<div style="background: #fff; padding: 20px; border-radius: 4px; max-width: 400px; width: 90%;"> |
|||
undoafter: contrib.parentid, |
|||
<h4 style="margin-top:0;">Confirm Rollback</h4> |
|||
<p>${message}</p> |
|||
<p>Number of edits: ${count}</p> |
|||
<div style="text-align: right; margin-top: 15px;"> |
|||
<button id="confirm-action" style="padding: 5px 10px; background-color: #007bff; color: #fff; border: none; border-radius: 3px; cursor: pointer; margin-right: 5px;">Confirm</button> |
|||
<button id="cancel-action" style="padding: 5px 10px; background-color: #aaa; color: #fff; border: none; border-radius: 3px; cursor: pointer;">Cancel</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
`); |
|||
$('body').append($modal); |
|||
$modal.find('#confirm-action').on('click', function() { |
|||
$modal.remove(); |
|||
resolve(true); |
|||
}); |
}); |
||
$modal.find('#cancel-action').on('click', function() { |
|||
$modal.remove(); |
|||
resolve(false); |
|||
location.reload(); |
|||
}).catch(function (error) { |
|||
console.error('An error occured when reverting:', error); |
|||
alert('An error occured while reverting.'); |
|||
}); |
}); |
||
}).fail(function (error) { |
|||
console.error('Error with downloading contributions:', error); |
|||
alert('Error with downloading contributions.'); |
|||
}); |
}); |
||
} |
}, |
||
// Nowa wersja: grupujemy edycje wg tytułu i dla każdej grupy wykrywamy ciąg kolejnych edycji. |
|||
$('#cancel-rollback').click(function () { |
|||
performRollback: function(contributions) { |
|||
$('#rollback-box').hide(); |
|||
const userName = mw.config.get('wgRelevantUserName'); |
|||
}); |
|||
const reason = $('#rollback-reason').val() === 'other' |
|||
? $('#custom-reason').val() |
|||
: $('#rollback-reason option:selected').text(); |
|||
const summary = `Reverted edit(s) by [[User:${userName}|${userName}]]: ${reason}`; |
|||
const api = new mw.Api(); |
|||
// |
// Grupujemy edycje wg tytułu strony |
||
const groups = {}; |
|||
$('li[data-mw-revid]').each(function () { |
|||
contributions.forEach(contrib => { |
|||
if (!groups[contrib.title]) { |
|||
groups[contrib.title] = []; |
|||
} |
|||
groups[contrib.title].push(contrib); |
|||
}); |
|||
const promises = []; |
|||
var $checkbox = $('<input type="checkbox" class="rollback-checkbox" style="margin-right: 5px;">') |
|||
// Dla każdej strony – sortuj wg daty malejąco i wykryj ciąg edycji |
|||
.data('revid', revid) |
|||
for (let title in groups) { |
|||
. |
let group = groups[title].sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)); |
||
let latest = group[0]; |
|||
// Rozpoczynamy ciąg od najnowszej edycji |
|||
let chain = [latest]; |
|||
let currentParent = latest.parentid; |
|||
// Przeglądamy kolejne edycje dla tego samego tytułu |
|||
for (let i = 1; i < group.length; i++) { |
|||
const contrib = group[i]; |
|||
// Jeżeli kolejna edycja jest dokładnie poprzednikiem poprzednio znalezionego elementu, dodajemy do ciągu |
|||
if (parseInt(contrib.revid, 10) === parseInt(currentParent, 10)) { |
|||
chain.push(contrib); |
|||
currentParent = contrib.parentid; |
|||
} else { |
|||
break; |
|||
} |
|||
} |
|||
// Wykonaj rollback tylko, jeśli mamy przynajmniej jedną edycję (zawsze prawda) |
|||
promises.push(api.postWithToken('csrf', { |
|||
action: 'edit', |
|||
undo: latest.revid, |
|||
undoafter: currentParent, |
|||
title: title, |
|||
summary: summary |
|||
})); |
|||
} |
|||
return Promise.all(promises); |
|||
}, |
|||
rollbackSelected: async function() { |
|||
// Pobieramy zaznaczone edycje z listy kontrybucji |
|||
const selected = $('.rollback-checkbox:checked').map(function() { |
|||
return { |
|||
title: $(this).data('title'), |
|||
revid: $(this).data('revid'), |
|||
parentid: $(this).data('parentid'), |
|||
// Wartość timestamp może nie być dostępna w tym widoku – można opcjonalnie dodać dodatkowy pobór danych |
|||
timestamp: $(this).closest('li').find('.mw-contributions-timestamp').text() || new Date().toISOString() |
|||
}; |
|||
}).get(); |
|||
if (selected.length === 0) { |
|||
mw.notify('No edits selected.', { type: 'warn' }); |
|||
return; |
|||
} |
|||
const confirmed = await this.confirmAction('Are you sure you want to rollback the selected edits?', selected.length); |
|||
if (!confirmed) return; |
|||
try { |
|||
await this.performRollback(selected); |
|||
mw.notify(`Successfully rolled back ${selected.length} edits.`, { type: 'success' }); |
|||
location.reload(); |
|||
} catch (error) { |
|||
mw.notify('Error during rollback.', { type: 'error' }); |
|||
console.error(error); |
|||
} |
|||
}, |
|||
rollbackSelectedFromFiltered: async function() { |
|||
const selected = $('.filtered-rollback-checkbox:checked').map(function() { |
|||
return { |
|||
title: $(this).data('title'), |
|||
revid: $(this).data('revid'), |
|||
parentid: $(this).data('parentid'), |
|||
timestamp: new Date().toISOString() // Jeśli API już zwraca timestamp, można go tutaj użyć |
|||
}; |
|||
}).get(); |
|||
if (selected.length === 0) { |
|||
mw.notify('No filtered edits selected.', { type: 'warn' }); |
|||
return; |
|||
} |
|||
const confirmed = await this.confirmAction('Are you sure you want to rollback the selected filtered edits?', selected.length); |
|||
if (!confirmed) return; |
|||
try { |
|||
await this.performRollback(selected); |
|||
mw.notify(`Successfully rolled back ${selected.length} filtered edits.`, { type: 'success' }); |
|||
location.reload(); |
|||
} catch (error) { |
|||
mw.notify('Error during rollback.', { type: 'error' }); |
|||
console.error(error); |
|||
} |
|||
}, |
|||
rollbackAll: async function() { |
|||
const userName = mw.config.get('wgRelevantUserName'); |
|||
const api = new mw.Api(); |
|||
try { |
|||
// Dodajemy "timestamp" by móc grupować edycje |
|||
const data = await api.get({ |
|||
action: 'query', |
|||
list: 'usercontribs', |
|||
ucuser: userName, |
|||
uclimit: 'max', |
|||
ucprop: 'ids|title|parentid|timestamp' |
|||
}); |
|||
const contributions = data.query.usercontribs; |
|||
if (contributions.length === 0) { |
|||
mw.notify('No edits to rollback.', { type: 'warn' }); |
|||
return; |
|||
} |
|||
const confirmed = await this.confirmAction('Are you sure you want to rollback ALL edits?', contributions.length); |
|||
if (!confirmed) return; |
|||
await this.performRollback(contributions); |
|||
mw.notify(`Successfully rolled back all ${contributions.length} edits.`, { type: 'success' }); |
|||
location.reload(); |
|||
} catch (error) { |
|||
mw.notify('Error during rollback.', { type: 'error' }); |
|||
console.error(error); |
|||
} |
|||
} |
|||
}; |
|||
$(document).ready(function() { |
|||
$this.prepend($checkbox); |
|||
MassRollback.init(); |
|||
} |
}); |
||
})(jQuery, mediaWiki); |
|||
}); |
|||