MediaWiki:Gadget-tutoring-enrollment.js

Nota: Depois de publicar, poderá ter de contornar a cache do seu navegador para ver as alterações.

  • Firefox / Safari: Pressione Shift enquanto clica Recarregar, ou pressione Ctrl-F5 ou Ctrl-R (⌘-R no Mac)
  • Google Chrome: Pressione Ctrl-Shift-R (⌘-Shift-R no Mac)
  • Internet Explorer / Edge: Pressione Ctrl enquanto clica Recarregar, ou pressione Ctrl-F5
  • Opera: Pressione Ctrl-F5.
/**
 * Gadget para inscrição no programa de tutoria
 *
 * @author [[w:pt:User:Diego Queiroz]]
 * @date 31/out/2014
 */
/* global mediaWiki, jQuery */

( function ( mw, $ ) {

	// semáforo que mantém o número de chamadas assíncronas pendentes
	var async_pending = 0,

		// flags de depuração
		permitir_inscricao_tutores = false, // (padrão: false)

		pagina_tutoria = 'Wikipédia:Tutoria',
		pagina_inscricao = pagina_tutoria + '/Inscritos',
		pagina_adotados = pagina_tutoria + '/Adotados',
		template_adocao_padrao = pagina_tutoria + '/Adoção padrão',
		editnotice_adocao = pagina_tutoria + '/Editnotice adoção',
		editnotice_inscricao = pagina_tutoria + '/Editnotice inscrição',
		editnotice_inscricaoIP = pagina_tutoria + '/Editnotice inscriçãoIP',
		editnotice_inscricaoTutor = pagina_tutoria + '/Editnotice inscriçãoTutor',
		editnotice_fimTutoria = pagina_tutoria + '/Editnotice fimTutoria',

		predefinicao_novato = 'Novo editor', // esse nome é utilizado em um regex, evitar caracteres "complicados"

		// strings a serem formatadas
		// campos:
		//  * {0} -> novato
		//  * {1} -> tutor
		titulo_adocao = 'Adoção de "{0}" no programa de tutoria',
		resumo_adocao = 'Adoção de [[Usuário(a):{0}|{0}]] por [[Usuário(a):{1}|{1}]]',
		titulo_fim_tutoria = 'Encerramento da tutoria de "{0}"',
		resumo_fim_tutoria = 'Encerramento da tutoria de [[Usuário(a):{0}|{0}]] por [[Usuário(a):{1}|{1}]]',
		titulo_inscricao = 'Inscrição no programa de tutoria',

		novo_titulo,
		namespace = mw.config.get( 'wgNamespaceNumber' ),
		usuario   = mw.config.get( 'wgUserName' ),
		novato;

	function waitForAsync( done ) {
		if ( async_pending > 0 ) {
			// existem tarefas pendentes, aguarda 100 ms e checa novamente
			setTimeout(function () {
				waitForAsync(done);
			}, 100);
		} else {
			// nenhuma tarefa pendente, executa o código
			done && done();
		}
	}

	function formatStr(str, obj) {
		// função adaptada de http://stackoverflow.com/questions/2534803/string-format-in-javascript#2534828
		return str.replace(/\{([^}\s]+)\}/g, function (m, p1) {
			return obj[p1];
		});
	}

	function getWikiText( title, ok, err ) {
		async_pending++;
		(new mw.Api()).get( {
			prop: 'revisions',
			rvprop: 'content',
			rvlimit: 1,
			indexpageids: true,
			titles: title
		} )
		.done( function ( data ) {
			var q = data.query,
			id = q && q.pageids && q.pageids[ 0 ],
			pg = id && q.pages && q.pages[ id ],
			rv = pg && pg.revisions;

			if ( rv && rv[ 0 ] && rv[ 0 ][ '*' ] ) {
			ok && ok( rv[ 0 ][ '*' ] );
			}
			async_pending--;
		} )
		.fail( function () {
			err && err();
			async_pending--;
		});
	}

	function editPage( title, text, summary, ok, err ) {
		async_pending++;
		(new mw.Api()).post( {
			action: 'edit',
			title: title,
			text: text,
			summary: summary,
			token: mw.user.tokens.get( 'csrfToken' )
		} )
		.done( function () {
			ok && ok();
			async_pending--;
		})
		.fail( function () {
			err && err();
			async_pending--;
		});
	}

	function remove_inscricao(novato, tutor, pagina, resumo) {
		getWikiText(pagina, function (txt) {
			// ajusta o nome do usuário para ser incorporado ao regex (escapa caracteres "complicados")
			novato = novato.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');

			var pattern = new RegExp('{{' + predefinicao_novato + ' *[|] *' + novato + ' *[|].*?}}\n?\n?', 'g');
			txt = txt.replace(pattern, '');

			editPage(pagina, txt, formatStr(resumo, [novato, tutor]) );
		});
	}

	function inserir_adocao(novato, tutor, pagina, resumo) {
		var conteudo = formatStr('\n{{subst:' + template_adocao_padrao + '|{0}|{1}}}', [novato, tutor]);

		// FIXME: se houver erro na execução do script (ex. problemas na conexão)
		//        o conteúdo é inserido duas vezes
		// Solução: Verificar antes se a adoção já não foi inserida

		async_pending++;
		(new mw.Api()).post( {
			action: 'edit',
			minor: false,
			title: pagina,
			appendtext: conteudo,
			summary: formatStr(resumo, [novato, tutor]),
			token: mw.user.tokens.get( 'csrfToken' )
		} )
		.done( function () {
			// ok && ok();
			async_pending--;
		} )
		.fail( function () {
			// err && err();
			async_pending--;
		} );
	}

	function insere_conteudo(pagina, elemento) {
		async_pending++;
		(new mw.Api()).get( {
			action: 'parse',
			page: pagina
		})
		.done( function (data) {
			var conteudoHTML = data.parse.text['*'];
			$(elemento).prepend(conteudoHTML);

			async_pending--;
		} )
		.fail( function () {
			// err && err();
			async_pending--;
		});
	}

	function check_permission(user, permission, ifTrue, ifFalse, alwaysFalse) {
		// parâmetro "alwaysFalse" é opcional e é utilizado para depuração somente
		// força que "ifFalse" seja sempre executado
		// independente das permissões do usuário

		ifTrue = ifTrue || function () {};
		ifFalse = ifFalse || function () {};

		async_pending++;
		(new mw.Api()).get( {
			action: 'query',
			list: 'users',
			usprop: 'rights',
			continue: '',
			ususers: user
		})
		.done( function (data) {
			var permissions = data.query.users[0].rights;
			if ( !alwaysFalse && $.inArray(permission, permissions) != -1 ) {
				ifTrue(permissions);
			} else {
				ifFalse(permissions);
			}
			async_pending--;
		} )
		.fail(function () {
			// err && err();
			async_pending--;
		});
	}

	function getParameterByName(name) {
		name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
		var regex = new RegExp('[\\?&]' + name + '=([^&#]*)'),
			results = regex.exec(location.search);
		return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
	}

	if (namespace == 3) { // namespace "User talk:"

		// Nome do novato é o título da página
		novato = mw.config.get( 'wgTitle' );

		// parâmetro "&tutoring=end" passado na URL para diferenciar a incrição da finalização da tutoria
		if ( getParameterByName('tutoring') != 'end' ) {
			// Tutor está ADOTANDO um novato
			novo_titulo = formatStr(titulo_adocao, [novato, usuario]);

			// marca a caixa de seleção "Vigiar"
			$( '#wpWatchthis' ).prop( 'checked', true );

			// altera o texto do botão de salvar
			$( '#wpSave' ).val( 'Confirmar adoção' );

			// insere conteúdo da página de adoção
			insere_conteudo(editnotice_adocao, '#mw-content-text');

			// faz com que o botão salvar também exclua o usuário adotado da
			// lista de inscritos
			$( '#wpSave' ).click( function (e) {
				remove_inscricao(novato, usuario, pagina_inscricao, resumo_adocao);
				inserir_adocao(novato, usuario, pagina_adotados, resumo_adocao);

				// sem as linhas a seguir, não funciona às vezes
				// evita que a página seja salva antes de gravar as alterações assíncronas
				e.preventDefault();
				$(this)
					.off(e)
					.prop('disabled', true);
				var thisSelector = '#' + this.id;
				waitForAsync( function () {
					$(thisSelector).prop('disabled', false);
					// código sequenciado por "setTimeout" devido a
					// http://bugs.jquery.com/ticket/14892
					setTimeout(function () {
						$(thisSelector).click();
						setTimeout(function () {
							$(thisSelector).prop('disabled', true);
						}, 10);
					}, 10);
				} );
				//
			} );
		} else {
			// Tutor ENCERRANDO a tutoria
			novo_titulo = formatStr(titulo_fim_tutoria, [novato, usuario]);

			// altera o texto do botão de salvar
			$( '#wpSave' ).val( 'Confirmar encerramento' );

			// insere conteúdo do editnotice de fim de tutoria
			insere_conteudo(editnotice_fimTutoria, '#mw-content-text');

			$( '#wpSave' ).click( function (e) {
				remove_inscricao(novato, usuario, pagina_adotados, resumo_fim_tutoria);

				// colocar em algum lugar os Ex-tutorados?? (lista de ex-tutorados?)
				// * inserir_adocao(novato, usuario, <<PAGINA>>, <<RESUMO>> );

				// sem as linhas a seguir, não funciona às vezes
				// evita que a página seja salva antes de gravar as alterações assíncronas
				e.preventDefault();
				$(this)
					.off(e)
					.prop('disabled', true);
				var thisSelector = '#' + this.id;
				waitForAsync( function () {
					$(thisSelector).prop('disabled', false);
					// código sequenciado por "setTimeout" devido a
					// http://bugs.jquery.com/ticket/14892
					setTimeout(function () {
						$(thisSelector).click();
						setTimeout(function () {
							$(thisSelector).prop('disabled', true);
						}, 10);
					}, 10);
				} );
				//
			} );

			// altera classe dos botões
			$( '#wpSave' ).addClass( 'mw-ui-button mw-ui-destructive' );
		}

	} else {
		// esta parte do código é executado quando um usuário registrado
		// está se INSCREVENDO na tutoria
		novo_titulo = formatStr(titulo_inscricao, [usuario, '']);

		// altera o texto do botão de salvar
		$( '#wpSave' ).val( 'Confirmar inscrição' );

		if ( usuario === null ) {
			// IP tentando se inscrever na tutoria
			// não permitir a inscrição e inserir mensagem personalizada

			// desativa botão salvar
			$('#wpSave').prop('disabled', true);

			// insere conteúdo para usuários IPs
			insere_conteudo(editnotice_inscricaoIP, '#mw-content-text');
		} else {
			// usuário registrado tentando se inscrever na tutoria

			// determina o conteúdo a ser exibido de acordo com as permissões do usuário
			check_permission(usuario, 'autoreviewer', function () {
				// usuário é experiente (possui permissão de autorrevisor)

				// *** observar que é verificada a PERMISSÃO e não o GRUPO ***
				// *** logo, sysops e eliminadores possuem a permissão     ***

				// desativa botão salvar para impedir que tutores se inscrevam
				$('#wpSave').prop('disabled', true);

				// insere conteúdo para usuários experientes
				insere_conteudo(editnotice_inscricaoTutor, '#mw-content-text');

			}, function () {
				// usuário é novato

				// desmarca a caixa de seleção "Vigiar"
				$( '#wpWatchthis' ).prop( 'checked', false );

				// insere conteúdo para usuários novatos
				insere_conteudo(editnotice_inscricao, '#mw-content-text');
			}, permitir_inscricao_tutores); // força a execução do primeiro bloco se essa flag for "true"
		}
	}

	// altera o comportamento do botão cancelar
	$( '#mw-editform-cancel' ).click( function (e) {
		e.preventDefault();
		history.back();
	} );

	// altera o título da página e da aba no navegador
	$( '#firstHeading' ).children( 'span' ).text( novo_titulo );
	document.title = novo_titulo + document.title.replace(/[^–]*/, ' ');

	// altera a propriedade "title" do botão salvar
	$('#wpSave').prop('title',  $('#wpSave').val() + ' [alt-shift-s]');

} ( mediaWiki, jQuery ) );