MediaWiki:Gadget-Massblock.js

Versione del 16 gen 2019 alle 20:42 di Daimona Eaytoy (discussione | contributi) (Ehhhh per oggi può bastare)

Questa pagina definisce alcuni parametri di aspetto e comportamento generale di tutte le pagine. Per personalizzarli vedi Aiuto:Stile utente.


Nota: dopo aver salvato è necessario pulire la cache del proprio browser per vedere i cambiamenti (per le pagine globali è comunque necessario attendere qualche minuto). Per Mozilla / Firefox / Safari: fare clic su Ricarica tenendo premuto il tasto delle maiuscole, oppure premere Ctrl-F5 o Ctrl-R (Command-R su Mac); per Chrome: premere Ctrl-Shift-R (Command-Shift-R su un Mac); per Konqueror: premere il pulsante Ricarica o il tasto F5; per Opera può essere necessario svuotare completamente la cache dal menù Strumenti → Preferenze; per Internet Explorer: mantenere premuto il tasto Ctrl mentre si preme il pulsante Aggiorna o premere Ctrl-F5.

/* jshint maxerr: 10000 */
/* global mediaWiki, jQuery, OO, window, document */
/**
 * Tool for mass-blocking a list of IPs/users.
 * Adapted from [[:en:User:Timotheus Canens/massblock.js]].
 * Go to [[Special:Massblock]] to use it.
 * If you want to add localized messages, see instructions on [[MediaWiki:Gadget-Massblock]]
 *
 * @author (i.e. blame him) [[:it:User:Daimona Eaytoy]]
 * @todo This should handle the async loop more nicely, and properly use Promise
 *   rejections.
 */
// These are default english messages.
var defaultMsg = {
	'toolbar-text': 'Massblock',
	'document-title': 'Mass-blocking tool - Wikipedia, the free encyclopedia',
	'page-title': 'Mass-blocking tool',
	'abuse-disclaimer': 'If you abuse this tool, it\'s your fault, not mine.',
	'basic-data': 'Basic data',
	'blockusers': 'Users to block (one on each line, please):',
	'talkmsg': 'Replace talk page with, or text to add if the expiry is not infinite (leave blank to leave no message):',
	'upmsg': 'Replace user page text with (leave blank for no change):',
	'block-options-label': 'Block options',
	'further-options-label': 'Further options',
	'common-reasons': 'Common reasons:',
	'other-reason': 'Other reason',
	'extra-reason': 'Other/additional reason:',
	'exptime': 'Expiration time, in english (blank for indefinite):',
	'summary-default': 'Blocked user.',
	'talksummary': 'Edit summary for talk page edit:',
	'talkprotect': 'Protect the talk (sysop level, infinite):',
	'upsummary': 'Edit summary for user page edit:',
	'upprotect': 'Protect the user page (sysop level, infinite):',
	'protect-reason-label': 'Reason for protection:',
	'protect-reason-default': 'Blocked user.',
	'anononly': 'Block anonymous users only (IPs only):',
	'autoblock': 'Enable autoblock (accounts only):',
	'nocreate': 'Block account creation:',
	'noemail': 'Block email:',
	'notalk': 'Remove talk page access:',
	'override': 'Override existing blocks:',
	'submit-text': 'Block',
	'result-alert': 'Blocked $1 users. Edited $2 talk pages and $3 user pages. Protected $4 talk pages and $5 user pages.',
	'failed-actions': 'Failed actions with errors:',
	'failure-help': 'help'
};

// This will hold the localized messages and must be defined before importing the
// script (see [[MediaWiki:Gadget-Massblock.js]])
var localMsg;

// OOUI element
var submitBtn,
	// Like wgBlockAllowsUTEdit
	blockAllowsTalkEdit;

/**
 * Main form processing routine, called on form submit
 */
