User:DannyS712/EFFPRH/sandbox.js

// // Script to respond to edit filter false positive reports // @author DannyS712 $( => { const EFFPRH = {}; window.EFFPRH = EFFPRH;

EFFPRH.config = { debug: false, version: '0-dev' }; EFFPRH.editSummary = 'Respond to false positive report via User:DannyS712/EFFPRH' + ' (v ' + EFFPRH.config.version + ')';

EFFPRH.init = function { mw.loader.using(		[ 'vue', '@wikimedia/codex', 'mediawiki.util', 'mediawiki.api' ],		EFFPRH.run	); };

EFFPRH.run = function { EFFPRH.addStyle; // Add links to each section to open a dialog $('span.mw-headline').each( function {		const $editSectionLinks = $( this ).parent.find( '.mw-editsection' );		if ( $editSectionLinks.length === 0 ) {			// Missing links span, nothing to do			return;		}		const sectionNum = EFFPRH.getHeadingSectionNum( $editSectionLinks );		if ( sectionNum === -1 ) {			// Missing link, no idea what section this is			return;		}		// Add a hidden div after the headline that will be where the Vue		// display goes		$( this ).parent.after( $( ' ' ).attr( 'id', 'script-EFFPRH-' + sectionNum ) );		const reporterName = $( this ).text;		EFFPRH.addHandlerLink( $editSectionLinks, reporterName, sectionNum );	} ); };

/** * Add styles for our interface. */ EFFPRH.addStyle = function { mw.util.addCSS(`		.script-EFFPRH-handler {			background-color: #e0e0e0;			border: 1px solid black;			margin: 10px 0 10px 0;		}		/* Override normal rules for indenting lists */		.cdx-menu ul {			margin-left: 0px;		}		/* Separate the dropdown and input */		.cdx-menu {			margin-bottom: 10px;		}		/* Reduce vertical space in the dropdown options */		.cdx-menu-item__content {			line-height: 1em;		}		/* Center form elements and labels */		.script-EFFPRH-handler td {			vertical-align: middle;		}		/* Don't use the grey background in the preview */		.script-EFFPRH-preview {			background-color: white;		}	`); };

/** * Get the section number for a response, given the jQuery element for the * with the edit section link. Returns -1 on failure. */ EFFPRH.getHeadingSectionNum = function ( $editSectionLinks ) { const editSectionUrl = $editSectionLinks.find( 'a:first' ).attr( 'href' ); if ( editSectionUrl === undefined ) { return -1; }	const sectionMatch = editSectionUrl.match( /&section=(\d+)(?:$|&)/ ); if ( sectionMatch === null ) { return -1; }	return parseInt( sectionMatch[1] ); };

/** * Add a link next to the edit section link that will launch the report handler. */ EFFPRH.addHandlerLink = function ( $editSectionLinks, reporterName, sectionNum ) { const $handlerLink = $( '' ) .attr( 'id', 'script-EFFPRH-launch-' + sectionNum ) .text( 'Review report' ); $handlerLink.click(		function {			// Only allow running once per link (until the Vue handler is removed)			if ( $( this ).hasClass( 'script-EFFPRH-disabled' ) ) {				return;			}			$( this ).addClass( 'script-EFFPRH-disabled' );			EFFPRH.showHandler( reporterName, sectionNum );		}	); // Add before the closing ] of the links $editSectionLinks.children.last.before(		' | ',		$handlerLink	); };

// Handler options, see EFFPRH.responseOptions = [ { value: 'none', label: 'None' }, { value: 'done', label: 'Done (no change to filter)' }, { value: 'defm', label: 'Done (may need a change to filter)' }, { value: 'notdone', label: 'Not Done (filter working properly)' }, { value: 'ndefm', label: 'Not Done (may need a change to filter)' }, { value: 'redlink', label: 'Not Done (notable people)' }, { value: 'alreadydone', label: 'Already Done' }, { value: 'denied', label: 'Decline (edits are vandalism)' }, { value: 'checking', label: 'Checking' }, { value: 'blocked', label: 'User blocked' }, { value: 'talk', label: 'Request on article talk page' }, { value: 'fixed', label: 'Fixed filter' }, { value: 'question', label: 'Question' }, { value: 'note', label: 'Note' }, { value: 'private', label: 'Private filter' }, { value: 'pin', label: 'Pin' }, { value: 'moot', label: 'Moot (filter working properly)' }, { value: 'mootefm', label: 'Moot (may need a change to filter)' } ];

/** * Actually show the handler for a given reporter name and section number. */ EFFPRH.showHandler = function ( reporterName, sectionNum ) { const targetDivId = 'script-EFFPRH-' + sectionNum; // Need a reference so that it can be unmounted let vueAppInstance; // We shouldn't use the mw.loader access directly, but I'm not // pasing around the `require` function everywhere const cdx = mw.loader.require( '@wikimedia/codex' ); // Extra component to render wikitext preview const previewRenderer = EFFPRH.getPreviewComponent; const handlerApp = { components: { CdxButton: cdx.CdxButton, CdxSelect: cdx.CdxSelect, CdxTextInput: cdx.CdxTextInput, CdxToggleButton: cdx.CdxToggleButton, previewRenderer: previewRenderer },		data: function { return { reporterName: reporterName, sectionNum: sectionNum, responseOptions: EFFPRH.responseOptions, selectedResponse: 'none', commentValue: '',

// Debug information of the state showDebug: EFFPRH.config.debug,

// Preview showPreview: false,

// Overall state haveSubmitted: false, editMade: false, editError: false };		},		computed: { canSubmit: function { return !this.haveSubmitted && this.selectedResponse !== 'none'; },			previewToggleLabel: function { return ( this.showPreview ? 'Hide preview' : 'Show preview' ); },			responseWikiText: function { // Computed here so that we can use it for the api preview, // does not include the leading newline let responseText = ': '; if ( this.commentValue ) { responseText += ' ' + this.commentValue; }				responseText += ' --~'; return responseText; }		},		methods: { reloadPage: function { // Needs to be a function instead of using href so that we				// can force the page to reload location.assign(					mw.util.getUrl( mw.config.get( 'wgPageName' ) + '#' + this.reporterName )				); location.reload; },			submitHandler: function { this.haveSubmitted = true; EFFPRH.respondToReport(					this.reporterName,					this.sectionNum,					this.responseWikiText				).then(					// arrow functions to simplify `this`					 => this.editMade = true,					 => this.editError = true				); },			cancelHandler: function { if ( vueAppInstance === undefined ) { console.log( 'Cannot unmount, no vueAppInstance' ); } else { vueAppInstance.unmount; // Restore link $( '#script-EFFPRH-launch-' + sectionNum ).removeClass(						'script-EFFPRH-disabled'					); }			}		},		template: ` Responding to report by. Section, selected response: , comment:.

 Submitting... Success!  Reload the page  Uh-oh, something went wrong. Please check the console for details.  Submit Cancel <cdx-toggle-button v-model="showPreview" :disabled="!canSubmit"></cdx-toggle-button>

<preview-renderer v-if="showPreview && canSubmit" :wikitext="responseWikiText"></preview-renderer> `	};	vueAppInstance = Vue.createMwApp( handlerApp ); vueAppInstance.mount( '#' + targetDivId ); };

/** * Extra component: preview of wikitext being added. */ EFFPRH.getPreviewComponent = function { return { props: { wikitext: { type: String, default: '' } },		data: function { return { previewHtml: '', haveHtml: false };		},		methods: { // Separate from the watcher so that can be called on mounted too loadPreview: function ( wikitextToPreview ) { new mw.Api.get( {					action: 'parse',					formatversion: 2,					title: mw.config.get( 'wgPageName' ),					text: wikitextToPreview,					prop: 'text|wikitext',					pst: true,					disablelimitreport: true,					disableeditsection: true,					sectionpreview: true				} ).then(					( res ) => {						console.log( res );						if ( res && res.parse && res.parse.wikitext === this.wikitext && res.parse.text ) {							this.previewHtml = res.parse.text;							this.haveHtml = true;						}					}				); }		},		watch: { wikitext: function ( newValue ) { // Reset when the wikitext to preview changes this.previewHtml = ''; this.haveHtml = false; this.loadPreview( newValue ); }		},		mounted: function { // Preview starting wikitext this.loadPreview( this.wikitext ); },		template: ` Loading preview of  ` }; };

/** * Actually make the page edit to respond to the report. Returns a promise * for the edit succeeding or not. */ EFFPRH.respondToReport = function (	reporterName,	sectionNum,	responseWikiText ) { return new Promise( function ( resolve, reject ) {		// wikitext is computed in Vue app so that it can have a preview too,		// we just need to add the leading newline		const wikitextToAdd = '\n' + responseWikiText;		const editParams = {			action: 'edit',			title: mw.config.get( 'wgPageName' ),			section: sectionNum,			summary: '/* ' + reporterName + ' */ ' + EFFPRH.editSummary,			notminor: true,			baserevid: mw.config.get( 'wgCurRevisionId' ),			nocreate: true,			appendtext: wikitextToAdd,			assert: 'user',			assertuser: mw.config.get( 'wgUserName' )		};		if ( EFFPRH.config.debug ) {			console.log( { ...editParams } );		}		new mw.Api.postWithEditToken( editParams )			.then( ( res ) => { console.log( res ); resolve; }, ( err ) => { console.log( err ); reject; } );	} ); };

});

$( document ).ready( => {	if ( mw.config.get( 'wgPageName' ) === 'Wikipedia:Edit_filter/False_positives/Reports' || mw.config.get( 'wgPageName' ) === 'User:DannyS712/EFFPRH/sandbox' ) {		window.EFFPRH.init;	} });

//