Misplaced Pages

User:Jackmcbarn/editProtectedHelper.js

Article snapshot taken from Wikipedia with creative commons attribution-sharealike license. Give it a read and then ask your questions in the chat. We can research this topic together.
< User:Jackmcbarn

This is an old revision of this page, as edited by Jackmcbarn (talk | contribs) at 23:49, 15 December 2020 (replace deprecated API). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

Revision as of 23:49, 15 December 2020 by Jackmcbarn (talk | contribs) (replace deprecated API)(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump.
This code will be executed when previewing this page.
This user script seems to have a documentation page at User:Jackmcbarn/editProtectedHelper.
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 Misplaced Pages:Bypass your cache.
// <nowiki>
var browserHasDataCorruptionBug = $('<div style="color:#ffd"></div>').outerHTML != '<div style="color:#ffd"></div>';
if(browserHasDataCorruptionBug && ('undefined' === typeof ephAllowDataCorruptionBug || !ephAllowDataCorruptionBug)) {
	$(document).ready(function() {
		$('.editrequest .mbox-text').append('<small>The editProtectedHelper script is currently disabled because your browser has a bug which will cause corruption of pages that it edits with Parsoid. See <a href="https://www.mediawiki.org/User:Catrope/IE_bugs#style_attributes_are_aggressively_normalized.2C_causing_data_loss">here</a> for details. To override this, add "ephAllowDataCorruptionBug = true;" immediately above the line where you load this script. You are responsible for any damage to pages that this causes.</small>');
	});
// only enable this on the latest revision of the page
} else if(mw.config.get('wgRevisionId') == mw.config.get('wgCurRevisionId')) {
	$(document).ready(function() {
		mw.loader.using( , function() {
			'use strict';
			var templateResponses = [
				,
				,
				,
				,
				,
				 , // TODO make dynamic
				,
				,
				, // TODO make dynamic
				,
				,
				,
				,
				,
				,
				,
				,
				,
				, // TODO make dynamic
				,
				,
				,
				,
				
			];
			var selector = $('.editrequest .mbox-text');

			// Global variable. In onParsoidDomReceived, this is set
			// to the ETag header so that we can pass it back later 
			// in convertModifiedDom.
			var gEtag;

			// Global variable. In onParsoidDomReceived, this is
			// set to the string we get back from the Parsoid API.
			// Used in convertModifiedDom.
			var parsoidDom;

			var selectedLevel = { semi: , extended: , template: , full: , interface:  };
			var templateLevel = { semi: 'semi-', extended: 'extended-', template: 'template-', full: 'fully-', interface: 'interface-' };
			var responseLevel = { semi: '{' + '{subst:ESp|', extended: '{' + '{subst:EEp|', template: '{' + '{subst:ETp|', full: '{' + '{subst:EP|', interface: '{' + '{subst:EIp|' };
			var warnOnRespond = false, warnOnQuickRespond = true, warnOnRemove = true, autoFixLevel = true;
			var quickResponses = [
				,
				,
				,
				,
				,
				
			];
			if('undefined' !== typeof ephWarnOnRespond) {
				warnOnRespond = ephWarnOnRespond;
			}
			if('undefined' !== typeof ephWarnOnQuickRespond) {
				warnOnQuickRespond = ephWarnOnQuickRespond;
			}
			if('undefined' !== typeof ephWarnOnRemove) {
				warnOnRemove = ephWarnOnRemove;
			}
			if('undefined' !== typeof ephAutoFixLevel) {
				autoFixLevel = ephAutoFixLevel;
			}
			if('undefined' !== typeof ephQuickResponses) {
				quickResponses = ephQuickResponses;
			}
			function yesno(val, def) {
				if(typeof val === 'string') {
					val = val.toLowerCase();
				}
				if(typeof val === 'undefined' || val === '') {
					return undefined;
				} else if(val === 'yes' || val === 'y' || val === 'true' || val === 1) {
					return true;
				} else if(val === 'no' || val === 'n' || val === 'false' || val === 0) {
					return false;
				}
				return def;
			}
			function getBanner(level, pagename, answered, force, demo) {
				return '{{edit ' + templateLevel + 'protected' + (pagename !== '' ? '|' + pagename : '') + '|answered=' + (answered ? 'yes' : 'no') + (force ? '|force=yes' : '') + (demo ? '|demo=yes}}' : '}}');
			}
			function getResponse(level, template, free) {
				return ':' + (template === '' ? '' : responseLevel + template + '}} ') + (free === '' ? '' : free + ' ') + '~~' + "~~\n";
			}
			function makeUniqueString(index) {
				// this looks like a strip marker on purpose
				return "\x7fUNIQ" + Math.random().toString(16).substr(2) + '-editProtectedHelper-' + index + "-QINU\x7f";
			}
			function saveWikitextFromParsoid(parsoidObj, data) {
				var newWikitext, tmp, removerequest = false;
				if(parsoidObj.removerequest) {
					tmp = data.split(parsoidObj.templateMarker);
					removerequest = true;
					newWikitext = tmp;
					if(parsoidObj.respondedInPage) {
						tmp = tmp.split(parsoidObj.responseMarker);
						newWikitext = newWikitext.replace(/\n+$/, '') + "\n\n" + tmp.replace(/^\n+/, '');
					}
				} else {
					var response = getResponse(parsoidObj.form.level.value, parsoidObj.form.responsetemplate.value, parsoidObj.form.responsefree.value);
					var banner = getBanner(parsoidObj.form.level.value, parsoidObj.form.pagetoedit.value, parsoidObj.form.answered.checked, parsoidObj.form.force.checked, parsoidObj.demo);
					// prevent empty response
					if(response == ':~~' + "~~\n") {
						response = '';
					}
					tmp = data.split(parsoidObj.templateMarker);
					newWikitext = tmp.replace(/\n*$/, tmp.trim().length ? "\n\n" : '') + banner + tmp.replace(/^\n*/, "\n");
					if(parsoidObj.respondedInPage) {
						tmp = newWikitext.split(parsoidObj.responseMarker);
						newWikitext = tmp.replace(/\n*$/, "\n") + response + tmp.replace(/^\n*/, "\n");
					} else {
						newWikitext = newWikitext.replace(/\n*$/, "\n") + response;
					}
				}
				var resultObj = parsoidObj.resultObj, sectionName = parsoidObj.section && parsoidObj.section.text().trim();
				if(sectionName && (sectionName.indexOf(parsoidObj.templateMarker) !== -1 || sectionName.indexOf(parsoidObj.responseMarker) !== -1)) {
					// someone put an edit request template inside the section header, or something like that.
					// don't even try to include a working section link in that case
					sectionName = null;
				}
				//console.log(newWikitext);
				resultObj.text('Saving...');
				debugger; // triggers only if debugger is active
				new mw.Api().get( { action: 'query', prop: 'revisions', rvprop: 'timestamp', revids: mw.config.get('wgRevisionId') }).done(function(data) {
					new mw.Api().postWithEditToken( { action: 'edit', pageid: mw.config.get('wgArticleId'), text: newWikitext, summary: (sectionName ? '/' + '* ' + sectionName + ' *' + '/ ' : '') + (removerequest ? 'Removed' : 'Responded to') + ' edit request', tags: 'editProtectedHelper', notminor: true, nocreate: true, basetimestamp: data.query.pages.revisions.timestamp } ).done(function(result) {
						if(typeof(result.edit.newrevid) === 'undefined' || typeof(result.edit.oldrevid) === 'undefined') {
							resultObj.css('color', 'red').text("Error: The API response omitted required information. Please check the page's history manually to see whether your edit was saved properly. The console may contain details.");
						} else {
							resultObj.css('color', 'green').text('Success: Loading diff...');
							if( window.editProtectedHelperReloadAfter ) {
								window.location.reload( /* forcedReload */ true );
							} else {
								location.href = mw.config.get('wgScript') + '?diff=' + result.edit.newrevid + '&oldid=' + result.edit.oldrevid;
							}
						}
					}).fail(function(err) {
						resultObj.css('color', 'red').text('Error: ' + err + '. The console may contain details.');
					}).always(console.log);
				});
			}
			function convertModifiedDom(e) {
				var parsoidObj, nextRequestBeforeHeader = false;
				if(warnOnRespond && !confirm('Are you sure you want to respond to this edit request?')) {
					return false;
				}
				$('.editrequest button').prop('disabled', true);
				parsoidObj = e.data;
				parsoidObj.resultObj.text('Preparing new wikitext...');
				$(parsoidObj).before(parsoidObj.templateMarker);
				$('h1,h2,h3,h4,h5,h6,.editrequest', parsoidDom).each(function() {
					var obj = $(this);
					if(!obj.hasClass('editrequest')) {
						// a heading
						if(obj.closest('').length) {
							// it's from a template transclusion. ignore
							return true;
						}
						if(obj.add(parsoidObj) === this) {
							// before our banner. set as our section header (tentatively) and otherwise ignore
							parsoidObj.section = obj;
							return true;
						}
					} else if (obj.add(parsoidObj) === this) {
						// not after our banner. ignore
						return true;
					} else {
						nextRequestBeforeHeader = true;
					}
					obj.before(parsoidObj.responseMarker);
					parsoidObj.respondedInPage = true;
					return false;
				});
				if(parsoidObj.removerequest && !nextRequestBeforeHeader && $(parsoidObj).prev().is(parsoidObj.section)) {
					// if the section header is immediately before a request being removed, remove it too
					parsoidObj.section.remove();
				}
				$('', parsoidDom).remove();
				$.ajax({
					type: 'POST',
					url: '/api/rest_v1/transform/html/to/wikitext/' + encodeURIComponent(mw.config.get('wgPageName')) + '/' + mw.config.get('wgRevisionId'),
					headers: {
						Accept: 'text/plain; charset=utf-8; profile="mediawiki.org/specs/wikitext/1.0.0"',
						'Api-User-Agent': 'editProtectedHelper (https://en.wikipedia.org/User:Jackmcbarn/editProtectedHelper)',
						'If-Match': gEtag
					},
					data: { html: parsoidDom.documentElement.outerHTML },
					success: function(data) { return saveWikitextFromParsoid(parsoidObj, data); }
				});
			}
			function appendForm(obj, level, pagename, answered, force, parsoidObj) {
				if(browserHasDataCorruptionBug) {
					$(obj).append('<div style="color:red;font-weight:bold">WARNING: Your browser has a bug which will cause corruption of pages that it edits with Parsoid. See <a href="https://www.mediawiki.org/User:Catrope/IE_bugs#style_attributes_are_aggressively_normalized.2C_causing_data_loss">here</a> for details. You are responsible for any damage to pages that this causes.</div>');
				}
				var form = $('<form class="editProtectedHelper" style="display: none" />');
				form.append('<style scoped>.mw-ui-input { background-color: white; }</style>');
				form.append('<label>Level: <select name="level" class="mw-ui-input mw-ui-input-inline"><option value="semi"' + selectedLevel + '>Semi-protected</option><option value="extended"' + selectedLevel + '>Extended-confirmed-protected</option><option value="template"' + selectedLevel + '>Template-protected</option><option value="full"' + selectedLevel + '>Fully protected</option><option value="interface"' + selectedLevel + '>Interface-protected</option></select></label> ');
				if(force) {
					form.append('<label>Disable protection level autodetection (use only if necessary): <input type="checkbox" name="force" checked="checked" /></label> ' );
				} else {
					form.append('<input type="checkbox" name="force" style="display: none" />' ); // if this is off and you want to turn it on, do it with firebug or something. otherwise people will use this when they shouldn't
				}
				var label = $('<label>Page to be edited: </label>');
				label.append($('<input type="text" name="pagetoedit" class="mw-ui-input mw-ui-input-inline" />').attr('value', pagename !== '<!-- Page to be edited -->' ? pagename : ''));
				form.append(label);
				form.append(' <label>Answered: <input type="checkbox" name="answered"' + (answered ? ' checked="checked"' : '') + ' /></label><br />Response: ');
				var select = $('<select name="responsetemplate" class="mw-ui-input" />');
				templateResponses.forEach(function(r) {
					select.append($('<option />').attr('value', r).text(r));
				});
				form.append(select);
				form.append('<textarea name="responsefree" class="mw-ui-input" placeholder="Enter any custom response text here. A signature will automatically be appended."></textarea> ~~' + '~~ ');
				var resultObj = $('<span></span>');
				var button = $('<button type="button" class="mw-ui-button mw-ui-constructive">Submit</button>').click(parsoidObj, convertModifiedDom);
				form.append(button);
				form.append(' ');
				$(obj).append(form);
				var buttons = $('<div />');
				buttons.append($('<button type="button" class="mw-ui-button mw-ui-progressive">Respond</button>').click(function(){
					buttons.hide();
					$(obj).closest('.editrequest').removeClass('mbox-small');
					form.show();
				}))
				.append(' ')
				.append($('<button type="button" class="mw-ui-button mw-ui-destructive">Remove request</button>').click(function(){
					if(!warnOnRemove || confirm('Are you sure you want to completely remove this edit request from the page? In general, this should not be done to good-faith requests unless they are blank or duplicates of other requests by the same user, etc.')) {
						warnOnRespond = false;
						parsoidObj.removerequest = true;
						button.click();
					}
				}));
				if(!answered) {
					quickResponses.forEach(function(r){
						buttons.append(' ').append($('<button type="button" class="mw-ui-button mw-ui-constructive" />').text(r).click(function(){
							if(!warnOnQuickRespond || confirm('Are you sure you want to respond to this edit request?')) {
								warnOnRespond = false;
								parsoidObj.form.answered.checked = true;
								parsoidObj.form.responsetemplate.value = r;
								parsoidObj.form.responsefree.value = r;
								button.click();
							}
						}));
					});
				}
				$(obj).append(buttons);
				$(obj).append(resultObj);
				parsoidObj.form = form;
				parsoidObj.resultObj = resultObj;
			}
			function parsoidSetupFieldsForTemplate(index) {
				var obj = $(this);
				var data_mw = obj.attr('data-mw');
				if(data_mw === undefined) {
					console.log("No data-mw attribute was found on edit request banner " + index + ". This is probably because some template above it on the page opened an HTML tag but didn't close it.");
					return;
				}
				data_mw = JSON.parse(data_mw);
				var templateName = data_mw.parts.template.target.wt;
				var level = obj.attr('data-origlevel');
				if(autoFixLevel) {
					switch(this.id) {
						case 'editsemiprotected':
							level = 'semi';
							break;
						case 'editextendedprotected':
							level = 'extended';
							break;
						case 'edittemplateprotected':
							level = 'template';
							break;
						case 'editprotected':
							level = 'full';
							break;
						case 'editinterfaceprotected':
							level = 'interface';
					}
				}
				var params = ;
				for(var key in data_mw.parts.template.params) {
					params = data_mw.parts.template.params.wt;
				}
				
				// this only runs on numerical parameters
				params.forEach(function(value, key) {
					if(/=/.test(value)) {
						params = key + '=' + value;
					}
				});
				
				var pagename = params.join('|').replace(/^\|+|\|+$/g, '').replace(/\|+/g, '|');
				var answered = yesno(params.ans || params.answered, true);
				this.demo = yesno(params.demo);
				var force = yesno(params.force);
				this.params = params;
				this.templateMarker = makeUniqueString(2 * index);
				this.responseMarker = makeUniqueString(2 * index + 1);
				appendForm(selector, level, pagename, answered, force, this);
			}
			function onParsoidDomReceived(data, textStatus, jqXHR) {

				// Grab the ETag header and store it in the global
				// gEtag for later use
				var headers = jqXHR.getAllResponseHeaders().split( "\r\n" );
				for( var i = 0, n = headers.length; i < n; i++ ) {
					if( headers.substring( 0, headers.indexOf( ":" ) ) === "etag" ) {
						gEtag = headers.substring( headers.indexOf( ":" ) + 2 );
						break;
					}
				}

				parsoidDom = new DOMParser().parseFromString(data, 'text/html');
				if(!parsoidDom) {
					// blech. Safari.
					parsoidDom = document.implementation.createHTMLDocument('');
					parsoidDom.documentElement.innerHTML = data;
				}
				var parsoidSelector = $('.editrequest', parsoidDom);
				if(selector.length != parsoidSelector.length) {
					console.log('Sanity check failed: ' + selector.length + ' edit requests exist in the page but only ' + parsoidSelector.length + ' were found in Parsoid\'s output.');
					return;
				}
				parsoidSelector.each(parsoidSetupFieldsForTemplate);
			}
			if(selector.length) {
				mw.loader.load('mediawiki.api');
				mw.loader.load('mediawiki.ui.button');
				mw.loader.load('mediawiki.ui.input');
				$.ajax({
					url: '/api/rest_v1/page/html/' + encodeURIComponent(mw.config.get('wgPageName')) + '/' + mw.config.get('wgRevisionId'),
					headers: {
						Accept: 'text/html; charset=utf-8; profile="https://www.mediawiki.org/Specs/HTML/2.0.0"',
						'Api-User-Agent': 'editProtectedHelper (https://en.wikipedia.org/User:Jackmcbarn/editProtectedHelper)'
					},
					success: onParsoidDomReceived
				});
			}
		});
	});
}
// </nowiki>