User:Terasail/COI Request Tool.js

/* 	COI Request Tool Created by: Terasail var nonResponseCOI = [ {label: "Close", title: "Close request", summary: "Closed edit request", parameter: "answered=yes", response: "", icon: "unFlag", flags: "", text: ""}, {label: "Open", title: "Reopen request", summary: "Reopened edit request", parameter: "answered=no", response: "", icon: "flag", flags: "", text: ""}, {label: "Remove", title: "Remove entire section", summary: "Removed COI request", parameter: "", response: "", icon: "trash", flags: ["primary", "destructive"], text: ""} ]; var responseCOI = [ {label: "Done", title: "Mark request as done", summary: "Marked COI request as done", parameter: "answered=yes", response: "d", icon: "checkAll", flags: ["primary", "progressive"], text: "Done"}, {label: "Partly done", title: "Mark request as partly done", summary: "Marked COI request as partly done", parameter: "P", response: "pd", icon: "check", flags: "", text: "Partly done:"}, {label: "Already done", title: "Mark request as already done", summary: "Marked COI request as already done", parameter: "answered=yes", response: "a", icon: "clock", flags: "", text: "Already done:"}, {label: "Note", title: "Add a note", summary: "Added a note", parameter: "", response: "note", icon: "ellipsis", flags: "", text: "Note:"}, {label: "Question", title: "Add a question", summary: "Added a question", parameter: "", response: "q", icon: "helpNotice", flags: "", text: "Question:"}, {label: "Go ahead", title: "Go ahead", summary: "User may go ahead and edit themselves", parameter: "G", response: "g", icon: "edit", flags: "", text: "Go ahead: I have reviewed these proposed changes and suggest that you go ahead and make the proposed changes to the page."}, {label: "Not done", title: "Decline request", summary: "Declined COI request", parameter: "D", response: "n", icon: "notice", flags: "", text: "Not done:"}, {label: "Not done for now", title: "Decline request for now", summary: "Declined request for now", parameter: "D", response: "nfn", icon: "notice", flags: "", text: "Not done for now:"}, {label: "Promotional", title: "Decline promotional request", summary: "Declined promotional request", parameter: "D|ADV", response: "mpro", icon: "signature", flags: "", text: "Not done: A majority of the requested changes are currently written in a promotional tone. Please review WP:Neutral point of view and ensure you follow this before submitting any edit requests."}, {label: "No consensus", title: "No consensus for the change", summary: "Declined request with no consensus for the change", parameter: "D|C", response: "nc", icon: "userGroup", flags: "", text: "Not done: No consensus could be obtained for making the requested change."}, {label: "Needs reliable sources", title: "Close request pending reliable sources", summary: "COI request declined: Change requires reliable sources", parameter: "D|V", response: "rs", icon: "quotes", flags: "", text: "Not done: please provide reliable sources that support the change you want to be made."}, {label: "Removing content", title: "Decline request removing well-cited content", summary: "Declined request removing well-cited content", parameter: "D|R", response: "rm", icon: "restore", flags: "", text: "Not done: The proposed changes are removing content that is well-cited or where sources exist."}, {label: "Partly promotional", title: "Decline partly promotional request for now", summary: "Declined partly promotional request for now", parameter: "D|ADV", response: "pro", icon: "signature", flags: "", text: "Not done for now: Some of the requested changes are currently written in a promotional tone. Please review WP:Neutral point of view and make changes where appropriate to follow this before reopening the request."}, {label: "Needs consensus", title: "Close request pending consensus", summary: "COI request declined: Change requires consensus first", parameter: "D|D", response: "c", icon: "userGroup", flags: "", text: "Please establish a consensus with editors engaged in the subject area before using the template for this proposed change."}, {label: "Unclear request", title: "Decline and mark as unclear", summary: "COI request closed as it is unclear what change is requested", parameter: "D|Unclear request", response: "xy", icon: "helpNotice", flags: "", text: "it's not clear what changes you want to be made. Please mention the specific changes in a \"change X to Y\" format."}, {label: "Unspecific", title: "Decline unspecific request", summary: "Declined unspecific request", parameter: "D|S", response: "s", icon: "speechBubbles", flags: "", text: "Not done for now: The current request is not specific enough to make changes to the page. Consider developing changes in a new talk section or visit the conflict of interest noticeboard for serious issues."}, {label: "Balance issues", title: "Decline request with balance issues", summary: "Declined request with balance issues", parameter: "D|O", response: "b", icon: "notice", flags: "", text: "Not done for now: The proposed changes create some balance issues with the article. These will need to be addressed before any changes can be made."}, {label: "Partly undo", title: "Partly undo request", summary: "COI request has been partly undone", parameter: "P|The requested edit has been partially undone", response: "udp", icon: "undo", flags: "", text: "Undone: This request has been partially undone."}, {label: "Undo", title: "Undo request", summary: "COI request has been undone", parameter: "D|The requested edit has been undone", response: "ud", icon: "undo", flags: "", text: "Undone: This request has been undone."} ]; var editRequests = $('.editrequest'); var COIRequests = []; for (let i = 0; i < editRequests.length; i++) { if (typeof(editRequests[i].attributes['data-origlevel']) == 'undefined') { $(editRequests[i].children[0]).append('  '); COIRequests.push(editRequests[i]); } }

if (COIRequests.length > 0) { mw.loader.using(["oojs-ui-core", "oojs-ui-widgets", "oojs-ui-windows"]).done(function {		mw.loader.load(["oojs-ui.styles.icons-alerts", "oojs-ui.styles.icons-interactions", "oojs-ui.styles.icons-moderation", "oojs-ui.styles.icons-editing-core", "oojs-ui.styles.icons-editing-advanced", "oojs-ui.styles.icons-user"]);		loadCOITool;	}); }

async function loadCOITool { // Get page watchers, visitors and user watch status. let watchStatus = []; let watchQuery = await ApiGetCOI({		action: "query",		prop: "info",		pageids: mw.config.get("wgArticleId"),		inprop: "watchers|visitingwatchers|watched",		format: "json"	}); let watchData = watchQuery.query.pages[mw.config.get("wgArticleId")]; let watched = watchData.watched; let expiry = watchData.watchlistexpiry; if (expiry) { watched = Math.ceil((new Date(expiry).getTime - Date.now) / 1000 / 60 / 60 / 24) + " days"; }	watchStatus.push(watchData.watchers || "less than 30", watchData.visitingwatchers || "<30", watched); //Increment through all COI requests & add respond button for (let i = 0; i < COIRequests.length; i++) { let respondButton = new OO.ui.ButtonWidget({			icon: "edit",			label: "Respond",			flags: "progressive",			title: "Open the response menu for this request"		}).on("click", function {			loadCOIResponse(COIRequests[i], respondButton, watchStatus);			respondButton.setDisabled(true);		}); respondButton.$element[0].style = "margin:5px"; $($('.response-cell')[i]).append(respondButton.$element); } }

function loadCOIResponse(COIRequest, respondButton, watchStatus) { let responseBoxHTML = ' '; $(responseBoxHTML).insertAfter(COIRequest, respondButton); let responseBox = COIRequest.nextElementSibling; let responseQuick = $(responseBox).find('.response-quick')[0]; let responseCustom = $(responseBox).find('.response-custom')[0]; let responsePreview = $(responseBox).find('.response-preview')[0]; let responseControls = $(responseBox).find('.response-controls')[0]; //Quick Responses //Create a HorizontalLayout & Fieldset for quick responses let quickLayout = new OO.ui.HorizontalLayout; let quickFieldset = new OO.ui.FieldsetLayout; quickFieldset.addItems([new OO.ui.FieldLayout(new OO.ui.Widget({content: [quickLayout]}), {align: 'top'})]); $(responseQuick).append(quickFieldset.$element); let quickNonResponses = [2];//Remove button if ($(COIRequest).find('hr').length > 0) {//If request is closed quickNonResponses.push(0);//Close button } else { quickNonResponses.push(1);//Open button }	for (let i = 0; i < quickNonResponses.length; i++) { let tempVal = quickNonResponses[i]; let tempButton = new OO.ui.ButtonWidget({			flags: nonResponseCOI[tempVal].flags,			icon: nonResponseCOI[tempVal].icon,			title: nonResponseCOI[tempVal].title,			invisibleLabel: true		}).on("click", function {			saveResponseCOI([COIRequest, responseQuick, responsePreview, responseControls], nonResponseCOI[tempVal], "", "nochange", undefined);		}); quickLayout.addItems([tempButton]); }	let quickResponses = [0, 5, 8, 10];//Done, Go ahead, Consensus, Unclear for (let i = 0; i < quickResponses.length; i++) { let tempVal = quickResponses[i]; let tempButton = new OO.ui.ButtonWidget({			flags: responseCOI[tempVal].flags,			label: responseCOI[tempVal].label,			title: responseCOI[tempVal].title		}).on("click", function {			saveResponseCOI([COIRequest, responseQuick, responsePreview, responseControls], responseCOI[tempVal], "", "nochange", undefined);		}); quickLayout.addItems([tempButton]); }	//Custom Responses //Response dropdown let responseDropdown = new OO.ui.DropdownWidget({		label: "Select reply option - Add additional text below",		menu: {items: []}	}).on("labelChange", function {		submitButton.setDisabled(false);		previewCOI(responseDropdown.menu.findSelectedItem.getData, responseText.value, responsePreview);	}); for (let i = 0; i < responseCOI.length; i++) { let tempWidget = new OO.ui.MenuOptionWidget({			label: responseCOI[i].text,			icon: responseCOI[i].icon,			data: responseCOI[i]		}); responseDropdown.menu.addItems([tempWidget]); }	responseDropdown.$element[0].style = "margin:auto; text-align:left;"; $(responseCustom).append(responseDropdown.$element); //Response text var responseText = new OO.ui.MultilineTextInputWidget({		autosize: true, rows: 4, label: "Additional text"	}).on("change", function {		if (responseDropdown.menu.findSelectedItem) {			previewCOI(responseDropdown.menu.findSelectedItem.getData, responseText.value, responsePreview);		}	}); responseText.$element[0].style = "margin:5px auto;"; $(responseCustom).append(responseText.$element); //Response Controls //Create a HorizontalLayout & Fieldset for response controls let controlsLayout = new OO.ui.HorizontalLayout; let controlsFieldset = new OO.ui.FieldsetLayout; controlsFieldset.addItems([new OO.ui.FieldLayout(new OO.ui.Widget({content: [controlsLayout]}), {align: 'top'})]); $(responseControls).append(controlsFieldset.$element); //Cancel Button let cancelButton = new OO.ui.ButtonWidget({		icon: "cancel",		flags: "destructive",		label: "Cancel",		framed: false,		title: "Cancel the response & close this menu"	}).on("click", function {		respondButton.setDisabled(false);		responseBox.remove;	}); controlsLayout.addItems([cancelButton]); //Watchlist dropdown let watchOptions = [{data: "infinite", label: "Permanent"}, {data: "1 day", label: "1 day"}, {data: "3 days", label: "3 days"}, {data: "1 week", label: "1 week"}, {data: "1 month", label: "1 month"}]; let watchValue = "infinite"; if (!!watchStatus[2]) { watchOptions.unshift({data: "nochange", label: watchStatus[2]}); watchValue = "nochange"; }	let watchlistLayout = new OO.ui.HorizontalLayout; let watchlistDropdown = new OO.ui.DropdownInputWidget({		value: watchValue,		options: watchOptions,		disabled: (watchStatus[2] == undefined)	}); watchlistLayout.addItems([watchlistDropdown]); //Watchlist checkbox & label let watchlistCheckbox = new OO.ui.CheckboxInputWidget({		selected: (watchStatus[2] != undefined)	}).on("change", function (newStatus) {		watchlistDropdown.setDisabled(!newStatus);	}); let watchlistLabel = new OO.ui.LabelWidget({label: "Watch this page"}).on("change", function (newStatus) {

});	//Submit Button	let submitButton = new OO.ui.ButtonWidget({ icon: "checkAll", flags: ["primary", "progressive"], label: "Submit", title: "Submit the response", disabled: true }).on("click", function { saveResponseCOI([COIRequest, responseQuick, responsePreview, responseControls], responseDropdown.menu.findSelectedItem.getData, responseText.value, watchlistCheckbox.selected, watchlistDropdown.value); });	controlsLayout.addItems([cancelButton, watchlistCheckbox, watchlistLabel, watchlistLayout, submitButton]); }

function previewCOI(responseOption, responseText, tableCell) { let restTransform = "https://en.wikipedia.org/api/rest_v1/transform/wikitext/to/html/" + encodeURI(mw.config.get("wgPageName")); if (responseOption.response != "") { responseText = " " + responseText; }	if (responseText != "") { let nickname = " " + mw.user.options.values.nickname; if (nickname == " ") {//Create default signature if no nickname nickname = mw.user.getName; nickname = " " + nickname + " (talk)"; }		let dateObj = new Date; let dateNow = dateObj.toLocaleDateString('en-GB', {			timeZone: 'UTC',			year: 'numeric',			month: 'long',			day: 'numeric'		}); let timeNow = dateObj.toLocaleTimeString('en-GB', {timeZone: 'UTC', hour: '2-digit', minute: '2-digit'}); responseText = responseText + nickname + " " + timeNow + ", " + dateNow + " (UTC)"; responseText = responseText.replaceAll(/{{subst:/gi, "{{"); responseText = responseText.replaceAll(/\s*~\s*/g, ""); $.post(restTransform, 'wikitext=' + encodeURIComponent(responseText) + '&body_only=true',			function (html) {				tableCell.style = "padding:8px 1em 2px;";				tableCell.children[1].innerHTML = html;			}		); } else { tableCell.style = "display:none;"; } }

