Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

User:Justarandomamerican/globalUserLock.js: Difference between revisions

From WikiOasis Meta
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 API_ENDPOINT = mw.config.get( 'wgScriptPath' ) + '/api.php';
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>suppressed</code> flag — requires suppressor right)</label>
<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 getCSRFToken() {
return $.ajax( {
url:  API_ENDPOINT,
data: { action: 'query', meta: 'tokens', type: 'csrf', format: 'json' }
} ).then( function ( data ) {
return data.query.tokens.csrftoken;
} );
}
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; }


$.ajax( { url: API_ENDPOINT, data: params } )
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 ( xhr ) {
.fail( function ( code ) {
deferred.reject( 'API request failed: ' + xhr.statusText );
deferred.reject( 'API request failed: ' + code );
} );
} );
}
}
Line 365: Line 358:
}
}


function lockAccount( username, token, reason, hide, suppress ) {
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,
token:  token,
format: 'json'
format: 'json'
};
};
Line 381: Line 373:
}
}


return $.ajax( { url: API_ENDPOINT, method: 'POST', data: payload } );
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; }
if ( !confirm( 'You are about to globally lock ' + selected.length + ' account(s).\n\nReason: ' + reason + '\n' + ( hide ? 'Hide username: YES\n' : '' ) + ( suppress ? 'Suppress username: YES\n' : '' ) + '\nThis action will be logged. Proceed?' ) ) { 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( 'Fetching CSRF token…', 'info' );
setStatus( 'Locking ' + selected.length + ' account(s)…', 'info' );
let done = 0, locked = 0, skipped = 0, failed = 0;


getCSRFToken()
function processNext( i ) {
.then( function ( token ) {
if ( i >= selected.length ) {
setStatus( 'Locking ' + selected.length + ' account(s)', 'info' );
setProgress( done, selected.length );
let done = 0, locked = 0, skipped = 0, failed = 0;
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;
}


function processNext( i ) {
const username = selected[ i ];
if ( i >= selected.length ) {
const wasLocked = $( 'tr[data-user="' + $.escapeSelector( username ) + '"] td:nth-child(3)' ).text().trim() === 'Locked';
setProgress( done, selected.length );
 
setStatus( 'Done. <strong>' + locked + '</strong> locked, <strong>' + skipped + '</strong> skipped, <strong>' + failed + '</strong> failed.', failed > 0 ? 'warning' : 'success' );
if ( skipLocked && wasLocked ) {
$( '#gul-btn-search' ).prop( 'disabled', false );
skipped++; done++;
$( '#gul-lock-summary' ).remove();
setBadge( username, 'skipped', 'Skipped' );
$( '#gul-results-wrap' ).append( $( '<div id="gul-lock-summary">' ).addClass( 'gul-summary' ).html( '<strong>Summary:</strong> ' + locked + ' locked · ' + skipped + ' skipped · ' + failed + ' failed' ) );
setProgress( done, selected.length );
return;
processNext( i + 1 );
}
return;
}


const username = selected[ i ];
setBadge( username, 'pending', 'Locking…' );
const wasLocked = $( 'tr[data-user="' + $.escapeSelector( username ) + '"] td:nth-child(3)' ).text().trim() === 'Locked';


if ( skipLocked && wasLocked ) {
lockAccount( username, reason, hide, suppress )
skipped++; done++;
.done( function ( result ) {
setBadge( username, 'skipped', 'Skipped' );
if ( result.error ) {
setProgress( done, selected.length );
failed++;
processNext( i + 1 );
setBadge( username, 'failed', 'Error: ' + mw.html.escape( result.error.info || result.error.code ) );
return;
} else {
locked++;
setBadge( username, 'locked', 'Locked ✓' );
}
}
 
} )
setBadge( username, 'pending', 'Locking…' );
.fail( function ( code, result ) {  
 
failed++;  
lockAccount( username, token, reason, hide, suppress )
const errMsg = (result && result.error && result.error.info) ? result.error.info : code;
.done( function ( data ) {
setBadge( username, 'failed', 'Error: ' + mw.html.escape( errMsg ) );  
if ( data.error ) {
} )
failed++;
.always( function () {
setBadge( username, 'failed', 'Error: ' + mw.html.escape( data.error.info || data.error.code ) );
done++;
} else {
setProgress( done, selected.length );
locked++;
setStatus( 'Processing ' + done + ' / ' + selected.length + '…', 'info' );
setBadge( username, 'locked', 'Locked ✓' );
setTimeout( function () { processNext( i + 1 ); }, 300 );
}
} );
} )
}
.fail( function () { failed++; setBadge( username, 'failed', 'Network error' ); } )
processNext( 0 );
.always( function () {
done++;
setProgress( done, selected.length );
setStatus( 'Processing ' + done + ' / ' + selected.length + '…', 'info' );
setTimeout( function () { processNext( i + 1 ); }, 300 );
} );
}
processNext( 0 );
} )
.fail( function () {
setStatus( 'Failed to obtain token. Are you logged in with the centralauth-lock right?', 'error' );
$( '#gul-btn-lock, #gul-btn-search' ).prop( 'disabled', false );
} );
}
}


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();
$( '#gul-btn-lock' ).prop( 'disabled', true );
}
}


Cookies help us deliver our services. By using our services, you agree to our use of cookies.