// <nowiki>
// Script to respond to edit filter false positive reports
// @author DannyS712
$(() => {
const EFFPRH = {};
window.EFFPRH = EFFPRH;
EFFPRH.config = {
debug: true,
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', 'wvui', 'mediawiki.util', 'mediawiki.api', 'es6-polyfills' ],
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(
$( '<div>' ).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;
}
/* Don't take up entire width for input, and make dropdown the same */
.wvui-dropdown,
.wvui-input {
width: 50%;
}
/* Override normal rules for indenting lists */
.wvui-dropdown ul {
margin-left: 0px;
}
/* Separate the dropdown and input */
.wvui-dropdown {
margin-bottom: 10px;
}
/* Reduce vertical space in the dropdown options */
.wvui-options-menu__item {
line-height: 1em;
}
`);
};
/**
* Get the section number for a response, given the jQuery element for the
* <span> 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( /§ion=(\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 = $( '<a>' )
.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 {{EFFP}}
EFFPRH.responseOptions = [
{ id: 'none', label: 'None' },
{ id: 'done', label: 'Done (no change to filter)' },
{ id: 'defm', label: 'Done (may need a change to filter)' },
{ id: 'notdone', label: 'Not Done (filter working properly)' },
{ id: 'ndefm', label: 'Not Done (may need a change to filter)' },
{ id: 'redlink', label: 'Not Done (notable people)' },
{ id: 'alreadydone', label: 'Already Done' },
{ id: 'denied', label: 'Decline (edits are vandalism)' },
{ id: 'checking', label: 'Checking' },
{ id: 'blocked', label: 'User blocked' },
{ id: 'talk', label: 'Request on article talk page' },
{ id: 'fixed', label: 'Fixed filter' },
{ id: 'question', label: 'Question' },
{ id: 'note', label: 'Note' },
{ id: 'private', label: 'Private 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 wvui = mw.loader.require( 'wvui' );
const handlerApp = {
components: {
WvuiButton: wvui.WvuiButton,
WvuiDropdown: wvui.WvuiDropdown,
WvuiInput: wvui.WvuiInput
},
data: function () {
return {
reporterName: reporterName,
sectionNum: sectionNum,
responseOptions: EFFPRH.responseOptions,
selectedResponse: 'none',
commentValue: '',
// Don't try and link to the specific section, otherwise it just navigates
// directly instead of reloading
reloadUrl: mw.util.getUrl( mw.config.get( 'wgPageName' ) ),
// Overall state
haveSubmitted: false,
editMade: false,
editError: false
};
},
computed: {
canSubmit: function () {
return !this.haveSubmitted && this.selectedResponse !== 'none';
}
},
methods: {
onCommentChange: function ( newComment ) {
this.commentValue = newComment;
},
submitHandler: function () {
this.haveSubmitted = true;
EFFPRH.respondToReport(
this.reporterName,
this.sectionNum,
this.selectedResponse,
this.commentValue
).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: `
<div class="script-EFFPRH-handler">
<p>Section {{ sectionNum }} for report by {{ reporterName }}. Selected response: {{ selectedResponse }}. Comment: {{ commentValue }}.</p>
<wvui-dropdown v-model="selectedResponse" :items="responseOptions" defaultLabel="Response to report" :disabled="haveSubmitted" />
<wvui-input :value="commentValue" v-on:input="onCommentChange" placeholder="Comment" :disabled="haveSubmitted" />
<br />
<ul v-show="haveSubmitted">
<li>Submitting...</li>
<li v-show="editMade">Success! <a :href="reloadUrl"><strong>Reload the page</strong></a></li>
<li v-show="editError">Uh-oh, something went wrong. Please check the console for details.</li>
</ul>
<wvui-button type="primary" action="progressive" :disabled="!canSubmit" v-on:click="submitHandler">Submit</wvui-button>
<wvui-button type="primary" action="destructive" :disabled="haveSubmitted" v-on:click="cancelHandler">Cancel</wvui-button>
</div>`
};
vueAppInstance = Vue.createMwApp( handlerApp );
vueAppInstance.mount( '#' + targetDivId );
};
/**
* Actually make the page edit to respond to the report. Returns a promise
* for the edit succeeding or not.
*/
EFFPRH.respondToReport = function (
reporterName,
sectionNum,
responseOption,
extraComment
) {
return new Promise( function ( resolve, reject ) {
let responseText = '\n: {{EFFP|' + responseOption + '}}';
if ( extraComment ) {
responseText += ' ' + extraComment;
}
responseText += ' --~~~~';
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: responseText,
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();
}
});
// </nowiki>