MediaWiki:Gadget-PrendiInCaricoRichiestaVAR.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.
/**
* Questo accessorio automatizza tutte le operazioni necessarie per prendere
* formalmente in carico una richiesta indirizzata alla Commissione arbitrale.
*
* Quando l'utente clicca il pulsante "Prendi in carico" del template:RichiestaVAR,
* visualizza una richiesta di conferma e poi riceve riscontro delle operazioni
* in corso in una finestra di dialogo.
*
* Per ulteriori informazioni: [[Aiuto:Accessori/Prendi in carico richiesta VAR]].
*
* @author https://it.wikipedia.org/wiki/Utente:Sakretsu
*/
/* global mediaWiki, jQuery, OO */
( function ( mw, $ ) {
'use strict';
const conf = mw.config.get( [
'wgArticleId',
'wgNamespaceIds',
'wgNamespaceNumber',
'wgPageName',
'wgRelevantUserName',
'wgRevisionId',
'wgUserGroups',
'wgUserId',
'wgUserName'
] );
const dependencies = [
'ext.gadget.ProgressDialog',
'mediawiki.api',
'mediawiki.util',
'oojs-ui-core',
'oojs-ui-widgets',
'oojs-ui-windows'
];
const resolutionRootPage = isOwnSandbox()
? `Utente:${ conf.wgUserName }/Sandbox/Test richiesta arbcom`
: 'Wikipedia:Commissione arbitrale/Deliberazioni';
const resolutionTemplate = isOwnSandbox()
? 'Wikipedia:Commissione arbitrale/ModelloDeliberazione/Sandbox'
: 'Wikipedia:Commissione arbitrale/ModelloDeliberazione';
const requestTemplate = isOwnSandbox()
? 'RichiestaVAR/Sandbox'
: 'RichiestaVAR';
/**
* Verifica se è stato compilato solo il parametro "motivo" del
* template:RichiestaVAR passato in input.
*
* @param {object} [request] Il template:RichiestaVAR da esaminare
* @return {boolean}
*/
function isRequestNew( request ) {
const status = request.args.stato,
resolutionLink = request.args[ 'link deliberazione' ],
reason = request.args.motivo;
return ( !status || !status.value.trim() ) &&
( !resolutionLink || !resolutionLink.value.trim() ) &&
( reason && reason.value.trim() );
}
/**
* Filtra i template:RichiestaVAR passati in input scartando le richieste vecchie.
* Restituisce il template il cui indice combacia con quello del pulsante
* cliccato dall'utente.
*
* @param {number} [buttonIndex] L'indice del pulsante cliccato
* @param {object} [requests] Array di template:RichiestaVAR
* @return {object}
*/
function findRequest( buttonIndex, requests ) {
return requests
.filter( request => isRequestNew( request ) )
.sort( ( a, b ) => {
return a.span[ 0 ] > b.span[ 0 ] ? 1 : -1;
} )[ buttonIndex ];
}
/**
* Restituisce la stringa passata in input dopo aver inserito un backslash
* davanti a ciascun carattere.
*
* @param {string} [str] La stringa da manipolare
* @return {string}
*/
function escapeChars( str ) {
return Array.from( str, c => '\\' + c ).join( '' );
}
/**
* Restituisce la data passata in input nel formato YYYY-MM-DD.
*
* @param {object} [date] L'oggetto che rappresenta la data
* @return {string}
*/
function formatDate( date ) {
const localeDateString = date.toLocaleDateString( 'it-IT', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
timeZone: 'Europe/Rome'
} );
return localeDateString.substring( 6, 10 ) + '-'
+ localeDateString.substring( 3, 5 ) + '-'
+ localeDateString.substring( 0, 2 );
}
/**
* Restituisce il link a una pagina wiki.
*
* @param {string} [title] Il titolo della pagina da linkare
* @return {string}
*/
function link( title ) {
return `<a href=${ mw.util.getUrl( title ) }>${ title }</a>`;
}
/**
* Effettua una chiamata API per chiedere la modifica di una pagina.
*
* @param {object} [customParams] I parametri della chiamata che si
* integrano con quelli precompilati (o li sovrascrivono)
* @return {jQuery.Promise}
*/
function editContent( customParams ) {
return new mw.Api( {
parameters: {
action: 'edit',
format: 'json',
watchlist: 'nochange'
}
} ).postWithToken( 'csrf', customParams );
}
/**
* Effettua una chiamata API per ottenere l'ID dell'ultima versione di una pagina.
*
* @param {number} [pageid] L'ID della pagina
* @return {jQuery.Promise}
*/
function getCurrentRevisionId( pageid ) {
return new mw.Api().get( {
action: 'query',
pageids: pageid,
prop: 'revisions',
rvprop: 'ids',
format: 'json',
formatversion: 2
} );
}
/**
* Effettua una chiamata API per ottenere la versione desiderata di una pagina,
* compreso un array di tutti i template:RichiestaVAR in essa contenuti.
*
* @param {number} [revid] L'ID della versione della pagina
* @return {jQuery.Promise}
*/
function getRevision( revid ) {
return $.ajax( {
url: '//itwikiapi.toolforge.org/v1/revisions',
data: {
revid: revid,
rvprop: 'ids|content|timestamp',
templatenames: requestTemplate
}
} );
}
/**
* Effettua una chiamata API per ottenere il conteggio delle pagine che
* sono state finora create sotto il path delle deliberazioni dell'arbcom.
*
* @return {jQuery.Promise}
*/
function getResolutionCount() {
const colonIndex = resolutionRootPage.indexOf( ':' ),
nsName = resolutionRootPage.substring( 0, colonIndex ),
pageName = resolutionRootPage.substring( colonIndex + 1 ),
pageNameEscaped = escapeChars( pageName );
const searchString = 'intitle:"' + pageName.replace( '"', '' ) + '"'
+ ' intitle:/' + pageNameEscaped + '\\/[1-9][0-9]*/'
+ ' -intitle:/' + pageNameEscaped + '\\/[1-9][0-9]*[^0-9]/'
+ ' -intitle:/.+' + pageNameEscaped + '/';
return new mw.Api().get( {
action: 'query',
list: 'search',
srsearch: searchString,
srnamespace: conf.wgNamespaceIds[ nsName.toLowerCase() ],
srinfo: 'totalhits',
srlimit: 1,
srsort: 'create_timestamp_desc',
format: 'json',
formatversion: '2'
} );
}
/**
* Inserisce il link alla deliberazione della Commissione arbitrale nel
* parametro "link deliberazione" del template:RichiestaVAR che corrisponde
* alla richiesta aperta.
* Restituisce il wikitesto modificato.
*
* @param {string} [link] Il link alla deliberazione
* @param {object} [request] Il template della richiesta aperta
* @param {string} [wikitext] Il wikitesto da modificare
* @return {string}
*/
function insertResolutionLink( link, request, wikitext ) {
let name, value, startIndex, endIndex;
if ( request.args[ 'link deliberazione' ] ) {
( { name, value } = request.args[ 'link deliberazione' ] );
value = value.replace( /(\n?$)/, link + '$1' );
[ startIndex, endIndex ] = request.args[ 'link deliberazione' ].span;
} else {
// aggiunge il parametro mantenendo la stessa spaziatura di "motivo"
( { name, value } = request.args.motivo );
name = name.replace( 'motivo', 'link deliberazione' );
value = value.replace( value.trim(), link );
startIndex = endIndex = request.span[ 1 ] - 2;
}
return wikitext.substring( 0, startIndex )
+ `|${ name }=${ value }`
+ wikitext.substring( endIndex );
}
/**
* Crea la pagina dove sarà pubblicata la deliberazione della Commissione
* arbitrale e la linka o include in tutte le pagine correlate.
*
* @param {number} [buttonIndex] L'indice del pulsante cliccato
* @param {function} [progressMsgHandler] La funzione per mostrare progressi
* @param {function} [errorMsgHandler] La funzione per mostrare errori
* @param {function} [successMsgHandler] La funzione per mostrare successo
* @return {jQuery.Promise}
*/
function acceptRequest( buttonIndex, progressMsgHandler, errorMsgHandler, successMsgHandler ) {
const currentRevisionIdPromise = getCurrentRevisionId( conf.wgArticleId );
let revision, resolutionTitle, request, submitter;
progressMsgHandler( mw.msg( 'fetchingRequestPage' ) );
// scarica i dati della versione visualizzata della pagina che contiene
// la richiesta
return getRevision( conf.wgRevisionId ).then( result => {
if ( !result.query.badrevids ) {
revision = result.query.pages[ 0 ].revisions[ 0 ];
request = findRequest( buttonIndex, revision.templates );
}
if ( !request ) {
currentRevisionIdPromise.abort();
return $.Deferred().reject( 'templatemissing' );
}
// ottiene l'ID dell'ultima versione della pagina che contiene la richiesta
return currentRevisionIdPromise;
} ).then( result => {
// verifica che la pagina non sia stata modificata nel frattempo
if (
result.query.pages[ 0 ].missing ||
result.query.pages[ 0 ].revisions[ 0 ].revid !== revision.revid
) {
return $.Deferred().reject( 'oldrevision' );
}
// ottiene il conteggio delle deliberazioni che sono state create finora
return getResolutionCount();
} ).then( result => {
progressMsgHandler( mw.msg( 'creatingResolutionPage' ) );
let requestDate,
counterparty,
resolutionNumber = result.query.searchinfo.totalhits + 1;
// verifica se il richiedente è un utente specifico
if ( conf.wgRelevantUserName && !isOwnSandbox() ) {
submitter = conf.wgRelevantUserName;
} else if ( request.args.richiedente ) {
submitter = request.args.richiedente.value.trim() || null;
}
if ( request.args[ 'data richiesta' ] ) {
requestDate = request.args[ 'data richiesta' ].value.trim() || null;
}
if ( request.args.controparte ) {
counterparty = request.args.controparte.value.trim() || null;
}
const editContentPromise = $.Deferred(),
loopStartDate = new Date();
( function loop() {
resolutionTitle = `${ resolutionRootPage }/${ resolutionNumber }`;
// compila il modello predefinito delle deliberazioni dell'arbcom
const text = '{{subst:' + resolutionTemplate
+ '|richiedente=' + ( submitter || 'Richiesta comunitaria' )
+ '|data richiesta=' + ( requestDate || '' )
+ '|motivo=' + request.args.motivo.value.trim()
+ '|oldid motivo=' + revision.revid
+ '|controparte=' + ( counterparty || '' )
+ '|data presa in carico=' + formatDate( new Date() )
+ '}}';
// crea la pagina dove sarà pubblicata la deliberazione
editContent( {
title: resolutionTitle,
text: text,
summary: mw.msg( 'resolutionPageEditSummary' ),
createonly: 1,
watchlist: 'watch'
} ).then( result => {
progressMsgHandler( mw.msg( 'resolutionPageCreated', link( resolutionTitle ) ) );
editContentPromise.resolve( result );
} ).fail( code => {
if ( code !== 'articleexists' ) {
// si è verificato un errore inatteso
editContentPromise.reject( code );
} else if ( ( new Date() - loopStartDate ) < 10000 ) {
// prova a creare la pagina con un numero più alto se per
// qualche motivo ne esiste già una con questo numero
resolutionNumber += 1;
loop();
} else {
// stoppa il loop dopo 10 secondi (dovrebbe succedere solo se
// la connessione è lenta o se ci sono fin troppe anomalie
// nella numerazione delle deliberazioni già pubblicate)
editContentPromise.reject( 'resolutionpagecreationtimeout' );
}
} );
}() );
return editContentPromise;
} ).then( () => {
progressMsgHandler( mw.msg( 'transcludingResolutionPage', link( resolutionRootPage ) ) );
// include la pagina della deliberazione nella pagina di servizio dove sono
// elencate le deliberazioni recenti o non ancora pubblicate
return editContent( {
title: resolutionRootPage,
appendtext: `\n\n{{${ resolutionTitle }}}`,
summary: submitter
? mw.msg( 'resolutionRootPageEditSummary-userRequest', submitter )
: mw.msg( 'resolutionRootPageEditSummary-communityRequest' )
} );
} ).then( () => {
progressMsgHandler( mw.msg( 'notifyingSubmitter' ) );
// aggiorna il template:RichiestaVAR compilando il parametro
// "link deliberazione" col nome della pagina appena creata
const text = insertResolutionLink(
resolutionTitle,
request,
revision.slots.main.content
);
// pubblica la modifica
return editContent( {
pageid: conf.wgArticleId,
text: text,
summary: mw.msg( 'requestPageEditSummary', resolutionTitle ),
starttimestamp: revision.timestamp,
baserevid: revision.revid
} );
} ).done( () => {
successMsgHandler( mw.msg( 'success' ) );
} ).fail( code => {
let errorText = mw.msg( 'errorOccurred' ) + ' ';
switch( code ) {
case 'articleexists':
case 'editconflict':
case 'oldrevision':
case 'templatemissing':
errorText += mw.msg( code );
break;
case 'resolutionpagecreationtimeout':
errorText += mw.msg( code, link( resolutionTitle ) );
break;
default:
errorText += mw.msg( 'unknownError', code );
}
errorMsgHandler( errorText );
} );
}
/**
* Dà il via alle operazioni e le mostra in una finestra di dialogo.
*
* @param {number} [buttonIndex] L'indice del pulsante cliccato
* @param {OO.ui.WindowManager} [windowManager] Il gestore della finestra
*/
function onConfirmButtonClick( buttonIndex, windowManager ) {
// crea la finestra che mostra le operazioni in corso
const ProgressDialog = require( 'ext.gadget.ProgressDialog' );
const progressDialog = new ProgressDialog( {
closeButtonLabel: mw.msg( 'closeButtonLabel' ),
dialogTitle: mw.msg( 'dialogTitle' )
} );
$( 'body' ).append( windowManager.$element );
windowManager.addWindows( [ progressDialog ] );
windowManager.openWindow( 'progressDialog' );
// esegue le operazioni tenendo aggiornata la finestra
acceptRequest(
buttonIndex,
text => progressDialog.appendProgressMsg( text ),
text => progressDialog.appendErrorMsg( text ),
text => progressDialog.appendSuccessMsg( text )
).always(
() => progressDialog.showCloseButton()
);
}
/**
* Mostra un prompt di conferma dopo aver verificato che non siano state
* già avviate le operazioni.
*
* @param {object} [event] L'oggetto che rappresenta l'evento del clic
*/
function onAcceptButtonClick( event ) {
event.preventDefault();
const { buttonIndex, windowManager } = event.data;
if ( windowManager.isElementAttached() ) {
// apre la finestra delle operazioni in corso se esiste già
windowManager.openWindow( 'progressDialog' );
} else {
// chiede conferma all'utente se deve iniziare le operazioni
OO.ui.confirm( mw.msg( 'prompt' ) ).done( confirmed => {
if ( confirmed ) {
onConfirmButtonClick( buttonIndex, windowManager );
}
} );
}
}
/**
* Verifica se l'utente che sta usando l'accessorio è un membro dell'arbcom.
*
* @return {boolean}
*/
function isUserArbitrator() {
return conf.wgUserGroups.includes( 'arbcom' ) ||
conf.wgUserId === 2525053; // account di servizio dell'arbcom
}
/**
* Verifica se la pagina visualizzata è la pagina di una UP.
*
* @return {boolean}
*/
function isUP() {
return conf.wgPageName.startsWith( 'Wikipedia:Utenti_problematici/' ) &&
conf.wgArticleId !== 0;
}
/**
* Verifica se la pagina visualizzata è la pagina di discussione di un utente.
*
* @return {boolean}
*/
function isTalkPage() {
return conf.wgNamespaceNumber === 3 &&
conf.wgRelevantUserName !== null &&
conf.wgArticleId !== 0;
}
/**
* Verifica se la pagina visualizzata è una sandbox dell'utente che sta
* usando l'accessorio.
*
* @return {boolean}
*/
function isOwnSandbox() {
return conf.wgNamespaceNumber === 2 &&
conf.wgRelevantUserName === conf.wgUserName;
}
/**
* Verifica se la pagina visualizzata rientra fra quelle dove è previsto che
* sia eseguito l'accessorio.
*
* @return {boolean}
*/
function isPageValid() {
return isOwnSandbox() || isTalkPage() || isUP();
}
$( () => {
const acceptButton = $( '.pulsante-prendi-in-carico-richiesta-var' );
// termina l'esecuzione se riscontra anomalie
if ( !isPageValid() || !isUserArbitrator() || !acceptButton.length ) {
return;
}
// aspetta il corretto caricamento delle dipendenze prima di procedere
mw.loader.using( dependencies ).done( () => {
// carica i messaggi di sistema dell'accessorio
mw.messages.set( require( './PrendiInCaricoRichiestaVAR-Messages.json' ) );
// modifica il comportamento del pulsante "Prendi in carico"
acceptButton.each( ( i, el ) => {
const windowManager = new OO.ui.WindowManager();
$( el ).on( 'click', {
buttonIndex: i,
windowManager: windowManager
}, onAcceptButtonClick );
} );
} );
} );
}( mediaWiki, jQuery ) );