MediaWiki:Gadget-RfDHelper.js

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/*!
 * Some enhancement on RfD that is enabled by default just for admins
 * Originally was developed inside Merge.js by me but I decided to split it
 * It will add a quick delete button near each header that is requested by merge.js
 * and will add a number near links button so admins can quickly detect an item has
 * not any back link then safely delete it. It also will check recent delete
 * log every 5 seconds and will make deleted item links on RfD red link
 * to prevent conflicts on deletion.
 * 
 * @author User:Ebraminio <ebrahim -at- gnu.org>
 * @license CC-Zero
 */

/*jslint regexp: true, unparam: true, browser: true, white: true*/
/*jshint unused: false*/
/*global mediaWiki, jQuery*/
//<nowiki>
(function ($, mw) {
	'use strict';

	if ( !( mw.config.get('wgPageName').indexOf('Wikidata:Requests_for_deletions') === 0
			&& mw.config.get('wgAction') === 'view' )
	) {
		return;
	}

	var api = new mw.Api();

	/**
	 * Get backlink (50 limited)
	 */
	function getBackLinks(title) {
		return api.get({
			action: 'query',
			list: 'backlinks',
			bltitle: title,
			blnamespace: [0, 120, 146],
			bllimit: 50
		}).then(function (data) {
			return data.query.backlinks.map(function (value) {
				return value.title;
			});
		});
	}

	/**
	 * Delete an item
	 */
	function deleteItem(id, reason) {
		return api.postWithToken('csrf', {
			action: 'delete',
			title: id,
			reason: reason
		});
	}

	/**
	 * Create a redirect from one item to another one.
	 */
	function createRedirect(fromId, toId) {
		return api.postWithToken('csrf', {
			action: 'wbcreateredirect',
			from: fromId,
			to: toId
		});
	}

	/**
	 * Fetch a page
	 */
	function loadPage(title) {
		return api.get({
			action: 'query',
			prop: 'revisions',
			titles: title,
			rvprop: 'content'
		}).then(function (data) {
			return $.map(data.query.pages, function (value) { return value; })[0].revisions[0]['*'];
		});
	}

	/**
	 * Save a page
	 */
	function savePage(title, text, summary) {
		return api.postWithToken('csrf', {
			action: 'edit',
			title: title,
			text: text,
			summary: summary,
			nocreate: '',
			minor: ''
		}).then(function (data) {
			if (data.error && data.error.info) {
				mw.notify(data.error.info);
			}
		}, function (data) {
			mw.notify(data);
		});
	}

	/**
	 * Request move backlinks
	 */
	function requestBacklinksMove(from, to, user, reason) {
		return loadPage('User:BeneBot*/movelinks.js').then(function (x) {
			return savePage(
				'User:BeneBot*/movelinks.js',
				x.replace('<!--{begin}-->', '<!--{begin}-->\n{{/move|from=' + from + '|to=' + to + '|user=' + user +	'|reason=' + reason + '}}'),
				'Add a request via [[MediaWiki:Gadget-RfDHelper.js|RfDHelper]]'
			);
		});
	}

	/**
	 * Get recent deletions
	 */
	function recentDeletions() {
		return api.get({
			action: 'query',
			list: 'logevents',
			letype: 'delete',
			leaction: 'delete/delete',
			leprop: 'title'
		});
	}

	$.when(
		$.ready,
		mw.loader.using('mediawiki.language')
	).then(function () {
		var itemLinks = {};
		$('a[href^="/wiki/Q"]:not(.new)').each(function (i, x) {
			itemLinks[x.href.replace(/.*\//, '')] = x;
		});

		setInterval(function () {
			recentDeletions().then(function (data) {
				Object.values( data.query.logevents ).forEach( function ( entry ) {
					if ( entry.ns !== 0 ) return;
					$( itemLinks[ entry.title ] ).addClass('new');
					itemLinks[ entry.title ] = undefined;
				} );
			});
		}, 5000);

		$("#mw-content-text .rfd-links").each(function (i, el) {
			var link = $( el ).find('a:first'),
				deleteQuery = new mw.Uri(link.prop('href')).query,
				header,
				buttonSize = window.devicePixelRatio !== undefined ? Math.floor(window.devicePixelRatio) * 17 : 17;
			if (deleteQuery.action !== 'delete') {
				return;
			}

			header = $( el ).parent().parent().prev().find('.mw-headline a');

			// add backlinks count
			getBackLinks(deleteQuery.title).then(function (backlinks) {
				var blcount = backlinks.length;
				if (blcount === 50) {
					blcount = '50+';
				}

				$('[href*="Special:WhatLinksHere"]', el).after(
					$('<span>')
						.css('color', blcount === 0 ? 'black' : 'red')
						.text(' (' + mw.language.convertNumber(blcount) + ')'),
					'' /* blcount === 0 ? '' : $('<button>', {
							title: 'Request backlinks move',
							style: 'cursor: pointer; padding: 0 1em;'
						}).append($('<img>', {
							src: '//upload.wikimedia.org/wikipedia/commons/thumb/f/f4/Symbol_userfy_vote.svg/' + buttonSize + 'px-Symbol_userfy_vote.svg.png',
							width: buttonSize,
							height: buttonSize
						})).click(function () {
							var that = $(this);
							that.text('wait...').attr('disabled', 'disabled');
							requestBacklinksMove(deleteQuery.title, deleteQuery.wpReason.match(/\[\[(Q\d+)\]\]/)[1], mw.config.get( 'wgUserName' ), 'merged').then(function () {
								that.replaceWith($('<span>', { html: ' <big style="color:#690; font-size:1.4em;">&#x2713;</big> ' }));
							});
						})*/
				);
			});

			// if admin has not `var rfdHero = true` on her/his common.js,
			// don't add quick delete button
			if (window.rfdHero !== true) {
				return;
			}

			// puts last edit link and history merge destination
			link.parent().parent().next().find('a[href^="/wiki/Q"]').after(
				$('<span>', { style: 'font-size: x-small' }).append(
					' (',
					$('<a>', {
						target: '_blank',
						text: 'history',
						href: '/wiki/' + deleteQuery.wpReason.match(/\[\[(Q\d+)\]\]/)[1] + '?action=history'
					}),
					' | ',
					$('<a>', {
						target: '_blank',
						text: 'last edit',
						href: '/wiki/' + deleteQuery.wpReason.match(/\[\[(Q\d+)\]\]/)[1] + '?diff=cur&oldid=prev#mw-content-text'
					}),
					') '
				)
			);

			// puts last edit link on the merged from item also
			link.next().after(' | ', $('<a>', {
				href: '/wiki/' + deleteQuery.title + '?diff=cur&oldid=prev#mw-content-text',
				text: 'last edit'
			}));

			// don't add buttons if is deleted already
			if (header.hasClass('new') || header.hasClass('mw-redirect')) {
				return;
			}

			var $deleteBtn, $redirectBtn;
			header.after($deleteBtn = $('<button>', {
				title: 'Quick Delete',
				style: 'cursor: pointer; margin: 0 1em; padding: 0 1em;'
			}).append($('<img>', {
				src: '//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Symbol_delete_vote.svg/' + buttonSize + 'px-Symbol_delete_vote.svg.png',
				width: buttonSize,
				height: buttonSize
			})).click(function (x) {
				header.css('text-decoration', 'line-through');
				deleteItem(deleteQuery.title, deleteQuery.wpReason).then(function () {
					header.addClass('new');
				});
				// delete buttons after request
				$deleteBtn.remove();
				$redirectBtn.remove();
			}));
			header.after($redirectBtn = $('<button>', {
				title: 'Create redirect',
				style: 'curser: pointer; margin: 0 1em; padding: 0 1em;'
			}).append($('<img>', {
				src: '//upload.wikimedia.org/wikipedia/commons/thumb/8/89/Symbol_redirect_vote2.svg/' + buttonSize + 'px-Symbol_redirect_vote2.svg.png',
				width: buttonSize,
				height: buttonSize
			})).click(function (x) {
				header.css('font-style', 'italic');
				createRedirect(deleteQuery.title, deleteQuery.wpReason.match(/\[\[(Q\d+)\]\]/)[1]);
				// delete buttons after request
				$deleteBtn.remove();
				$redirectBtn.remove();
			}));
		});
	});

}(jQuery, mediaWiki));
//</nowiki>