async function saveResponseCOI(requestBox, responseOption, responseText, watchPage, watchValue) { await new Promise(function(resolve) {		OO.ui.confirm("Confirm in order to reply to this edit request.").done(function(confirmed) { if (confirmed) { resolve; } else { return; }});	});	//Create label box & remove quick actions requestBox[1].innerHTML = ""; requestBox[3].remove; let statusMessage = new OO.ui.MessageWidget({		icon: 'pageSettings',		type: 'notice',		label: 'Processing request — Edit request starting, getting section data to edit.'	}); statusMessage.$element[0].style = "margin:5px 0; max-width:50em"; $(requestBox[1]).append(statusMessage.$element); //Create progress bar let progressBar = new OO.ui.ProgressBarWidget({		progress: false	}); $(requestBox[1]).append(progressBar.$element); //Set preview for output previewCOI(responseOption, responseText, requestBox[2]); // Find header let header = ""; let sectionIndex = 0; let tempElement = requestBox[0]; let sectionQuery = await ApiGetCOI({		action: "parse",		page: mw.config.get("wgPageName"),		prop: "sections"	}); let sections = sectionQuery.parse.sections; do { tempElement = tempElement.previousElementSibling; if (tempElement.classList.contains("mw-heading")) { if (tempElement.parentElement.tagName == "SECTION") { //Need to support both while new parser is being implemented header = $(tempElement).find("h1,h2,h3,h4,h5,h6")[0].id; sectionIndex = parseInt(tempElement.parentElement.dataset.mwSectionId); } else { if (tempElement.getElementsByClassName("mw-headline").length > 0) { //Vector 2022 header = tempElement.getElementsByClassName("mw-headline")[0].id; } else { //Vector Legacy header = $(tempElement).find("h1,h2,h3,h4,h5,h6")[0].id; }				for (let i = 0; i < sections.length; i++) { if (sections[i].anchor == header) { sectionIndex = parseInt(sections[i].index); }				}			}		}	}	while (header == ""); statusMessage.setLabel("Processing request — Making changes to the edit request"); let editSummary = "/* " + header.replaceAll("_", " ") + " */ " + responseOption.summary + " (COI Request Tool)"; let wikitextQuery = await ApiGetCOI({		action: "parse",		page: mw.config.get("wgPageName"),		section: sectionIndex,		prop: "wikitext|revid"	}); let wikitext = wikitextQuery.parse.wikitext["*"]; let latestRevision = wikitextQuery.parse.revid; if (responseOption.parameter != "") { let template = "{{Edit COI|" + responseOption.parameter + "}}"; wikitext = wikitext.replace(/{{ *(Edit[ _])?COI(-protected|([ _](edit|request)){2})?( *\| *([=A-Z])*)* *}}/i, template); }	if (responseOption.response != "") { wikitext += "\n:{{subst:ECOI|" + responseOption.response + "}}"; wikitext += responseText.replaceAll(/\s*~\s*/g, "") + " ~"; }	if (responseOption.label == "Remove") { wikitext = ""; editSummary = editSummary.replace(/[^]+\*\/ /, ""); }	statusMessage.setType("success"); statusMessage.setLabel("Processing request — Saving changes to the talk page."); if (latestRevision != mw.config.values.wgRevisionId) { await new Promise(function(resolve) {			OO.ui.confirm("There has been a new revision to the page, do you wish to continue?").done(function(confirmed) { if (confirmed) { resolve; } else { return; }});		});	}	if (watchPage) { if (watchPage != "nochange") { watchPage = "watch"; }	} else { watchPage = "unwatch"; }	let apiParams = { action: 'edit', title: mw.config.get("wgPageName"), text: wikitext, section: sectionIndex, summary: editSummary, watchlist: watchPage };	if (watchPage == "watch") { apiParams.watchlistexpiry = watchValue; }	let reloadURL = "/w/index.php?title=" + encodeURI(mw.config.get("wgPageName")) + "&type=revision&diff=cur&oldid=prev"; new mw.Api.postWithEditToken(apiParams).done(function {		window.location = reloadURL;	}); }

function ApiGetCOI(params) { return new Promise(function(resolve) {		new mw.Api.get(params)		.done(function (data) {resolve(data);})		.fail(function (data) {console.error(data);});	}); } //