User:Justarandomamerican/globalUserLock.js: Difference between revisions
From WikiOasis Meta
More actions
maybe? |
No edit summary |
||
| (4 intermediate revisions by the same user not shown) | |||
| Line 14: | Line 14: | ||
const TOOL_TITLE = 'Global User Lock'; | const TOOL_TITLE = 'Global User Lock'; | ||
const PORTLET_ID = 'ca-global-user-lock'; | const PORTLET_ID = 'ca-global-user-lock'; | ||
const MAX_ACCOUNTS = 200; | const MAX_ACCOUNTS = 200; | ||
// ── Load Dependencies First ──────────────────────────────────────────────── | // ── Load Dependencies First ──────────────────────────────────────────────── | ||
$.when( | $.when( | ||
mw.loader.using( [ 'mediawiki.util', 'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows' ] ), | mw.loader.using( [ 'mediawiki.util', 'mediawiki.api', 'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows' ] ), | ||
$.ready | $.ready | ||
).then( function () { | ).then( function () { | ||
// Initialize the MediaWiki API helper | |||
const api = new mw.Api(); | |||
// ── OOUI Dialog Definition ───────────────────────────────────────────── | // ── OOUI Dialog Definition ───────────────────────────────────────────── | ||
| Line 235: | Line 237: | ||
<div class="gul-checkbox-row"> | <div class="gul-checkbox-row"> | ||
<input type="checkbox" id="gul-hide" /> | <input type="checkbox" id="gul-hide" /> | ||
<label for="gul-hide" style="margin:0;">Hide username (<code>hidden</code> flag — requires suppressor right)</label> | <label for="gul-hide" style="margin:0;">Hide username (<code>hidden = 1</code> flag — requires suppressor right)</label> | ||
</div> | </div> | ||
<div class="gul-checkbox-row"> | <div class="gul-checkbox-row"> | ||
<input type="checkbox" id="gul-suppress" /> | <input type="checkbox" id="gul-suppress" /> | ||
<label for="gul-suppress" style="margin:0;">Suppress username (<code> | <label for="gul-suppress" style="margin:0;">Suppress username (<code>hidden = 2</code> flag — requires suppressor right)</label> | ||
</div> | </div> | ||
<div class="gul-checkbox-row"> | <div class="gul-checkbox-row"> | ||
| Line 298: | Line 300: | ||
// ── API helpers ──────────────────────────────────────────────────────── | // ── API helpers ──────────────────────────────────────────────────────── | ||
function fetchMatchingUsers( query, fromUser ) { | function fetchMatchingUsers( query, fromUser ) { | ||
const deferred = $.Deferred(); | const deferred = $.Deferred(); | ||
| Line 332: | Line 325: | ||
if ( continueFrom ) { params.agufrom = continueFrom; } | if ( continueFrom ) { params.agufrom = continueFrom; } | ||
api.get( params ) | |||
.done( function ( data ) { | .done( function ( data ) { | ||
const users = ( data.query || {} ).globalallusers || []; | const users = ( data.query || {} ).globalallusers || []; | ||
| Line 356: | Line 349: | ||
} | } | ||
} ) | } ) | ||
.fail( function ( | .fail( function ( code ) { | ||
deferred.reject( 'API request failed: ' + | deferred.reject( 'API request failed: ' + code ); | ||
} ); | } ); | ||
} | } | ||
| Line 365: | Line 358: | ||
} | } | ||
function lockAccount( username | function lockAccount( username, reason, hide, suppress ) { | ||
const payload = { | const payload = { | ||
action: 'setglobalaccountstatus', | action: 'setglobalaccountstatus', | ||
| Line 371: | Line 364: | ||
locked: 'lock', | locked: 'lock', | ||
reason: reason, | reason: reason, | ||
format: 'json' | format: 'json' | ||
}; | }; | ||
| Line 381: | Line 373: | ||
} | } | ||
return | return api.postWithToken( 'setglobalaccountstatus', payload ); | ||
} | } | ||
| Line 458: | Line 450: | ||
const skipLocked = $( '#gul-skip-locked' ).prop( 'checked' ); | const skipLocked = $( '#gul-skip-locked' ).prop( 'checked' ); | ||
if ( !reason ) { setStatus( 'A lock reason is required.', 'error' ); return; } | if ( !reason ) { | ||
setStatus( '🛑 A lock reason is required before locking.', 'error' ); | |||
$( '#gul-reason' ).trigger( 'focus' ); | |||
return; | |||
} | |||
const selected = []; | const selected = []; | ||
| Line 464: | Line 460: | ||
if ( selected.length === 0 ) { setStatus( 'No accounts selected.', 'warning' ); return; } | if ( selected.length === 0 ) { setStatus( 'No accounts selected.', 'warning' ); return; } | ||
const $btn = $( '#gul-btn-lock' ); | |||
// ── INLINE CONFIRMATION ── | |||
// First click: Change button state and wait for second click | |||
if ( !$btn.data( 'ready-to-lock' ) ) { | |||
$btn.data( 'ready-to-lock', true ) | |||
.text( '⚠️ Confirm Lock (' + selected.length + ')' ) | |||
.css( { 'background-color': '#5a1010', 'border-color': '#3a0000' } ); | |||
setStatus( '⚠️ <strong>WARNING:</strong> You are about to lock ' + selected.length + ' account(s). Click the button again to proceed.', 'warning' ); | |||
// Reset if they don't click within 5 seconds | |||
setTimeout( function () { | |||
if ( $btn.data( 'ready-to-lock' ) ) { | |||
$btn.data( 'ready-to-lock', false ) | |||
.text( '🔒 Lock Selected' ) | |||
.css( { 'background-color': '', 'border-color': '' } ); | |||
clearStatus(); | |||
} | |||
}, 5000 ); | |||
return; | |||
} | |||
// Second click: Reset button visuals and proceed | |||
$btn.data( 'ready-to-lock', false ).css( { 'background-color': '', 'border-color': '' } ); | |||
$( '#gul-btn-lock, #gul-btn-search' ).prop( 'disabled', true ); | $( '#gul-btn-lock, #gul-btn-search' ).prop( 'disabled', true ); | ||
$( '#gul-progress' ).addClass( 'visible' ); | $( '#gul-progress' ).addClass( 'visible' ); | ||
setProgress( 0, selected.length ); | setProgress( 0, selected.length ); | ||
setStatus( ' | |||
setStatus( 'Locking ' + selected.length + ' account(s)…', 'info' ); | |||
let done = 0, locked = 0, skipped = 0, failed = 0; | |||
function processNext( i ) { | |||
. | if ( i >= selected.length ) { | ||
setStatus( ' | setProgress( done, selected.length ); | ||
setStatus( 'Done. <strong>' + locked + '</strong> locked, <strong>' + skipped + '</strong> skipped, <strong>' + failed + '</strong> failed.', failed > 0 ? 'warning' : 'success' ); | |||
$( '#gul-btn-search' ).prop( 'disabled', false ); | |||
$( '#gul-lock-summary' ).remove(); | |||
$( '#gul-results-wrap' ).append( $( '<div id="gul-lock-summary">' ).addClass( 'gul-summary' ).html( '<strong>Summary:</strong> ' + locked + ' locked · ' + skipped + ' skipped · ' + failed + ' failed' ) ); | |||
return; | |||
} | |||
const username = selected[ i ]; | |||
const wasLocked = $( 'tr[data-user="' + $.escapeSelector( username ) + '"] td:nth-child(3)' ).text().trim() === 'Locked'; | |||
if ( skipLocked && wasLocked ) { | |||
skipped++; done++; | |||
setBadge( username, 'skipped', 'Skipped' ); | |||
setProgress( done, selected.length ); | |||
processNext( i + 1 ); | |||
return; | |||
} | |||
setBadge( username, 'pending', 'Locking…' ); | |||
if ( | lockAccount( username, reason, hide, suppress ) | ||
.done( function ( result ) { | |||
setBadge( username, ' | if ( result.error ) { | ||
failed++; | |||
setBadge( username, 'failed', 'Error: ' + mw.html.escape( result.error.info || result.error.code ) ); | |||
} else { | |||
locked++; | |||
setBadge( username, 'locked', 'Locked ✓' ); | |||
} | } | ||
} ) | |||
.fail( function ( code, result ) { | |||
failed++; | |||
const errMsg = (result && result.error && result.error.info) ? result.error.info : code; | |||
setBadge( username, 'failed', 'Error: ' + mw.html.escape( errMsg ) ); | |||
} ) | |||
.always( function () { | |||
done++; | |||
setProgress( done, selected.length ); | |||
setStatus( 'Processing ' + done + ' / ' + selected.length + '…', 'info' ); | |||
setTimeout( function () { processNext( i + 1 ); }, 300 ); | |||
} ); | |||
} | |||
processNext( 0 ); | |||
} | } | ||
function onClear() { | function onClear() { | ||
// Reset the confirmation button state if clear is clicked | |||
const $btn = $( '#gul-btn-lock' ); | |||
$btn.data( 'ready-to-lock', false ) | |||
.text( '🔒 Lock Selected' ) | |||
.css( { 'background-color': '', 'border-color': '' } ) | |||
.prop( 'disabled', true ); | |||
$( '#gul-query, #gul-from, #gul-reason' ).val( '' ); | $( '#gul-query, #gul-from, #gul-reason' ).val( '' ); | ||
$( '#gul-hide, #gul-suppress' ).prop( 'checked', false ); | $( '#gul-hide, #gul-suppress' ).prop( 'checked', false ); | ||
| Line 533: | Line 557: | ||
$( '#gul-progress-bar' ).css( 'width', '0%' ); | $( '#gul-progress-bar' ).css( 'width', '0%' ); | ||
clearStatus(); | clearStatus(); | ||
} | } | ||