function doMassBlock() {
	var users = $( "#wpMassBlockUsers textarea" ).val().split( "\n" );

	// First trim everything
	users = users.map( function( s ) {
		return s.trim();
	} );
	// Then remove blanks and duplicates
	users = users.filter( function( el, index, me ) {
		return el !== '' && me.indexOf( el ) === index;
	} );
	if ( users.length === 0 ) {
		// Easy
		return;
	}
	submitBtn.setDisabled( true );

	var wpMassBlockReasons = $( "#wpMassBlockReasons select :selected" ).val().trim(),
		wpMassBlockReason = $( "#wpMassBlockReason input" ).val().trim(),
		blocked = 0,
		talkpageedited = 0,
		userpageedited = 0,
		talkpageprotected = 0,
		userpageprotected = 0,
		errors = {};
	var wpMassBlockAnononly = $( "#wpMassBlockAnononly input" ).prop( 'checked' ),
		wpMassBlockNocreate = $( "#wpMassBlockNocreate input" ).prop( 'checked' ),
		wpMassBlockEmail = $( "#wpMassBlockEmail input" ).prop( 'checked' ),
		wpMassBlockAutoblock = $( "#wpMassBlockAutoblock input" ).prop( 'checked' ),
		wpMassBlockTalkpage = blockAllowsTalkEdit ? $( "#wpMassBlockTalkpage input" ).prop( 'checked' ) : false,
		wpMassBlockReblock = $( "#wpMassBlockReblock input" ).prop( 'checked' );
	var wpMassBlockMessage = $( "#wpMassBlockMessage textarea" ).val().trim(),
		wpMassBlockTag = $( "#wpMassBlockTag textarea" ).val().trim(),
		wpMassBlockExpiry = $( "#wpMassBlockExpiry input" ).val().trim();
	var wpMassBlockSummaryTalk = $( "#wpMassBlockSummaryTalk input" ).val().trim(),
		wpMassBlockSummaryUser = $( "#wpMassBlockSummaryUser input" ).val().trim(),
		wpMassBlockProtectTalk = $( "#wpMassBlockProtectTalk input" ).prop( 'checked' ),
		wpMassBlockProtectUser = $( "#wpMassBlockProtectUser input" ).prop( 'checked' );

	/**
	 * Process an error in any part of the process
	 *
	 * @param {string} e The error code
	 * @param {string} user The user we're processing
	 * @param {string} action The action we're doing. This is of the form
	 *   {$actionname}-{$page}, where $action name is the name of the API module
	 *   being used (e.g. 'block' or 'edit') and $page is either 'talk' or 'user',
	 *   representing the target of the action. The only special case is 'block',
	 *   which has no hyphen and no page.
	 */
	var errorHandler = function( e, user, action ) {
		var obj = {};
		obj[ action ] = e;
		if ( !errors[ user ] ) {
			errors[ user ] = [ obj ];
		} else {
			errors[ user ].push( obj );
		}


	};

	var isInfty = isInfinity( wpMassBlockExpiry );
	// Several actions can only be executed if the block is infinite
	wpMassBlockProtectTalk = wpMassBlockProtectTalk && isInfty;
	wpMassBlockProtectUser = wpMassBlockProtectUser && isInfty;
	wpMassBlockTag = isInfty ? wpMassBlockTag : '';


	/**
	 * Perform a single block via API
	 *
	 * @param {string} user The user to block
	 * @return {Promise}
	 */
	var doBlock = function( user ) {
		return new mw.Api().postWithToken( "csrf", {
			action: 'block',
			allowusertalk: wpMassBlockTalkpage,
			autoblock: wpMassBlockAutoblock,
			nocreate: wpMassBlockNocreate,
			expiry: wpMassBlockExpiry === "" ? "indefinite" : wpMassBlockExpiry,
			anononly: wpMassBlockAnononly,
			noemail: wpMassBlockEmail,
			reblock: wpMassBlockReblock,
			reason: wpMassBlockReasons === "other" ? wpMassBlockReason : wpMassBlockReasons + ( wpMassBlockReason ? ": " + wpMassBlockReason : "" ),
			user: user
		} );
	};

	/**
	 * The post-block handler. Performs edits and protections.
	 *
	 * @param {Object} response The response of the block API call
	 * @return {Promise} A promise which is resolved after all actions are done
	 *   for all pages. This will never be rejected.
	 */
	var successHandler = function( response ) {
		var user = response.block.user,
			talkPage = 'User talk:' + user,
			userPage = 'User:' + user,
			// @var {Promise} These track the state for all actions (edit and protect)
			//  on every page. They are resolved as soon as a page is processed, with or
			//  without a failure. This way the final $.when will resolve after all promises
			//  have been processed, and not after the first rejection.
			talkDone = $.Deferred(),
			userDone = $.Deferred();

		blocked++;

		if ( wpMassBlockMessage !== "" ) {
			doEditPage( talkPage, wpMassBlockMessage, wpMassBlockSummaryTalk, !isInfty )
				.done( function() {
					talkpageedited++;
					if ( wpMassBlockProtectTalk ) {
						doProtectPage( talkPage, 'edit=sysop|move=sysop' )
							.done( function() {
								talkpageprotected++;
							} )
							.fail( function( e ) {
								errorHandler( e, user, "protect-talk" );
							} )
							.always( function() {
								talkDone.resolve();
							} );
					} else {
						talkDone.resolve();
					}
				} )
				.fail( function( e ) {
					errorHandler( e, user, "edit-talk" );
					talkDone.resolve();
				} );
		} else if ( wpMassBlockProtectTalk ) {
			new mw.Api().get( {
					action: 'query',
					titles: talkPage
				} )
				.then( function( data ) {
					var exists = Object.keys( data.query.pages )[ 0 ] !== -1,
						prType = exists ? 'edit=sysop|move=sysop' : 'create=sysop';
					doProtectPage( talkPage, prType )
						.done( function() {
							talkpageprotected++;
						} )
						.fail( function( e ) {
							errorHandler( e, user, "protect-talk" );
						} )
						.always( function() {
							talkDone.resolve();
						} );
				} )
				.fail( function( e ) {
					errorHandler( e, user, "query-talk" );
					talkDone.resolve();
				} );
		} else {
			talkDone.resolve();
		}

		if ( wpMassBlockTag !== "" ) {
			doEditPage( userPage, wpMassBlockTag, wpMassBlockSummaryUser )
				.done( function() {
					userpageedited++;
					if ( wpMassBlockProtectUser ) {
						doProtectPage( userPage, 'edit=sysop|move=sysop' )
							.done( function() {
								userpageprotected++;
							} )
							.fail( function( e ) {
								errorHandler( e, user, "protect-user" );
							} )
							.always( function() {
								userDone.resolve();
							} );
					} else {
						userDone.resolve();
					}
				} )
				.fail( function( e ) {
					errorHandler( e, user, "edit-user" );
					userDone.resolve();
				} );
		} else if ( wpMassBlockProtectUser ) {
			new mw.Api().get( {
					action: 'query',
					titles: userPage
				} )
				.then( function( data ) {
					var exists = Object.keys( data.query.pages )[ 0 ] !== -1,
						prType = exists ? 'edit=sysop|move=sysop' : 'create=sysop';
					doProtectPage( userPage, prType )
						.done( function() {
							userpageprotected++;
						} )
						.fail( function( e ) {
							errorHandler( e, user, "protect-user" );
						} )
						.always( function() {
							userDone.resolve();
						} );
				} )
				.fail( function( e ) {
					errorHandler( e, user, "query-user" );
					userDone.resolve();
				} );
		} else {
			userDone.resolve();
		}

		return $.when( talkDone, userDone );
	};

	// Array of Promises, one for each user. Each one is resolved after the user
	//  is processed, even in case of failure.
	var deferreds = [];

	users.forEach( function( user ) {
		deferreds.push(
			doBlock( user )
			.then(
				function( data ) {
					return successHandler( data );
				},
				function( e ) {
					errorHandler( e, user, "block" );
					// Return so that this counts as resolved and won't leave
					// other promises unresolved.
					return $.when();
				}
			)
		);
	} );

	$.when.apply( $, deferreds ).always( function() {
		doPostBlockActions( errors );
		OO.ui.alert(
			msg( 'result-alert' ).replace( '$1', blocked ).replace( '$2', talkpageedited ).replace( '$3', userpageedited )
			.replace( '$4', talkpageprotected ).replace( '$5', userpageprotected )
		);
	} );
}

