Utente:Titore/VC.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.
/**
* Aggiunge un link nella barra degli strumenti laterale
* per segnalare rapidamente un'utenza o un IP tra i vandalismi in corso.
*
* @author https://it.wiki.x.io/wiki/Utente:Titore and contributors
* @date 2019-09-17
* @license Dual-licensed under CC BY-SA 3.0 and GPL 3.0 (or any later version)
*/
(function (mw, $) {
'use strict';
var windowManager, dialog;
// API MediaWiki
const api = new mw.Api();
// Variabili MediaWiki
const conf = mw.config.get([
'wgRelevantUserName', // Presente in pagine attinenti all'utente (es. contributi, discussioni, pagina utente)
'wgUserName',
'wgArticlePath',
]);
// Utente da segnalare
const utente = conf.wgRelevantUserName;
// Utilizzatore dello script
const currentUser = conf.wgUserName;
// Pagina di servizio
const paginaSegnalazioni = 'Wikipedia:Vandalismi in corso';
// Campo oggetto
const oggetto = `[[Utente:Titore/VC|VC.js]]: segnalazione [[Speciale:Contributi/${utente}|${utente}]]`; //TODO: o tutto qui o tutto lì (inviaSegnalazione -> summary:)
// Motivazioni frequenti
const motivazioniFrequenti = [
'Reiterati vandalismi.',
'Avvisato, insiste.',
'Continua dopo il giallo.',
'Recidivo.',
'Evasione del blocco: ',
'Rimozione contenuti.',
'Utenza creata al solo scopo di vandalizzare.',
];
// Numero massimo di segnalazioni da elaborare per ricerca duplicati
var maxSegnalazioni = 5;
// Aggiunge link nella barra degli strumenti laterale
if (utente && utente!=currentUser) { // Evita di segnalare se stessi
var link = mw.util.addPortletLink(
'p-tb',
'#',
'Segnala vandalo',
't-segnalavandalo',
`Segnala ${utente} tra i vandalismi in corso`,
getAvailableAccessKey('v', 'i', 'c')); // Tasto di scelta rapida (es. Alt + Maiusc + v) e alternative in caso sia già in uso
$(link).click((event) => {
event.preventDefault();
avviaScript();
});
}
/**
* Avvia lo script.
*/
function avviaScript() {
mw.loader.using('oojs-ui-windows')
.done(() => finestraDiDialogo())
.fail(() => console.error('Impossibile avviare lo script VC.js.'));
}
/**
* Finestra di dialogo
*/
function finestraDiDialogo() {
// Subclass di ProcessDialog
function Dialog(config) {
Dialog.super.call(this, config);
}
OO.inheritClass(Dialog, OO.ui.ProcessDialog);
// Nome, titolo, action set
Dialog.static.name = 'dialogo';
Dialog.static.title = 'Nuova segnalazione';
Dialog.static.actions = [
{
flags: ['primary', 'progressive'], // Azione primaria, blu
label: 'Invia',
action: 'save',
disabled: true, // Disabilito finché non pronto //TODO: usare setAbilities()
},
{
flags: ['safe', 'close'],
label: 'Annulla',
}
];
// initialize(): aggiungo contenuto e layout
Dialog.prototype.initialize = function () {
Dialog.super.prototype.initialize.call(this);
this.panel = new OO.ui.PanelLayout({
padded: true,
expanded: false,
});
this.content = new OO.ui.FieldsetLayout();
this.vandaloInput = new OO.ui.TextInputWidget({ //this.vandaloInput = new mw.widgets.UserInputWidget({ //non funziona da mobile
readOnly: true,
});
var style = ".vertical textarea{ resize: vertical; }"; // Solo resize verticale
mw.util.addCSS(style);
this.motivoInput = new OO.ui.MultilineTextInputWidget({
placeholder: 'Descrivi brevemente il tipo di vandalismo in corso.\nLa firma è automatica.',
rows: 3,
classes: ['vertical'],
/*multiline: true,
autosize: true,
maxRows: 3,
indicator: 'required',*/
});
// Dropdown
// TODO: meglio in una funzione? https://en.wiki.x.io/w/index.php?title=MediaWiki:Gadget-defaultsummaries.js#L-66
var itemSelected; //<--- per strict
var self = this; //TODO: bind "this" to the function call instead
// https://stackoverflow.com/questions/337878/var-self-this#comment14371472_338106
// https://stackoverflow.com/a/10115970/8762088
this.dropDown = new OO.ui.DropdownWidget({
label: 'Motivazioni frequenti'
}),
// Evento che inserirà nel campo motivo l'opzione selezionata dal menu a tendina
itemSelected = function () { //TODO: inserire direttamente come anon func?
var motivazioneFrequente = self.dropDown.getMenu().findSelectedItem().getLabel();
self.motivoInput.setValue(motivazioneFrequente);
};
popolaDropdown(this.dropDown, motivazioniFrequenti);
// Fieldset
var fieldsetLayout = new OO.ui.FieldsetLayout({
items: [
new OO.ui.FieldLayout(this.vandaloInput, {
label: 'Vandalo:',
align: 'top',
}),
new OO.ui.FieldLayout(this.motivoInput, {
label: 'Motivazione:',
align: 'top',
/*$overlay: this.$overlay,
help: 'Firma aggiunta automaticamente',
helpInline: true,*/
}),
new OO.ui.FieldLayout(this.dropDown, {
align: 'inline',
}),
]
});
this.content.addItems([fieldsetLayout]);
this.panel.$element.append(this.content.$element);
/* // Disclaimer
this.content.$element.append( '<small><p>Accertarsi che sia chiaro cosa si intende per <a href="/wiki/Wikipedia:Vandalismo" title="Wikipedia:Vandalismo">vandalismo</a>, che il vandalo sia stato avvertito e che i vandalismi siano recenti.</small>' );*/
this.$body.append(this.panel.$element);
this.dropDown.getMenu().on('select', itemSelected); // Quando viene scelta un'opzione dal menu
this.motivoInput.connect(this, { 'change': 'onMotivoInputChange' }); // Alla modifica del campo motivo
};
// Impedisce motivazione vuota
Dialog.prototype.onMotivoInputChange = function (value) {
this.actions.setAbilities({
save: !!value.length
});
};
// getSetupProcess(): passa dei dati all'apertura della finestra
Dialog.prototype.getSetupProcess = function (data) {
data = data || {};
return Dialog.super.prototype.getSetupProcess.call(this, data)
.next(function () {
this.vandaloInput.setValue(data.vandalo);
this.primoTentativo = true;
segnalatoDiRecente(paginaSegnalazioni).then(result => {
result ? console.log(`${utente} già segnalato.`) : console.log(`${utente} non ancora segnalato.`);
});
}, this);
};
// getActionProcess(): gestione delle azioni (es. 'save').
Dialog.prototype.getActionProcess = function (action) {
var parent = this; // Fallback to parent handler // TODO: <-------
const url = {
vc: conf.wgArticlePath.replace('$1', paginaSegnalazioni),
contribs: conf.wgArticlePath.replace('$1', `Special:Contribs/${this.vandaloInput.getValue()}`)
};
const err = {
giaBloccato: $(`<span>${this.vandaloInput.getValue()} <a href="${url.contribs}">risulta già bloccato</a>. Inviare comunque?</span>`),
giaSegnalato: $(`<span>${this.vandaloInput.getValue()} è già stato segnalato <a href="${url.vc}#footer">di recente</a>.</span>`)
};
return Dialog.super.prototype.getActionProcess.call(this, action)
// Impedisce se segnalato di recente
.next(function () {
// Alla pressione del tasto "Invia"
if (action === 'save') {
var promise = $.Deferred();
segnalatoDiRecente(paginaSegnalazioni).then(result => {
if (result) {
promise.reject(new OO.ui.Error(err.giaSegnalato, { recoverable: false }));
} else {
promise.resolve();
}
});
return promise;
}
}, this)
// Avvisa se già bloccato
.next(function () {
if (action === 'save') {
var promise = $.Deferred();
controllaBlocco(this.vandaloInput.getValue(), blocco => {
if (blocco && this.primoTentativo) { // Mostra il warning solo la prima volta
this.primoTentativo = false;
promise.reject(new OO.ui.Error(err.giaBloccato, { warning: true }));
console.log(`Segnalazione non inviata: ${parent.vandaloInput.getValue()} già bloccato.\n` +
`Il testo era: "${parent.motivoInput.getValue()}"`);
} else {
promise.resolve();
}
});
return promise;
}
}, this)
// Invia la segnalazione
.next(function () {
if (action === 'save') {
var promise = $.Deferred();
inviaSegnalazione(parent.vandaloInput.getValue(), parent.motivoInput.getValue())
.done(() => windowManager.closeWindow(dialog))
.fail((code) => { promise.reject(new OO.ui.Error(code)); console.error(`Segnalazione non inviata: ${code}`); });
return promise;
}
}, this);
};
//if (!windowManager) { // per non perdere contenuto alla chiusura della finestra //FIXME: cleanup dopo invio
// Crea e aggiunge nuovo window manager
windowManager = new OO.ui.WindowManager();
$(document.body).append(windowManager.$element);
// Crea finestra di dialogo
dialog = new Dialog();
// Aggiunge la finestra al window manager
windowManager.addWindows([dialog]);
//}
// Apre la finestra
windowManager.openWindow(dialog, { vandalo: utente })
// Codice da eseguire dopo che la finestra è stata aperta
.opened.then(function () {
// Focus all'input per la motivazione
dialog.motivoInput.focus();
});
}
/**
* Invia la segnalazione.
*
* @param {string} utente - Vandalo da segnalare
* @param {string} motivo - Contenuto segnalazione
* @returns {object} jQuery.Promise
*/
function inviaSegnalazione(utente, motivo) {
const segnalazione = `{{vandalo|${utente}}} ${motivo} --~~\~~`;
// Data odierna, ordinale per il primo del mese
const dataOdierna = new Date()
.toLocaleDateString("it-IT", { day: 'numeric', month: 'long' })
.replace(/\b[1] /, '1º ');
// URL per link in notifica
const url = conf.wgArticlePath.replace('$1', paginaSegnalazioni);
// Per aggiungere sottosezione con data se non già esistente
return titoloUltimaSezione(paginaSegnalazioni)
.then(titoloUltimaSezione => {
return api.postWithEditToken({
action: 'edit',
title: paginaSegnalazioni,
appendtext: (dataOdierna == titoloUltimaSezione) ? ('\n\n' + segnalazione) :
('\n\n' + `=== ${dataOdierna} ===` + '\n' + segnalazione),
summary: `/* ${dataOdierna} */ ${oggetto}`
}).done(data => {
mw.notify($(`<span>${utente} è stato segnalato tra i <a href="${url}#footer">vandalismi in corso</a>.</span>`), {
title: 'Segnalato',
tag: 'notif'
});
console.log(`L'utente ha scritto "${motivo}" e ha fatto clic su "OK".`);
console.log(data);
});
});
}
/**
* Ottiene titoli e numeri sezioni.
*
* @param {string} pagina - Titolo della pagina di servizio
* @returns {object} jQuery.Promise - Sezioni
*/
function parsing(pagina) {
return api.get({
page: pagina,
action: 'parse',
prop: 'sections'
});
}
/**
* Ottiene il titolo dell'ultima sezione.
*
* @param {string} pagina - Titolo della pagina di servizio
* @returns {object} jQuery.Promise - Titolo dell'ultima sezione
*/
function titoloUltimaSezione(pagina) {
return parsing(pagina).then(data => {
return data.parse.sections[data.parse.sections.length - 1].line
// ordinale per il primo del mese
.replace(/\b[1] /, '1º ')
.replace('1° ', '1º ');
});
}
/**
* Ottiene l'ultimo numero di sezione della pagina.
*
* @returns {Promise<string>} Numero dell'ultima sezione
*/
function indexUltimaSezione(pagina) {
return parsing(pagina).then(data => data.parse.sections[data.parse.sections.length - 1].index);
}
/**
* Ottiene il wikitesto a partire dal numero della sezione.
*
* @param index Numero di sezione
* @returns {Promise<string>} wikitesto
*/
function wikitestoSezione(index, pagina) {
return api.get({
page: pagina,
action: 'parse',
prop: 'wikitext',
section: index,
formatversion: 2 //'cci tua
}).then(data => data.parse.wikitext);
//return data.parse.wikitext['*'] //https://stackoverflow.com/q/7610191/8762088; mw:API:JSON_version_2#Changes_to_JSON_output_format
}
/**
* Ottiene il wikitesto delle ultime due sezioni.
*
* @returns {Promise<array>} Array di sezioni
*/
function ultimeSezioni(pagina) {
return Promise.all([
// Wikitesto penultima sezione
indexUltimaSezione(pagina).then(index => wikitestoSezione(index - 1, pagina)),
// Wikitesto ultima sezione
indexUltimaSezione(pagina).then(index => wikitestoSezione(index, pagina))
]);
}
/**
* Controlla se il vandalo è già stato segnalato di recente.
* Solo ultime 2 sezioni e non oltre limite massimo.
* @param {string} pagina - Titolo della pagina di servizio
* @returns {Promise<boolean>} bool - Se è già stato segnalato
*/
function segnalatoDiRecente(pagina) {
return ultimeSezioni(pagina)
.then(wikitesto => {
var regex = /{{[\s\t\n\r]*vandalo[\s\t\n\r]*\|[\s\t\n\r]*([^}]*)\b/gi;
var segnalati = [];
var match = regex.exec(wikitesto);
while (match !== null) {
segnalati.push(match[1]);
match = regex.exec(wikitesto);
}
// Mi accerto che il limite massimo non superi il numero di segnalazioni
if (maxSegnalazioni > segnalati.length) {
maxSegnalazioni = segnalati.length;
}
//var segnalatiDiRecente = []
console.log(`Ultime segnalazioni: ${segnalati.slice(-maxSegnalazioni).join(", ")}.`);
for (var i = segnalati.length - maxSegnalazioni; i < segnalati.length; i++) {
// Se il vandalo è già presente tra le ultime segnalazioni
if (utente.toLowerCase() == segnalati[i].toLowerCase()) {
return true;
}
//segnalatiDiRecente.push(segnalati[i])
}
});
}
/**
* Controlla se l'utente è bloccato.
* @param {string} utente - Utente da controllare
* @param {function} blocco - Risultato
*/
function controllaBlocco(utente, blocco) {
api.get({
action: 'query',
list: 'blocks',
bkusers: utente,
bklimit: '1',
bkprop: 'by',
format: 'json'
}).done(data => {
blocco(data.query.blocks[0]);
});
}
/**
* Popola il menu a tendina.
*
* @param dropdown - Dropdown widget
* @param {string[]} opzioni - Scelte
*/
function popolaDropdown(dropdown, opzioni) {
dropdown.menu.addItems(opzioni.map((opzione) => {
return new OO.ui.MenuOptionWidget({ label: opzione });
}));
}
/**
* Controlla se un tasto di scelta rapida è disponibile.
*
* @param {string} key - Tasto da provare
* @returns {boolean} Se disponibile o meno
*/
function isAccessKeyAvailable(key) {
//$('[accesskey=v]')[0]
if (!$(`[accesskey=${key}]`)[0]) { // se non già in uso
return true;
}
}
/**
* Trova il primo tasto di scelta rapida disponibile tra quelli indicati
*
* @param {string[]} accessKeys - Tasti da provare
* @returns {string} Il primo tasto disponibile
*/
function getAvailableAccessKey(...accessKeys) {
for (var accessKey of accessKeys) {
if (isAccessKeyAvailable(accessKey)) {
return accessKey;
}
}
}
}(mediaWiki, jQuery));
// FIXME:
// Escludere se stessi può rendere difficile il debug in commons.js
// TODO:
// se già bloccato (es. allungare blocco filtro):
// chiedere conferma anche prima del clic a 'invia'
// visualizzare durata e autore blocco
// controllare che vandalo sia stato avvisato
// permettere di scegliere utente
// di selezionare più utenti
// controllare se utente esiste
// aggiungere anche mese se non presente
// motivazioni personalizzabili (tramite variabile da inserire in user script, JSON o option api)