User:Ahecht/Scripts/draft-sorter.js

//jshint maxerr:512
//jshint esnext:false
//jshint esversion:8

//Based on [[User:Enterprisey/draft-sorter.js]] <nowiki>
( function ( $, mw ) { mw.loader.using( ["mediawiki.api", "jquery.chosen", "oojs-ui-core"], function () {
	mw.loader.load( "mediawiki.ui.input", "text/css" );
	var api = new mw.Api( { userAgent: 'draft-sorter/0.0.1' } );

	if ( mw.config.get( "wgNamespaceNumber" ) !== 118 ) { 
		if ( mw.util.getParamValue('draftsorttrigger') ) {
			// "Next draft" was clicked, but we ended up on a non-draft page
			nextDraft();
			return;
		} else {
			return;
		}
	}

	var portletLink = mw.util.addPortletLink("p-cactions", "#", "Sort draft", "pt-draftsort", "Manage WikiProject tags");
	$( portletLink ).click( function ( e ) {
		e.preventDefault();

		// If it's already there, don't duplicate
		if ( $( "#draft-sorter-wrapper" ).length ) { return; }
		
		// Configure defaults
		//var templateCache = mw.config.get("wgFormattedNamespaces")[2]+":"+mw.config.get("wgUserName")+"/Scripts/draft-sorter.json";
		var templateCache = "Wikipedia:WikiProject Articles for creation/WikiProject templates.json";
		
		// Define the form
		var form = $( "<div>" )
			.attr( "id", "draft-sorter-wrapper" )
			.css( { "background-image": "url(https://upload.wikimedia.org/wikipedia/commons/e/e2/OOjs_UI_icon_tag-ltr-progressive.svg)",
					"background-repeat": "no-repeat",
					"background-position-y": "center",
					"background-size": "50px",
					"min-height": "50px",
					"margin": "1em auto",
					"border": "thin solid #BBB",
					"padding": "0.5em 50px",
					"display": "inline-block",
					"border-radius": "0.25em"
			} ).append( $( "<span>" )
				.text( "Loading form..." )
				.css( "color", "gray" )
			);
		// Add the form to the page
		form.insertAfter( "#contentSub" );

		var select = $( "<select>" )
			.attr( "id", "draft-sorter-form" )
			.attr( "multiple", "multiple" );
		
		var submitButton = new OO.ui.ButtonWidget ()
				.setLabel( "Submit" )
				.setFlags( [ 'primary', 'progressive' ] )
				.on("click", function ( e ) { submit(); } );
		
		var cancelButton = new OO.ui.ButtonWidget ()
				.setLabel( "Cancel" )
				.setFlags( ["destructive"] )
				.on("click", function( e ) {
					$( "#draft-sorter-wrapper" ).remove();
					window.location.replace( window.location.href.replace("draftsorttrigger=y","") );
				} );

		var nextButton = new OO.ui.ButtonWidget ()
			.setIcon( "next" )
			.setLabel( "Skip" )
			.on("click", function ( e ) { nextDraft(); } );

		// Determine what templates are already on the talk page
		var existingProjects = [];
		var wikiprojects = {};

		api.get( {
				action: "query",
				titles: "Draft talk:" + mw.config.get( "wgTitle" ),
				generator: "templates",
				redirects: "1",
				gtllimit: "max",
		} ).done (function (data) {
			if (data && data.query && data.query.pages) {
				$.each(data.query.pages, function (i) {
					var item = data.query.pages[i].title.match(/^Template:(WikiProject\s[^\/]*)$/i);
					if (item && item[1] && item[1] != "WikiProject banner shell") {
						existingProjects.push(item[1]);
					}
				} );
			}
			console.log( "Project templates found on talk page: ");
			console.log( existingProjects );
			fetchJSONList(templateCache).then( (cachedList) => {
				wikiprojects = cachedList;
				constructForm();
			} );
		} ).fail (function() {
			console.log("Retrieving project templates from talk page failed.");
			fetchJSONList(templateCache).then( (cachedList) => {
				wikiprojects = cachedList;
				constructForm();
			} );
		});
		
		predicts = [];
		
		async function fetchJSONList(listName) {
			var parsedList = {}, listData;
			
			var query = {
				action:'parse',
				prop:'wikitext',
				page: listName,
				formatversion: '2',
				origin: '*'
			};
			
			try {
				listData = await api.get( query );
			} catch (jsonerror) {
				console.warn("Unable to fetch contents of " + listName + ":");
				console.log(jsonerror);
			}
			
			if (listData && listData.parse && listData.parse.wikitext) {
				try {
					parsedList = JSON.parse(listData.parse.wikitext);
				} catch (jsonerror) {
					console.warn("Error parsing JSON list " + listName + ":");
					console.log(jsonerror);
				}
			}
		
			return parsedList;
		}
		
		function nextDraft() {
			// Special:RandomInCategory isn't random, use toolforge instead
			if (nextButton) {
				nextButton.setLabel( "Loading..." ).setDisabled( true );
			}
			window.location.href = "https://randomincategory.toolforge.org/Pending_AfC_submissions?draftsorttrigger=y&cmnamespace=118&cmtype=page&returntype=subject&server=" + mw.config.get("wgServerName");
		}
		
		function showPredicts() {
			$( "#draft-sorter-status" ).append( "<li>Suggested categories from <a href=\"https://www.mediawiki.org/wiki/ORES#Topic_routing\">ORES</a>:<ul id=\"draft-sorter-suggest\"></ul></li>" );
			predicts.forEach( function(item) { 
				function addWithLink(p) {
					$( "#draft-sorter-suggest" ).append(
						$( "<li>" ).text( item + " (" ).append(
							$( "<a>" ).text("add").click(
								function() {
									$( select ).val( 
										$( select ).val().concat( [ "WikiProject " + p ] )
									).trigger("chosen:updated");
								}
							)
						).append( ")" )
					);
				}
				
				var singularItem = item.replace(/s$/, '');
				if( !existingProjects.includes( "WikiProject " + item ) 
					&& wikiprojects[item]
				) { //Prediction matches a WikiProject and doesn't already exist
					addWithLink(item);
				} else if( singularItem != item
					&& !existingProjects.includes( "WikiProject " + singularItem ) 
					&& wikiprojects[singularItem]
				) { //Singular form of prediction matches a WikiProject and doesn't exist
					addWithLink(singularItem);
				} else { //Prediction doesn't match a WikiProject or already exists
					$( "#draft-sorter-suggest" ).append( 
						$( "<li>" ).append( item  )
					);
				}
			} );
			return;
		}
		
		function getPredicts() {
			var lang = mw.config.get("wgServerName").split(".wikipedia.org");
			if (lang.length == 1) return;
			
			const liftWingExternalEndpoint = "https://api.wikimedia.org/service/lw/inference/v1/models/";
			let headers = new Headers({
			    "Content-Type": "application/json",
				"User-Agent": "draft-sorter (https://en.wikipedia.org/wiki/User:Ahecht/Scripts/draft-sorter.js)"
			});
			var revID = mw.config.get( "wgCurRevisionId" );
			var model = (lang[0] == "en") ? "enwiki-drafttopic" : "outlink-topic-model";
			var postBody = JSON.stringify({
				"rev_id":  revID,
				"lang": lang[0],
				"page_title": mw.config.get("wgPageName")
			});

			fetch(liftWingExternalEndpoint + model + ":predict", {
				method: "POST",
				headers: new Headers({
				    "Content-Type": "application/json",
					"User-Agent": "draft-sorter (https://en.wikipedia.org/wiki/User:Ahecht/Scripts/draft-sorter.js)"
					}),
				body: postBody
			}).then(response => response.json()).then(data => {
				var prediction = [];
				var dbName = mw.config.get("wgDBname");
				
				if(data && data[dbName] && data[dbName].scores &&
					data[dbName].scores[revID] &&
					data[dbName].scores[revID].drafttopic &&
					data[dbName].scores[revID].drafttopic.score &&
					data[dbName].scores[revID].drafttopic.score.prediction) {
					prediction = data[dbName].scores[revID].drafttopic.score.prediction;
				} else if (data && data.prediction && data.prediction.results) {
					data.prediction.results.forEach( p => {
						if (p && p.topic) prediction.push(p.topic);
					} );
				} 
				
				if (prediction.length) {
					console.log("Got ORES response! Raw predictions:");
					console.log(prediction);
					
					prediction.forEach( function (item) {
						var last = item.split(".")[item.split(".").length-1];
						var penultimate = item.split(".")[item.split(".").length-2];
						if ( last.substr(-1) == "*" ) {
							// Filter out redundant starred predictions
							if (prediction.find(element => (
								element.split(".")[element.split(".").length-1] != last &&
								element.split(".")[element.split(".").length-2] == penultimate
							) ) ) {
								console.log("Prediction \"" + last + "\" excluded.");
								last = null;
							} else {
								last = penultimate;
							}
						}
						
						if ( wikiprojects[last] ) {
							// WikiProject found, no need to try splitting
							predicts.push(last);
						} else if ( last ) {
							// Can't find wikiProject, try splitting
							var splitLast = last.split(/( & | and )/);
							for (i=0;i<=splitLast.length;i+=2) {
								splitLast[i] = splitLast[i].charAt(0).toUpperCase()
									+ splitLast[i].slice(1);
								predicts.push( splitLast[i] );
							}
						}
					} );
					console.log("Filtered predictions:");
					console.log(predicts);
					showPredicts();
				} else {
					console.warn("Error finding predictions in ORES response:");
					console.warn(data);
				}
			} ).catch( e => console.warn("Error retrieving ORES data: " + e) );

			return;
		}

		// Construct the form
		function constructForm() {
			mw.loader.load( "oojs-ui.styles.icons-movement"); 
			
			Object.keys(wikiprojects).sort().forEach( function(name) {
				select.append( $( "<option>" )
					.attr( "value", wikiprojects[name] )
					.text( name ) );
			} );
			form.hide();
			form.empty();
			form.append( $( "<span>" )
				.text( "Tag WikiProjects: " )
				.css( {
					"font-size": "115%",
					"font-weight": "bold"
				} )
			);
			form.append( select );
			form.append( "&#32;&#32;" );
			form.append( submitButton.$element );
			form.append( cancelButton.$element );
			form.append( nextButton.$element );
			form.append ( $( "<ul>" )
				.attr( "id", "draft-sorter-status" )
			);
			form.show();
			$( select )
				.val( existingProjects )
				.chosen( {"placeholder_text_multiple": "Select some WikiProjects"} )
				.on("change", function(evt, params) { //Make existing projects undeletable
					$( "#draft-sorter-status" ).empty();
					if ( predicts.length > 0 ) { showPredicts(); }
					if ( params.deselected && existingProjects.includes(params.deselected) ) {
						$( select ).val( $( select ).val().concat([params.deselected]) ).trigger("chosen:updated");
						$( "#draft-sorter-status" ).prepend( $( "<li>" )
							.text( "Draft Sorter cannot remove existing WikiProjects." )
							.addClass( "error" )
						);
					}
				} );

			// Add completed form to the page
			$( '#draft-sorter-wrapper' ).replaceWith(form);
			getPredicts();
			return;
		}

		// The submission function
		function submit() {
			$( "#draft-sorter-form" )
				.attr("disabled", true)
				.trigger("chosen:updated");
			submitButton
				.setLabel( "Submitting..." )
				.setDisabled( true );
			cancelButton
				.setLabel ( "Close" );
				
			var newTags = [];

			$( "#draft-sorter-form" ).val().forEach( function (element) {
				if ( !existingProjects.includes(element) ) {
					newTags.push(element);
				}
			} );

			console.log( newTags.length + " new tag(s): " + newTags.join(", ") );
			var statusList = $( "#draft-sorter-status" )
				.html( "<li>Saving " + newTags.length + " new tags.</li>" );
			var showStatus = function ( status ) {
				return $( "<li>" )
					.text( status )
					.appendTo( statusList );
			};
			var newText = "";
			newTags.forEach( function ( element ) {
					newText += "{{" + element + "|importance=|class=draft}}\n";
			} );

			function editTalk(text, prefix) {
				var params = {
					action: "edit", section: "0",
					title: "Draft talk:" + mw.config.get( "wgTitle" ),
					summary: "Tagging draft: +" + newTags.join(", +") +
						" ([[User:Ahecht/Scripts/draft-sorter|draft-sorter]])",
				};
				params[prefix + "text"] = text;

				api.postWithEditToken( params ).done( function ( data ) {
					if ( data && data.edit && data.edit.result && data.edit.result === "Success" ) {
						showStatus( "Edit saved successfully! (" )
							.append( $( "<a>" )
								.text( "reload" )
								.attr( "href", "#" )
								.click( function () {
									window.location.replace( 
										window.location.href.replace("draftsorttrigger=y","")
									);
								} )
							).append( ")" );
						submitButton.setLabel( "Submitted" );
						nextButton.setLabel( "Next draft" ).setFlags( [ 'progressive' ] );
					} else {
						showStatus( "Couldn't save due to error: " + JSON.stringify( data ) );
					}
				} ).fail( function ( error ) {
					showStatus( "Couldn't save due to error: " + JSON.stringify( error ) );
				} );
				return;
			}

			api.get( {
				action: "query",
				titles: "Draft talk:" + mw.config.get( 'wgTitle' ),
				prop: "templates",
				tltemplates: "Template:WikiProject_banner_shell"
			} ).done (function (data) {
				var bannerShellUsed = Object.entries(data.query.pages)[0][1].templates;
				if(typeof(bannerShellUsed) == "object" && bannerShellUsed.length > 0) {
					api.get( {
						action: "parse",
						page: "Draft talk:" + mw.config.get( 'wgTitle' ),
						prop: "wikitext",
						section: "0"
					} ).done (function (data) {
						var talkText = data.parse.wikitext["*"];
						if (typeof(talkText) == "string") {
							var pattern = /(\{\{\s*(?:Wiki[ _]?Project[ _]?banners?[ _]?shell(?:\/redirect)?|(?:(?:WP)?[ _]?Banner|(?:Wiki)?Project|Scope)[ _]?shell|Multiple[ _]wikiprojects|WikiProject[ _]?Banners?|WPBS?)\s*\|(?:\s*[a-z1]+\s*=[^\{\}]*)*\s*(?:\\n)*?)/im;
							if (talkText.search(pattern) >= 0) {
								newText = talkText.replace( pattern, ("$1" + newText) );
								editTalk(newText,"");
							} else {
								console.log("Banner shell on talk page, but not found in wikitext: " + talkText);
								editTalk(newText,"prepend");
							}
						} else {
							console.log("typeof(talkText) = " + typeof(talkText));
							editTalk(newText,"prepend");
						}
					} ).fail (function (error) {
						console.warn( "Couldn't retrieve talk page text due to error: " + JSON.stringify( error ) );
						editTalk(newText,"prepend");
					} );
				} else if(newTags.length > 2) {
					console.log("typeof(bannerShellUsed) = " + typeof(bannerShellUsed) );
					newText = "{{WikiProject banner shell|\n" + newText + "}}";
					editTalk(newText,"prepend");
				} else {
					console.log("typeof(bannerShellUsed) = " + typeof(bannerShellUsed) + "; newTags.length = " + newTags.length);
					editTalk(newText,"prepend");
				}
			} ).fail( function ( error ) {
				console.warn( "Couldn't retrieve templates on talk page due to error: " + JSON.stringify( error ) );
				editTalk(newText,"prepend");
			} );
			return;
		}
	} );
	if (mw.util.getParamValue('draftsorttrigger')) {
		$( portletLink ).trigger("click");
	}
} ) }( jQuery, mediaWiki ) );
//</nowiki>

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.