/**
 * Executed after all users have been processed.
 *
 * @param {Object} errors The errors collected on the way. The structure of
 *   this object is { username: [ { action: errorcode }, ... ], ... }
 */
function doPostBlockActions( errors ) {
	if ( Object.keys( errors ).length > 0 ) {
		var linkedList = "";

		var errorsToCodes = function( el ) {
			var key = Object.keys( el )[ 0 ],
				action = key.split( "-" )[ 0 ],
				link = '//mediawiki.org/wiki/API:' + action + '#Possible_errors';
			return key + ': <code style="color:red">' +
				el[ key ] + '</code> (<a href="' + link + '">' +
				msg( 'failure-help' ) + '</a>)';
		};

		for ( var user in errors ) {
			var codes = errors[ user ].map( errorsToCodes );
			linkedList += "<li><a href=\"" + mw.config.get( 'wgScript' ) + "?title=Special:Contributions/" + encodeURIComponent( user ) + "\">" + user + "</a>: " +
				codes.join( "; " ) + "</li>";
		}
		$( "#wpMassBlockFailedContainer" ).html(
			'<h3>' + msg( 'failed-actions' ) + '</h3><ul>' + linkedList + '</ul><hr />'
		);
	}
}

/**
 * Edit the given page.
 *
 * @param {string} title The title of the page
 * @param {string} text The text to add
 * @param {string} summary The summary to use
 * @param {bool} append Whether to append the text or replace the whole page content
 * @return {Promise}
 */
