MediaWiki:Gadget-Azioni-dopo-blocco.js

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.

// <nowiki>

/**
 * Aggiunge un pulsante in Speciale:Blocca che apre un form dove si possono
 * scegliere azioni aggiuntive per l'ultimo blocco dato all'utente selezionato,
 * come l'inserimento di template nella sua pagina utente e/o l'inserimento di
 * avvisi nella sua talk, con o senza svuotamento e protezione delle pagine.
 * 
 * @author https://it.wikipedia.org/wiki/Utente:Rotpunkt
 */
/* global mediaWiki, jQuery, OO */

( function ( mw, $ ) {
	'use strict';

	var indefMsgs = {
		bloccoInfinitoOption:
			'Sostituisci ' + getLinkTpl( 'BloccoInfinito' ) + ' alla pagina utente e alla talk utente',
		sockpuppetOption:
			'Sostituisci ' + getLinkTpl( 'BloccoInfinito' ) + ' + ' + getLinkTpl( 'Sockpuppet' ) + ' alla pagina utente e ' + getLinkTpl( 'BloccoInfinito' ) + ' alla talk utente',
		bloccoNomeUtenteOption:
			'L\'opzione BloccoNomeUtente è disabilitata quando l\'utente non può modificare la propria talk (modificare il blocco oppure valutare le opzioni precedenti)',
		nota:
			'pagina utente e talk sono poi protette; la talk non &egrave; creata se non esiste (a parte con BloccoNomeUtente)'
	};
	var appealableIndefMsgs = {
		bloccoInfinitoOption:
			'Sostituisci ' + getLinkTpl( 'BloccoInfinito' ) + ' alla pagina utente e aggiungi ' + getLinkTpl( 'Blocco' ) + ' nella talk utente',
		sockpuppetOption:
			'Sostituisci ' + getLinkTpl( 'BloccoInfinito' ) + ' + ' + getLinkTpl( 'Sockpuppet' ) + ' alla pagina utente e aggiungi ' + getLinkTpl( 'Blocco' ) + ' nella talk utente',
		bloccoNomeUtenteOption:
			'Aggiungi ' + getLinkTpl( 'BloccoNomeUtente' ) + ' nella talk utente e sostituisci ' + getLinkTpl( 'BloccoInfinito' ) + ' alla pagina utente',
		nota:
			'la pagina utente è poi protetta; la talk non &egrave; creata se non esiste (a parte con BloccoNomeUtente)'
	};

	/**
	 * Funzione di utilità per le etichette, restituisce il link a un template wiki.
	 * 
	 * @param {string} tpl - Il nome del template.
	 * @return {string}
	 */
	function getLinkTpl( tpl ) {
		return '{{<a target="_blank" href="' +
			   mw.config.get( 'wgArticlePath' ).replace( '$1', 'Template:' + tpl ) +
			   '">' + tpl + '</a>}}';
	}

	/**
	 * Ripulisce la cache di una pagina per forzare l'eventuale aggiornamento
	 * della categorizzazione automatica del template NomeUtenteInappropriato.
	 * 
	 * @param {string} title - La pagina dove pulire la cache.
	 */
	function purge( title ) {
		new mw.Api().post( {
			action: 'purge',
			titles: title,
			format: 'json',
		} );
	}

	/**
	 * Protegge una pagina con il tipo di protezione specificato.
	 * 
	 * @param {string} title - La pagina da proteggere.
	 * @param {string} reason - La motivazione da usare.
	 * @param {string} protections - Il tipo di protezione.
	 * @param {function} successHandler - La funzione da richiamare in caso di successo.
	 */
	function protect( title, reason, protections, successHandler ) {
		new mw.Api().postWithToken( 'csrf', {
			action: 'protect',
			title: title,
			protections: protections,
			reason: reason,
			watchlist: 'nochange',
			format: 'json',
		} ).then( function () {
			successHandler();
		} ).fail( function () {
			OO.ui.alert( 'Errore nel proteggere la pagina:' + title );
		} );
	}

	/**
	 * Sostituisce il contenuto di una pagina con quello specificato.
	 * 
	 * @param {string} title - Il titolo della pagina.
	 * @param {string} newContent - Il nuovo contenuto.
	 * @param {boolean} nocreate - Se impostato a true la pagina non verrà creata se non esiste.
	 * @param {string}} summary - L'oggetto da usare per la modifica.
	 * @param {function} successHandler - La funzione da richiamare in caso di successo.
	 */
	function replaceContent( title, newContent, nocreate, summary, successHandler ) {
		new mw.Api().postWithEditToken( {
			action: 'edit',
			title: title,
			text: newContent,
			summary: summary,
			nocreate: nocreate,
			watchlist: 'nochange',
			format: 'json',
		} ).done ( function ( data ) {
			successHandler( true );
		} ).fail ( function ( code, data ) {
			if ( code === 'missingtitle' ) {
				successHandler( false );
			} else {
				OO.ui.alert( 'Errore nello svuotare la pagina: ' + title );
			}
		} );
	}

	/**
	 * Aggiunge una sezione alla pagina indicata.
	 * 
	 * @param {string} title - Il titolo della pagina.
	 * @param {string} text - Il contenuto della sezione.
	 * @param {string} watchlist - Il corrispettivo parametro di API:Edit,
	 *							   un valore fra watch/unwatch/preferences/nochange
	 * @param {function} successHandler - La funzione da richiamare in caso di successo.
	 */
	 function newSection( title, text, watchlist, successHandler ) {
		new mw.Api().newSection( title, 'Blocco', text, { watchlist: watchlist } )
			.done( function ( data ) {
				successHandler();
			} )
			.fail ( function ( code, data ) {
				OO.ui.alert( 'Errore nel creare l\'avviso: ' + code );
			} );
	 }

	/**
	 * Crea il form per scegliere un'eventuale azione dopo il blocco utente.
	 * 
	 * @param {string} username - Il nome dell'utente che è stato bloccato.
	 * @param {string} expiry - La durata che è stata data al blocco.
	 * @param {string} reason - La motivazione che è stata usata per il blocco.
	 * @param {array|null} blockedPages - Le pagine oggetto del blocco parziale o null
	 * @param {array|null} blockedNs - I namespace oggetto del blocco parziale o null
	 */
	function buildForm( username, expiry, reason, blockedPages, blockedNs ) {
		var $form = $( '<div id="adb-form">' );
		var fieldset = new OO.ui.FieldsetLayout( {
			id: 'adb-fieldset',
			label: 'Azioni opzionali dopo il blocco utente'
		} );
		var tplRc = new OO.ui.RadioOptionWidget( {
			label: $( '<span>Aggiungi ' + getLinkTpl( 'Rc' ) + ' nella talk dell\'utente</span>' )
		} );
		var tplBlocco = new OO.ui.RadioOptionWidget( {
			label: $( '<span>Aggiungi ' + getLinkTpl( 'Blocco' ) + ' nella talk dell\'utente</span>' )
		} );
		var radioSelect = new OO.ui.RadioSelectWidget( { 
			items: [ tplRc, tplBlocco ] 
		} );
		var undoButton = new OO.ui.ButtonWidget( {
			label: 'Annulla'
		} ).on( 'click', function () {
			$form.remove();
		} );
		var confirmButton = new OO.ui.ButtonWidget( {
			label: 'Inserisci avviso',
			flags: [ 'primary', 'progressive' ]
		} ).on( 'click', function () {
			var text;
			if ( tplRc.isSelected() ) {
				text = '{{Rc|' + expiry + '}} --~~~~';
			} else if ( tplBlocco.isSelected() ) {
				text = '{{Blocco|' + reason + '|' + expiry;
				if ( blockedPages ) {
					text += '|pagine = ' + blockedPages.join( ', ' );
				}
				if ( blockedNs ) {
					text += '|namespace = ' + blockedNs.map( ns => `{{subst:ns:${ ns }}}` ).join( ', ' );
				}
				text +=  '}} --~~~~';
			}
			if ( text ) {
				newSection( 'Discussioni utente:' + username, text, 'preferences', function () {
					$form.remove();
					OO.ui.alert( 'Avviso aggiunto con successo.' );
				} );
			} else {
				OO.ui.alert( 'Nessuna opzione selezionata' );
			}
		} );
		var buttonField = new OO.ui.FieldLayout(
			new OO.ui.Widget( {
				content: [
					new OO.ui.HtmlSnippet( '<hr style="margin:16px 0">' ),
					new OO.ui.HorizontalLayout( {
						items: [
							undoButton,
							confirmButton
						]
					} )
				]
			} ),
			{
				align: 'top'
			}
		);
		fieldset.addItems( [ radioSelect, buttonField ] );

		$form
			.append( '<h2>Azioni dopo il blocco</h2>', fieldset.$element )
			.insertBefore( '#adb-button' );
		fieldset.scrollElementIntoView();
	}

	/**
	 * Sostituisce {{BloccoInfinito}} alla pagina utente e la protegge.
	 * Sostituisce {{BloccoInfinito}} alla talk utente e la protegge se è stata impedita
	 * la modifica della talk all'utente, altrimenti aggiunge {{Blocco}} nella talk.
	 * 
	 * @param {string} username - Il nome dell'utente che è stato bloccato.
	 * @param {string} reason - La motivazione che è stata usata per il blocco.
	 * @param {boolean} createUP - Se creare o meno la pagina utente qualora non esista.
	 * @param {boolean} appealAllowed - Se l'utente può chiedere una revisione del blocco.
	 * @param {function} successHandler - La funzione da richiamare in caso di successo.
	 */
	function doBloccoInfinito( username, reason, createUP, appealAllowed, successHandler ) {
		var userText = '{{BloccoInfinito}}';
		var summary = 'utente bloccato infinito';
		replaceContent( 'Utente:' + username, userText, !createUP, summary, function ( exists ) {
			var protections = exists ? 'edit=sysop|move=sysop' : 'create=sysop';
			protect( 'Utente:' + username, summary, protections, function () {
				if ( appealAllowed ) {
					let talkText = '{{Blocco|' + reason + '|infinito}} --~~~~';
					newSection( 'Discussioni utente:' + username, talkText, 'nochange', successHandler );
				} else {
					let talkText = '{{BloccoInfinito}}';
					replaceContent( 'Discussioni utente:' + username, talkText, true, summary, function ( exists ) {
						var protections = exists ? 'edit=sysop|move=sysop' : 'create=sysop';
						protect( 'Discussioni utente:' + username, summary, protections, successHandler );
					} );
				}
			} );
		} );
	}

	/**
	 * Sostituisce {{BloccoInfinito}} + {{Sockpuppet}} alla pagina utente e la protegge.
	 * Sostituisce {{BloccoInfinito}} alla talk utente e la protegge se è stata impedita
	 * la modifica della talk all'utente, altrimenti aggiunge {{Blocco}} nella talk.
	 * 
	 * @param {string} username - Il nome dell'utente che è stato bloccato.
	 * @param {string} reason - La motivazione che è stata usata per il blocco.
	 * @param {boolean} appealAllowed - Se l'utente può chiedere una revisione del blocco.
	 * @param {function} successHandler - La funzione da richiamare in caso di successo.
	 */
	function doSockpuppet( username, reason, appealAllowed, successHandler ) {
		OO.ui.prompt(
			'L\'utente sockmaster dietro il sockpuppet, senza "Utente:"',
			{ title: 'Parametro obbligatorio' }
		).done( function ( result ) {
			if ( !result.trim() ) { return; }
			var userText = '{{BloccoInfinito}}{{Sockpuppet|' + result + '}}';
			var summary = 'utente bloccato infinito';
			replaceContent( 'Utente:' + username, userText, false, summary, function () {
				protect( 'Utente:' + username, summary, 'edit=sysop|move=sysop', function () {
					if ( appealAllowed ) {
						let talkText = '{{Blocco|' + reason + '|infinito}} --~~~~';
						newSection( 'Discussioni utente:' + username, talkText, 'nochange', successHandler );
					} else {
						let talkText = '{{BloccoInfinito}}';
						replaceContent( 'Discussioni utente:' + username, talkText, true, summary, function ( exists ) {
							var protections = exists ? 'edit=sysop|move=sysop' : 'create=sysop';
							protect( 'Discussioni utente:' + username, summary, protections, successHandler );
						} );
					}
				} );
			} );
		} );
	}

	/**
	 * Sostituisce {{BloccoInfinito}} alla pagina utente e la protegge.
	 * Aggiunge {{BloccoNomeUtente}} nella talk utente.
	 * 
	 * @param {string} username - Il nome dell'utente che è stato bloccato.
	 * @param {boolean} createUP - Se creare o meno la pagina utente qualora non esista.
	 * @param {function} successHandler - La funzione da richiamare in caso di successo.
	 */
	function doBloccoNomeUtente( username, createUP, successHandler ) {
		var userText = '{{BloccoInfinito}}';
		var talkText = '{{subst:BloccoNomeUtente}} --~~~~';
		var summary = 'utente bloccato infinito';
		replaceContent( 'Utente:' + username, userText, !createUP, summary, function ( exists ) {
			var protections = exists ? 'edit=sysop|move=sysop' : 'create=sysop';
			protect( 'Utente:' + username, summary, protections, function () {
				newSection( 'Discussioni utente:' + username, talkText, 'nochange', successHandler );
			} );
		} );
	}

	/**
	 * Crea il form per scegliere un'eventuale azione dopo il blocco utente (infinito).
	 * 
	 * @param {string} username - Il nome dell'utente che è stato bloccato (infinito).
	 * @param {string} reason - La motivazione che è stata usata per il blocco (infinito).
	 * @param {boolean} appealAllowed - Se l'utente può chiedere una revisione del blocco.
	 */
	function buildFormInfinito( username, reason, appealAllowed ) {
		var msgs = appealAllowed ? appealableIndefMsgs : indefMsgs;
		var $form = $( '<div id="adb-form">' );
		var fieldset = new OO.ui.FieldsetLayout( {
			id: 'adb-fieldset',
			label: 'Azioni opzionali dopo il blocco utente (infinito)'
		} );
		var tplBloccoInfinito = new OO.ui.RadioOptionWidget( {
			label: $( '<span>' + msgs.bloccoInfinitoOption + '</span>' )
		} );
		var tplSockpuppet = new OO.ui.RadioOptionWidget( {
			label: $( '<span>' + msgs.sockpuppetOption + '</span>' )
		} );
		var tplBloccoNomeUtente = new OO.ui.RadioOptionWidget( {
			label: $( '<span>' + msgs.bloccoNomeUtenteOption + '</span>' ),
			disabled: !appealAllowed
		} );
		var radioSelect = new OO.ui.RadioSelectWidget( { 
			items: [ tplBloccoInfinito, tplSockpuppet, tplBloccoNomeUtente ]
		} );
		// Con {{bloccoinfinito}} e {{blocconomeutente}} permette di scegliere se creare la talk
		var creaPUCb = new OO.ui.CheckboxInputWidget( {
			disabled: true
		} );
		var creaPUField = new OO.ui.FieldLayout(
			creaPUCb,
			{
				label: 'Crea la pagina utente se non esiste',
				align: 'inline'
			}
		);
		radioSelect.on( 'choose', function () {
			var hide = !( tplBloccoInfinito.isSelected() || tplBloccoNomeUtente.isSelected() ),
				select = hide ? false : creaPUCb.isSelected();
			creaPUCb
				.setDisabled( hide )
				.setSelected( select );
		} );
		var undoButton = new OO.ui.ButtonWidget( {
			label: 'Annulla'
		} ).on( 'click', function () {
			$form.remove();
		} );
		var confirmButton = new OO.ui.ButtonWidget( {
			label: 'Inserisci i template',
			flags: [ 'primary', 'progressive' ]
		} ).on( 'click', function () {
			var createUP = creaPUCb.isSelected();
			var successHandler = () => {
				$form.remove();
				OO.ui.alert( 'Modifiche e protezioni pagina completate con successo' );
			};
			if ( tplBloccoInfinito.isSelected() ) {
				doBloccoInfinito( username, reason, createUP, appealAllowed, successHandler );
			} else if ( tplSockpuppet.isSelected() ) {
				doSockpuppet( username, reason, appealAllowed, successHandler );
			} else if ( tplBloccoNomeUtente.isSelected() ) {
				doBloccoNomeUtente( username, createUP, successHandler );
			} else {
				OO.ui.alert( 'Nessuna opzione selezionata' );
			}
		} );
		var buttonField = new OO.ui.FieldLayout(
			new OO.ui.Widget( {
				content: [
					new OO.ui.HtmlSnippet( '<hr style="margin:16px 0">' ),
					new OO.ui.HorizontalLayout( {
						items: [
							undoButton,
							confirmButton
						]
					} )
				]
			} ),
			{
				align: 'top'
			}
		);
		var notaLabel = new OO.ui.LabelWidget( {
			label: new OO.ui.HtmlSnippet( '<div style="margin-top:0.5em"><b>Nota</b>: ' + msgs.nota + '.</div>' )
		} );
		fieldset.addItems( [ radioSelect, creaPUField, notaLabel, buttonField ] );

		$form
			.append( '<h2>Azioni dopo il blocco</h2>', fieldset.$element )
			.insertBefore( '#adb-button' );
		fieldset.scrollElementIntoView();
	}

	/**
	 * Esegue una chiamata API per ottenere i dati del blocco impostato.
	 * 
	 * @param {string} username - Il nome dell'utente che è stato bloccato.
	 * @return {jQuery.Promise}
	 */
	function getLatestBlockData( username ) {
		return new mw.Api().get( {
			action: 'query',
			list: 'blocks',
			bkusers: username,
			bkprop: 'id|user|by|timestamp|expiry|reason|flags|restrictions',
			format: 'json',
			formatversion: 2
		} ).then( function ( data ) {
			const blocks = data.query.blocks;
			const filteredBlocks = blocks.filter( block => block.by === mw.config.get( 'wgUserName' ) );
			return filteredBlocks.sort( ( b1, b2 ) => b1.id < b2.id )[ 0 ];
		} );
	}

	/**
	 * Individua quale form creare in base ai dati del blocco impostato.
	 */
	function onAdbButtonClick() {
		var blockedUser = decodeURI( window.___location.pathname.split( '/' ).pop() ).replaceAll( '_', ' ' );

		getLatestBlockData( blockedUser ).done( function ( block ) {
			if ( !block ) {
				OO.ui.alert( `Non hai impostato un blocco per l\'utente ${ blockedUser }` );
				return;
			}

			const {
				user: username,
				'duration-l10n': expiry,
				reason,
				allowusertalk: allowUserTalk,
				partial: isPartial,
				restrictions
			} = block;

			if ( !username || !expiry || !reason ) {
				return;
			}

			if ( !isPartial && expiry === 'infinito' ) {
				buildFormInfinito( username, reason, allowUserTalk );
			} else {
				const blockedPages = restrictions.pages
					? restrictions.pages.map( page => page.title )
					: null;
				const blockedNs = restrictions.namespaces;
				buildForm( username, expiry, reason, blockedPages, blockedNs );
			}
		} );
	}

	$( function () {
		if ( mw.config.get( 'wgCanonicalSpecialPageName' ) !== 'Block' ) {
			return;
		}

		mw.loader.using( [
			'mediawiki.api',
			'mediawiki.util',
			'oojs-ui-core',
			'oojs-ui-widgets',
			'oojs-ui-windows'
		] ).done( function () {
			const button = new OO.ui.ButtonWidget( {
				id: 'adb-button',
				label: 'Azioni dopo il blocco'
			} ).on( 'click', onAdbButtonClick );

			mw.util.addCSS( `
				#adb-button {
					margin-top: 4px;
				}
				#adb-fieldset {
					margin-top: 16px;
				}
				#adb-form {
					margin-top: 8px;
				}
				#adb-form + #adb-button,
				#mw-block-form:not(:has(.mw-block-log)) + #adb-button {
					display: none;
				}
			` );

			$( '#mw-block-form' ).after( button.$element );
		} );
	} );
}( mediaWiki, jQuery ) );

// </nowiki>