Jump to content

User:SD0001/RFUD-helper.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by SD0001 (talk | contribs) at 13:04, 29 August 2019 (also remove image deletion (di-*) tags). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/**
 *
 * Script to easily respond to requests made at [[Wp:Requests for undeletion]]
 * (WP:RFUD). When accepting a request, the script would do the following:
 * - Post a response below the request
 * - Undelete the page
 * - Remove any deletion tags from the page
 * - Undelete the talk page if it existed
 * - If is a WT:AFC subpage, move it to draft namespace
 * - Offer to userfy the page instead of restoring to source
 *
 */

// TODO:  figure out how to make the dialog close after actions

// <nowiki>

$.when(
	mw.loader.using([ 'mediawiki.util', 'mediawiki.api', 'mediawiki.Title', 'jquery.ui.dialog', 'jquery.tipsy' ]),
	mw.loader.getScript( 'https://en.wikipedia.org/w/index.php?title=MediaWiki:Gadget-morebits.js&action=raw&ctype=text/javascript' ),
	importStylesheet( 'MediaWiki:Gadget-morebits.css' ),
	$.ready
).then(function() {


if (mw.config.get('wgPageName') !== 'Wikipedia:Requests_for_undeletion') {
	return;
}


// Until the patch to morebits is accepted, see #646
Morebits.status.actionCompleted = function(text) {
	var node = document.createElement( 'div' );
	node.appendChild( document.createElement('span') ).appendChild( document.createTextNode( text ) );
	node.className = 'tw_status_info';
	if (Morebits.status.root) {
		Morebits.status.root.appendChild( node );
	}
};


var rfud = {};
window.rfud = rfud;

rfud.advert = ' ([[User:SD0001/RFUD-helper.js|RFUD-helper]])';
Morebits.wiki.api.setApiUserAgent('[[w:User:SD0001/RFUD-helper.js|RFUD-helper]]');

var pageName;

var params = {};
rfud.params = params;

rfud.callback = function(e) {
	e.preventDefault();
	pageName = e.target.parentElement.childNodes[1].title.slice(17).replace(/_/g,' ');
	rfud.sectionName = pageName;

	var template_element = e.target.parentElement.parentElement.parentElement;
	var header_element = template_element.previousElementSibling;
	while (header_element.tagName !== 'H2') {
		header_element = header_element.previousElementSibling;
	}
	rfud.sectionName = header_element.firstChild.textContent;
	if (rfud.sectionName === '') {
		rfud.sectionName = header_element.firstChild.nextElementSibling.textContent;
	}

	var buttonResponse = e.target.classList[1].slice('rfud-'.length);

	var Window = new Morebits.simpleWindow( 700, 500 );
	Window.setTitle( "Respond to undeletion request of " + pageName );
	Window.setScriptName( "RFUD helper" );
	Window.addFooterLink( "Administrator instructions", "Wikipedia:Requests for undeletion/Administrator instructions" );
	// Window.addFooterLink( "Script documentation", "WP:RFUDHELPER" );

	var form = new Morebits.quickForm( rfud.evaluate );

	if (pageName === 'Page name goes here') {
		form.append({
			type: 'div',
			label: Morebits.htmlNode('div', 'Caution: Accepting this will undelete "Page name goes here", please fix the template usage before proceeding.', 'red')
		});
	}
	else if (pageName !== rfud.sectionName) {
		form.append({
			type: 'div',
			label: Morebits.htmlNode('div', 'Note: page name used in template is different from the section name', 'red')
		});
	}

	var field = form.append({
		type: 'field',
		label: 'Response'
	});

	field.append({
		type: 'radio',
		name: 'response',
		list: [
			{
				label: 'Accept',
				value: 'accept',
				checked: buttonResponse === 'accept'
			},
			{
				label: 'Decline',
				value: 'decline',
				checked: buttonResponse === 'decline'
			}
		],
		event: rfud.toggleResponse
	});

	form.append( {
		type: 'field',
		label: 'Work area',
		name: 'work_area'
	} );

	var previewlink = document.createElement( 'a' );
	previewlink.addEventListener('click', function() {
		rfud.preview(result);  // `result` is defined below
	});

	previewlink.style.cursor = "pointer";
	previewlink.textContent = 'Preview';
	form.append( { type: 'div', id: 'rfudpreview', label: [ previewlink ] } );
	form.append( { type: 'div', id: 'rfud-previewbox', style: 'display: none' } );

	form.append( { type:'submit' } );

	var result = form.render();
	Window.setContent(result);
	Window.display();

	result.previewer = new Morebits.wiki.preview(document.getElementById('rfud-previewbox'));

	// Init the controls
	var evt = document.createEvent( "Event" );
	evt.initEvent( 'change', true, true );
	result.response[ buttonResponse === 'accept' ? 0 : 1 ].dispatchEvent( evt );

};

rfud.toggleResponse = function(e) {
	var form = e.target.form;
	var work_area;

	if (e.target.value === 'accept') {

		work_area = new Morebits.quickForm.element({
			type: 'field',
			label: 'Accept request',
			name: 'work_area'
		});

		var permanent_section_link = 'Special:Permalink/' + mw.config.get('wgRevisionId') + '#' + rfud.sectionName;
		work_area.append({
			type: 'input',
			label: 'Undeletion summary',
			name: 'logsummary',
			tooltip: 'Summary for the deletion log',
			size: '66px',
			value: 'Undelete per [[' + permanent_section_link + '|request]] at [[WP:RFUD]]'
		});

		work_area.append({
			type: 'checkbox',
			list: [ {
				label: 'Userfy this page',
				name: 'userfy',
				value: 'userfy',
				checked: false,
				subgroup: [ {
					type: 'input',
					label: 'Userfy to: ',
					name: 'page',
					size: '60px',
					tooltip: 'User page to which the page is to be moved',
					value: 'User:USERNAME/' + pageName.replace(/^Draft:/, '')
							.replace(/^Wikipedia talk:Articles for creation\//, '')
				}, {
					type: 'checkbox',
					list: [ {
						label: 'Suppress redirect',
						name: 'sredr',
						checked: true
					} ]
				} ],
				event: function(e) {
					if (e.target.checked) {
						var userfypagefield = form['userfy.page'];
						if (userfypagefield.value.includes('USERNAME')) {
							userfypagefield.setSelectionRange('User:'.length, 'User:USERNAME'.length);
							userfypagefield.focus();
						}

						form.template.value = '{{UND|userfy}}';

						if (form.movetodraft) {
							form.movetodraft.checked = false;
							form.movetodraft.disabled = true;
						} else if (form.undeletetalk) {
							form.undeletetalk.checked = false;
							form.undeletetalk.disabled = true;
						}
					} else {
						form.template.value = '{{UND|done}}';
						if (form.movetodraft) {
							form.movetodraft.disabled = false;
						} else if (form.undeletetalk) {
							form.undeletetalk.disabled = false;
						}
					}
				}
			} ]
		});

		if (! mw.Title.newFromText(pageName).isTalkPage()) {
			work_area.append({
				type: 'checkbox',
				list: [ {
					label: 'Undelete talk page if it existed',
					name: 'undeletetalk',
					value: 'undeletetalk',
					checked: true
				} ]
			});
		}

		if (pageName.startsWith('Wikipedia talk:Articles for creation/')) {
			work_area.append({
				type: 'checkbox',
				list: [ {
					label: 'Move to draft namespace',
					name: 'movetodraft',
					value: 'movetodraft',
					checked: true
				} ]
			});
		}

	} else if (e.target.value === 'decline') {

		work_area = new Morebits.quickForm.element({
			type: 'field',
			label: 'Decline request',
			name: 'work_area'
		});

	}

	var template_responses = work_area.append({
		type: 'select',
		label: 'Select a template response: ',
		name: 'template',
		event: rfud.responseTemplateChanged
	});

	$.each(rfud.template_responses[e.target.value], function(i, el) {
		template_responses.append({
			type: 'option',
			value: i,
			label: (i ? (i + ': ') : '') + el,
			selected: /^(Done|Not done)$/.test(el)
		});
	});

	work_area.append({
		type: 'textarea',
		label: 'Comments',
		tooltip: 'Any comments in addition to the template response',
		name: 'comments'
	});


	var old_area = form.work_area;
	var result = work_area.render();
	old_area.replaceWith(result);


};

rfud.template_responses = {
	accept: {
		'' : "No template response",
		'{{UND|done}}' : "Done",
		'{{UND|prod}}' : "contested proposed deletion",
		'{{UND|g13}}' : "draft deleted under G13",
		'{{UND|afc}}' : "for G13s where {{AFC submission}} was used and the draft was submitted and declined",
		'{{UND|afc-ns}}' : "for G13s where {{AFC submission}} was used and where the draft was never submitted",
		'{{UND|g13-draft}}' : "for G13s of draft namespace pages never part of the AfC process",
		'{{UND|userfy}}' : "Userfied",
		'{{UND|userA7}}' : "Userfied A7 article, contact admin before moving back" // has subgroup

	},
	decline: {
		'' : "No template response",
		'{{UND|notdone}}' : "Not done",
		'{{UND|notdone2}}' : "Not done and will not be done",
		'{{UND|2nd}}' : "Was undeleted once, not worked on (soft-decline)",
		'{{UND|doesnotexist}}' : "No deleted page with this name",
		'{{UND|notdonecompany}}' : "Company article failing NCORP",
		'{{UND|notdoneperson}}' : "Biographical article failing NBIO",
		'{{UND|inappropriate}}': "Inappropriate topic for Wikipedia",
		'{{UND|g11}}' : "Blatant advertisement",
		'{{UND|discussion}}' : "Article deleted at a discussion", // has subgroup
		'{{UND|csd}}' : "Article deleted per CSD criteria", // has subgroup
		'{{UND|copyvio}}' : "Copyright violation" // has subgroup

	}
};

rfud.responseTemplateChanged = function(e) {
	var template = e.target.value;
	var newInput;
	if (template === '{{UND|discussion}}') {
		newInput = new Morebits.quickForm.element({
			type: 'input',
			label: 'Discussion: ',
			name: 'discussion',
			size: '70px',
			className: 'UNDtemplateinput'
		});
		newInput.append({
			type: 'input',
			label: 'Closing admin: ',
			name: 'closingadmin',
			className: 'UNDtemplateinput'
		});
	} else if (template === '{{UND|csd}}' || template === '{{UND|userA7}}') {
		newInput = new Morebits.quickForm.element({
			type: 'input',
			label: 'Deleting admin: ',
			name: 'deletingadmin',
			className: 'UNDtemplateinput'
		});
	} else if (template === '{{UND|copyvio}}') {
		newInput = new Morebits.quickForm.element({
			type: 'input',
			label: 'Copyvio source: ',
			name: 'copyviosource',
			size: '70px',
			className: 'UNDtemplateinput'
		});
	}
	if (template === '{{UND|userfy}}' || template === '{{UND|userA7}}') {
		if (e.target.form.userfy.checked === false) {
			e.target.form.userfy.click();
		}
	}
	$('.UNDtemplateinput').remove();
	if (newInput) {
		$(e.target.form).find('h5').before(newInput.render());
	}
};

rfud.getResponseWikitext = function(forPreview) {
	var templatetext = params.template.replace(/^\{\{/, '{{subst:');
	var parameterText = '';
	switch (params.template) {
		case '{{UND|discussion}}':
			parameterText = '|' + params.discussion + '|' + params.closingadmin;
			break;
		case '{{UND|csd}}':
			parameterText = '|' + params.deletingadmin;
			break;
		case '{{UND|copyvio}}':
			parameterText = '|' + params.copyviosource;
			break;
		case '{{UND|userfy}}':
			parameterText = '|' + params['userfy.page'];
			break;
		case '{{UND|userA7}}':
			parameterText = '|' + params['userfy.page'] + '|' + params.deletingadmin;
			break;
	}
	templatetext = templatetext.replace('}}', parameterText + '}}');
	if (forPreview) {
		var text = (templatetext ? (templatetext + (params.comments ? '\n\n' : '' )) : '') + params.comments + ' ~~~~';
	} else {
		var text = '\n:' + (templatetext ? (templatetext + (params.comments ? '\n\n:' : '' )) : '') + params.comments + ' ~~~~';
	}
	return text;
};

rfud.preview = function(result) {
	params = {};
	$(result).find('input, select, textarea').each(function(_i, e) {
		params[e.name] = (e.type === 'checkbox' ? e.checked : e.value);
	}); // params is not set fully correctly, but this is ok as the incorrectly set fields are not required in preview
	result.previewer.beginRender(rfud.getResponseWikitext(true), "API"); // Force wikitext
};

rfud.evaluate = function(e) {

	var form = e.target;

	params = {};
	params.response = form.response.value; // radio input
	$(form).find('input, select, textarea').each(function(_i, e) {
		if (e.type === 'radio') return true;
		if (e.disabled) return true;
		params[e.name] = (e.type === 'checkbox' ? e.checked : e.value);
	});

	if (!params.template && !params.comments) {
		alert('Please select a template response or add a comment');
		return;
	}

	if (params['userfy.page'] && params['userfy.page'].includes('USERNAME')) {
		alert('Please replace "USERNAME" with a valid username');
		return;
	}

	// disable submit of this form only, leaving any other open morebits forms untouched
	$(form).parent().parent().find('.morebits-dialog-buttons')[0].disabled = true;
	Morebits.status.init(form);

	// Never getting called for some reason, window doesn't autoclose
	Morebits.wiki.actionCompleted.event = function() {
		// Morebits.wiki.actionCompleted.notice = "Completed, closing window in a second...";
		// Morebits.status.actionCompleted( Morebits.wiki.actionCompleted.notice );
		// window.setTimeout(function() {
		// 	$(form).parent().prev().find('a').click(); // click close button
		// 	window.location.hash = rfud.sectionName;
		// }, 1000);
	};

	if (params.response === 'accept') {
		var p = new Morebits.wiki.page(pageName, 'Undeleting ' + pageName);
		p.setEditSummary(params.logsummary + rfud.advert);
		p.undeletePage(rfud.doExtras);
	} else {
		rfud.addResponse();
	}

};

rfud.doExtras = function(e) {

	var pageobj = e.parent;
	pageobj.getStatusElement().info('Done');

	// UNDELETE TALK PAGE
	if (params.undeletetalk) {
		// code adapted from [[Mediawiki:Gadget-Twinklebatchdelete.js]]
		var talkpagename = mw.Title.newFromText(pageName).getTalkPage().getPrefixedText();
		var statelem = new Morebits.status('Talk page');
		var query = {
			'action': 'query',
			'prop': 'deletedrevisions',
			'drvprop': 'ids',
			'drvlimit': 1,
			'titles': talkpagename
		};
		new Morebits.wiki.api('Checking for deleted revisions', query, function talk_page( apiobj ) {
			var xml = apiobj.responseXML;
			var exists = $(xml).find('page:not([missing])').length > 0;
			var delrevs = $(xml).find('rev').attr('revid');

			if (!delrevs) {
				statelem.info('No deleted revisions found');
				return;
			}
			if (exists) {
				statelem.update('Already exists');
				return;
			}

			statelem.unlink();
			var talkpage = new Morebits.wiki.page(apiobj.query.titles, "Talk page");
			talkpage.setEditSummary('Undeleting [[Help:Talk page|talk page]] of "' + pageName + '"' + rfud.advert);
			talkpage.undeletePage();
		}, statelem).post();
	}


	// USERFY
	if (params.userfy) {
		pageobj = new Morebits.wiki.page(pageName, 'Userfying to ' + params['userfy.page']);
		pageobj.setMoveDestination(params['userfy.page']);
		pageobj.setMoveSuppressRedirect(params.sredr);
		pageobj.setEditSummary('Userfying' + rfud.advert);
		pageobj.move(function onUserfySuccess() {
			pageobj.getStatusElement().info('Done');
			rfud.removeDeletionTags();
		}, function onUserfyFailure(e) {
			var statelem = pageobj.getStatusElement();
			if (e.errorCode === 'articleexists') {
				Morebits.wiki.numberOfActionsLeft++; // hold dialog from closing //
				var skip_button = Morebits.htmlNode('button', 'Skip move');
				skip_button.addEventListener('click', function() {
					Morebits.wiki.numberOfActionsLeft--; // allow dialog to close //
					statelem.warn('skipped following name conflict');
				});
				var newUserfy = $('<input>').attr('type', 'text')[0];
				var new_button = $('<button>').text('OK').click(function() {
					Morebits.wiki.numberOfActionsLeft--;
					var newTitle = newUserfy.value;
					pageobj.setMoveDestination(newTitle);
					pageobj.setMoveSuppressRedirect(params.sredr);
					pageobj.move(function() {
						statelem.info(['userfied to ' + newTitle + ' instead. ',
							Morebits.htmlNode('span', 'Please update your response to reflect the new page name', 'red')]);
					});
				});
				statelem.warn([ 'A user page of that name already exists, enter new user page name or skip ',
					newUserfy, new_button[0], skip_button ]);
			}
		});
	}


	// MOVE WT:AFC PAGES TO DRAFT: SPACE
	if (params.movetodraft) {
		pageobj = new Morebits.wiki.page(pageName, 'Moving page to draft namespace');
		pageobj.setMoveDestination(pageName.replace('Wikipedia talk:Articles for creation/', 'Draft:'));
		pageobj.setEditSummary('Moving to new location for drafts' + rfud.advert);
		pageobj.move(function onAfcMoveSuccess() {
			pageobj.getStatusElement().info('Done');
			rfud.removeDeletionTags();
		}, function onAfcMoveFailure(e) {
			if (e.errorCode === 'articleexists') {
				pageobj.getStatusElement().warn('A draft page of that name already exists, skipping...');
				Morebits.wiki.numberOfActionsLeft++; // prevent dialog from closing
				Morebits.status.actionCompleted('Completed'); // ?
			}
		});
	}


	// REMOVE DELETION TAGS
	if (!params.userfy && !params.movetodraft) {
		rfud.removeDeletionTags();
	}


	// ADD RESPONSE AT WP:RFUD
	rfud.addResponse();

};

rfud.removeDeletionTags = function() {
	var currentPageName = pageName;
	if (params.userfy) {
		currentPageName = params['userfy.page'];
	} else if (params.movetodraft) {
		currentPageName = pageName.replace('Wikipedia talk:Articles for creation/', 'Draft:');
	}
	var pageobj = new Morebits.wiki.page(currentPageName, 'Removing deletion tags from page if any');

	pageobj.load(function(pageobj) {
		var pageText = pageobj.getPageText();
		var statelem = pageobj.getStatusElement();

		var newPageText = pageText.replace(
			// Regex adapted from [[Mediawiki:Gadget-friendlytag.js]]
			new RegExp(
				// leading whitespace
				'^\\s*' +
				// capture template(s)
				'(?:((?:\\s*' +
				// AfD
				'(?:<!--.*AfD.*\\n\\{\\{(?:Article for deletion\\/dated|AfDM).*\\}\\}\\n<!--.*(?:\\n<!--.*)?AfD.*(?:\\s*\\n))?|' + // trailing whitespace/newline needed since this subst's a newline
				// CFD
				'<!-- BEGIN CFD TEMPLATE -->[\\s\\S]*?<!-- END CFD TEMPLATE -->|' +
				// begin template format
				'\\{\\{\\s*(?:' +
					// CSD
					'db|delete|db-.*?|speedy deletion-.*?|di-.*?|' +
					// PROD
					'(proposed deletion|prod blp|book-prod)\\/dated(?:\\s+\\|(?:concern|user|timestamp|help).*)+|' +
					// sometimes under a CSD or AfD
					'|salt|proposed deletion endorsed' +
					// MFD/FFD
					'|mfd|ffd' +
				// end main template name
				')\\s*' +
				// template parameters
				'(\\|(?:\\{\\{[^{}]*\\}\\}|[^{}])*)?' +
				// end template format
				'\\}\\})+' +
				// end capture
				'(?:\\s*\\n)?)' +
				// trailing whitespace
				'\\s*)?',
			'i'), ''
		);

		var changed = false;

		// RFD
		if (/#invoke:RfD/.test(newPageText)) {
			newPageText = newPageText.replace(/\{\{.*?#invoke:RfD[\s\S]*?\|content=\n([\s\S]*?)<!-- Don.*?-->\n?\}\}/, '$1');
			changed = true;
		}

		// TFD
		else if (/Template for discussion\/dated/.test(newPageText)) {
			newPageText = newPageText.replace(/\{\{Template for discussion\/dated.*?(\{\{#invoke:Noinclude.*?\}\})?\}\}/, '');
			changed = true;
		}

		if (changed || newPageText !== pageText) {
			pageobj.setPageText(newPageText);
			pageobj.setEditSummary('Removing deletion tags after undeletion' + rfud.advert);
			pageobj.save();
		} else {
			statelem.info('None found');
		}

	});
};

rfud.addResponse = function rfudAddResponse() {

	var sectionNumber;

	// Get section number by scraping the html of the TOC
	var sectionFound = false;
	$('.toctext').each(function(_i, e) {
		if ( Morebits.string.toUpperCaseFirstChar(e.textContent.replace(/_/g,' ')) === rfud.sectionName ) {
			sectionNumber = parseInt(e.previousElementSibling.textContent);
			sectionFound = true;
			return false; // break
		}
	});
	if (!sectionFound) {
		new Morebits.status('Saving response').warn('Unable to find section.');
		Morebits.status.printUserText(rfud.getResponseWikitext(true), "Your response is provided below, which you may save manually:");
		Morebits.wiki.numberOfActionsLeft++; // prevent dialog from closing //
		return;
	}

	var appendtext = rfud.getResponseWikitext();
	var editsummary = '/* ' + rfud.sectionName + ' */ ' + (params.response === 'accept' ? 'Accepting' : 'Declining') + ' request' + rfud.advert;

	var rfudpage = new Morebits.wiki.page('Wikipedia:Requests for undeletion', 'Saving response');
	rfudpage.setPageSection(sectionNumber);
	rfudpage.setAppendText(appendtext);
	rfudpage.setEditSummary(editsummary);
	rfudpage.setMaxConflictRetries(4);  // Account for admins responding to multiple requests simultaneously
	rfudpage.append(function(pageobj) {
		// Reload the section html
		var statelem = pageobj.getStatusElement();
		var reloadApi = new Morebits.wiki.api('Done, reloading section', {
			action: 'parse',
			page: 'Wikipedia:Requests for undeletion',
			section: sectionNumber,
		}, function onReloadSuccess(apiobj) {
			var html = $(apiobj.responseXML).find('text').text().replace(/&lt;/g, '<').replace(/&gt;/g, '>');
			var header = $($('.mw-headline')[sectionNumber - 1].parentElement);
			header.nextUntil('h2').remove();
			header.replaceWith($(html));
			statelem.info('Done, section reloaded');
		}, statelem);
		reloadApi.post();
	});
};

// Add Accept/Decline button
$('li .sysop-show a').each(function(_i, e) {

	var rfudhelperlink_accept = Morebits.htmlNode('a', 'Accept');
	rfudhelperlink_accept.href = '#';
	rfudhelperlink_accept.className = 'rfud-helper-button rfud-accept';
	rfudhelperlink_accept.addEventListener('click', rfud.callback);

	var rfudhelperlink_decline = Morebits.htmlNode('a', 'Decline');
	rfudhelperlink_decline.href = '#';
	rfudhelperlink_decline.className = 'rfud-helper-button rfud-decline';
	rfudhelperlink_decline.addEventListener('click', rfud.callback);

	e.after(document.createTextNode(' | '), rfudhelperlink_accept,
		document.createTextNode(' | '), rfudhelperlink_decline);
});


});
// </nowiki>