function doEditPage( title, text, summary, append ) {
	var appendText = append || false,
		params = {
			action: 'edit',
			title: title,
			summary: summary,
			watchlist: 'nochange'
		};
	if ( appendText ) {
		params.appendtext = text;
	} else {
		params.text = text;
	}
	return new mw.Api().postWithEditToken( params );
}

/**
 * Protect the given page
 *
 * @param {string} title The page to protect
 * @param {string} protections As accepted by the Protect API module
 * @return {Promise}
 */
function doProtectPage( title, protections ) {
	return new mw.Api().postWithToken( 'csrf', {
		action: 'protect',
		title: title,
		protections: protections,
		reason: $( "#wpMassBlockProtectReason input" ).val().trim(),
		watchlist: 'nochange'
	} );
}

/**
 * Get a localised messages, or the default one as fallback.
 *
 * @param {string} msg The key of the message to get
 * @return {string}
 */
function msg( msg ) {
	return localMsg[ msg ] || defaultMsg[ msg ];
}

/**
 * Build the form
 */
function massblockform() {
	var reasons = mw.msg( 'Ipbreason-dropdown' ).split( '**' ),
		// OOUI elements
		talkTextField, userTextField, talkProtectCb, talkSummaryField,
		userSummaryField, userProtectCb, protectReasonField;

	// Ideally this would use $wgBlockAllowsUTEdit, but it's not available.
	blockAllowsTalkEdit = window.___location.href.indexOf( 'it.wikipedia' ) === -1;

	$( "h1" ).first().html( msg( 'page-title' ) );
	document.title = msg( 'document-title' );

	var form = new OO.ui.FormLayout( {
		id: 'wpMassBlock'
	} );

	talkTextField = new OO.ui.MultilineTextInputWidget( {
		rows: 10
	} );

	userTextField = new OO.ui.MultilineTextInputWidget( {
		rows: 10
	} );

	var basicFieldset = new OO.ui.FieldsetLayout( {
		label: msg( 'basic-data' ),
		items: [
			new OO.ui.FieldLayout(
				new OO.ui.MultilineTextInputWidget( {
					rows: 10
				} ), {
					label: msg( 'blockusers' ),
					align: 'top',
					id: 'wpMassBlockUsers'
				}
			),
			new OO.ui.FieldLayout(
				talkTextField, {
					label: msg( 'talkmsg' ),
					align: 'top',
					id: 'wpMassBlockMessage'
				}
			),
			new OO.ui.FieldLayout( userTextField, {
				label: msg( 'upmsg' ),
				align: 'top',
				id: 'wpMassBlockTag'
			} )
		]
	} );

	var reasonOpts = [ {
			optgroup: msg( 'other-reason' )
		},
		{
			data: 'other',
			label: msg( 'other-reason' )
		},
		{
			optgroup: msg( 'common-reasons' )
		}
	];
	for ( var i = 1, j = reasons.length; i < j; i++ ) {
		reasonOpts.push( {
			data: reasons[ i ],
			label: reasons[ i ]
		} );
	}

	var expiryField = new OO.ui.TextInputWidget( {
		maxLength: 255,
		id: 'wpMassBlockExpiry'
	} );

	var otherReasonField = new OO.ui.TextInputWidget( {
		maxLength: 255,
		id: 'wpMassBlockReason'
	} );

	var reasonsDropdown = new OO.ui.DropdownInputWidget( {
		options: reasonOpts,
		id: 'wpMassBlockReasons'
	} ).on( 'change', function() {
		var reason = reasonsDropdown.getValue(),
			maxlength = ( reason === "other" ? 255 : ( 255 - ': '.length ) - reason.length );

		$( '#wpMassBlockReason input' ).attr( "maxlength", maxlength );
	} );


	var blockOptsArray = [
		new OO.ui.FieldLayout(
			reasonsDropdown, {
				label: msg( 'common-reasons' )
			}
		),

		new OO.ui.FieldLayout(
			otherReasonField, {
				label: msg( 'extra-reason' )
			}
		),

		new OO.ui.FieldLayout( expiryField, {
			label: msg( 'exptime' )
		} ),

		new OO.ui.FieldLayout(
			new OO.ui.CheckboxInputWidget( {
				id: 'wpMassBlockAnononly',
				selected: true
			} ), {
				label: msg( 'anononly' )
			}
		),
		new OO.ui.FieldLayout(
			new OO.ui.CheckboxInputWidget( {
				id: 'wpMassBlockAutoblock',
				selected: true
			} ), {
				label: msg( 'autoblock' )
			}
		),
		new OO.ui.FieldLayout(
			new OO.ui.CheckboxInputWidget( {
				id: 'wpMassBlockNocreate',
				selected: true
			} ), {
				label: msg( 'nocreate' )
			}
		),
		new OO.ui.FieldLayout(
			new OO.ui.CheckboxInputWidget( {
				id: 'wpMassBlockEmail'
			} ), {
				label: msg( 'noemail' )
			}
		)
	];

	if ( blockAllowsTalkEdit ) {
		blockOptsArray.push( new OO.ui.FieldLayout(
			new OO.ui.CheckboxInputWidget( {
				id: 'wpMassBlockTalkpage'
			} ), {
				label: msg( 'notalk' )
			}
		) );
	}

	blockOptsArray.push( new OO.ui.FieldLayout(
		new OO.ui.CheckboxInputWidget( {
			id: 'wpMassBlockReblock'
		} ), {
			label: msg( 'override' )
		}
	) );

	var blockOpts = new OO.ui.FieldsetLayout( {
		label: msg( 'block-options-label' ),
		items: blockOptsArray
	} );


	talkSummaryField =
		new OO.ui.TextInputWidget( {
			maxLength: 255,
			id: 'wpMassBlockSummaryTalk',
			value: msg( 'summary-default' ),
			// The text is empty by default
			disabled: true
		} );

	talkTextField.on( 'change', function() {
		talkSummaryField.setDisabled( talkTextField.getValue().trim() === '' );
	} );

	userSummaryField =
		new OO.ui.TextInputWidget( {
			maxLength: 255,
			id: 'wpMassBlockSummaryUser',
			value: msg( 'summary-default' ),
			// The text is empty by default
			disabled: true
		} );

	userTextField.on( 'change', function() {
		userSummaryField.setDisabled( userTextField.getValue().trim() === '' );
	} );

	talkProtectCb =
		new OO.ui.CheckboxInputWidget( {
			id: 'wpMassBlockProtectTalk',
			selected: true
		} );

	userProtectCb =
		new OO.ui.CheckboxInputWidget( {
			id: 'wpMassBlockProtectUser',
			selected: true
		} );

	protectReasonField =
		new OO.ui.TextInputWidget( {
			maxLength: 255,
			id: 'wpMassBlockProtectReason',
			value: msg( 'protect-reason-default' )
		} );

	/**
	 * Toggle protection fields
	 */
	var toggleProtectionFields = function() {
		if ( !talkProtectCb.isSelected() && !userProtectCb.isSelected() ) {
			protectReasonField.setDisabled( true );
		} else {
			protectReasonField.setDisabled( false );
		}
	};

	talkProtectCb.on( 'change', toggleProtectionFields );
	userProtectCb.on( 'change', toggleProtectionFields );

	var furtherOpts = new OO.ui.FieldsetLayout( {
		label: msg( 'further-options-label' ),
		items: [
			new OO.ui.FieldLayout( talkSummaryField, {
				label: msg( 'talksummary' )
			} ),
			new OO.ui.FieldLayout( userSummaryField, {
				label: msg( 'upsummary' )
			} ),
			new OO.ui.FieldLayout( talkProtectCb, {
				label: msg( 'talkprotect' )
			} ),
			new OO.ui.FieldLayout( userProtectCb, {
				label: msg( 'upprotect' )
			} ),
			new OO.ui.FieldLayout( protectReasonField, {
				label: msg( 'protect-reason-label' )
			} )
		]
	} );

	expiryField.on( 'change', function() {
		// Several fields cannot be used if the expiry isn't infinite
		var enable = isInfinity( $( '#wpMassBlockExpiry input' ).val().trim() ),
			// These are OOUI elements
			disableEls = [
				userTextField,
				talkProtectCb,
				userSummaryField,
				userProtectCb,
				protectReasonField
			];

		for ( var el in disableEls ) {
			disableEls[ el ].setDisabled( !enable );
		}
	} );

	submitBtn = new OO.ui.ButtonInputWidget( {
			label: msg( 'submit-text' ),
			title: msg( 'submit-text' ),
			id: 'wpMassBlockSubmit',
			flags: [
				'primary',
				'progressive'
			]
		} )
		.on( 'click', doMassBlock );

	form.addItems( [ basicFieldset, blockOpts, furtherOpts, new OO.ui.FieldLayout( submitBtn ) ] );


	var bodyContentID = ( mw.config.get( 'skin' ) === "cologneblue" ? "#article" : "#bodyContent" );
	$( bodyContentID ).html( '<div style="font-size:150%"><u>' + msg( 'abuse-disclaimer' ) + '</u></div><br /><hr /><div id="wpMassBlockFailedContainer"></div>' );
	$( bodyContentID ).append( form.$element );
}

/**
 * Utility function to tell if an expiry is infinite
 *
 * @param {string} expiry
 * @return {bool}
 */
function isInfinity( expiry ) {
	return /^(indefinite|infinite|infinity|never|)$/i.test( expiry );
}

/**
 * Init this gadget.
 *
 * @param {Object} msg An array of local messages
 */
function initMassBlock( msg ) {
	console.log( 'Loading Massblock.' );
	if ( mw.config.get( "wgNamespaceNumber" ) === -1 &&
		( mw.config.get( "wgTitle" ) == "Massblock" || mw.config.get( "wgTitle" ) === "MassBlock" ) &&
		( /sysop/ ).test( mw.config.get( "wgUserGroups" ) )
	) {
		localMsg = msg;
		mw.loader.using( [ 'mediawiki.util', 'mediawiki.api', 'mediawiki.jqueryMsg', 'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows' ], $.ready )
			.then( function loadMsg() {
				return new mw.Api().loadMessagesIfMissing( [ 'Ipbreason-dropdown' ] );
			} )
			.then( massblockform );
	} else {
		mw.loader.using( 'mediawiki.util' )
			.then( function() {
				mw.util.addPortletLink( 'p-tb', mw.config.get( 'wgScript' ) + '?title=Special:Massblock', msg( 'toolbar-text' ) );
			} );
	}
}