User:Max/EFFPRH.js
Appearance
< User:Max
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 code will be executed when previewing this page.
This user script seems to have a documentation page at User:Max/EFFPRH.
// Script to respond to edit filter false positive reports
// <nowiki>
$(() => {
const EFFPRH = {
config: {
debug: false
},
responseOptions: [
{ value: 'none', label: 'None' },
{ value: 'nofilterstriggered', label: 'No filters triggered' },
{ 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)' }
],
responseSummaryMap: {
none: '',
nofilterstriggered: 'No filters triggered',
done: 'Done; no change to filter',
defm: 'Done; may need a change to filter',
notdone: 'Not done; filter working properly',
ndefm: 'Not done; may need a change to filter',
redlink: 'Not done; notable people lists and similar are only meant for [[WP:WTAF|people who already have articles about them]]',
alreadydone: 'Already done',
denied: 'Decline; edits are vandalism',
checking: 'Checking',
blocked: 'User is blocked',
talk: 'Please request on article talk page',
fixed: 'Fixed filter',
question: 'Question',
note: 'Note',
private: 'Private filter',
pin: 'Pin',
moot: 'Moot; filter working properly',
mootefm: 'Moot; may need a change to filter'
},
init() {
const allowedPages = [
'Wikipedia:Edit filter/False positives/Reports'
];
const currentPage = mw.config.get('wgPageName').replace(/_/g, ' ');
if (!allowedPages.includes(currentPage)) return;
mw.loader.using(['vue', '@wikimedia/codex', 'mediawiki.util', 'mediawiki.api']).then((require) => {
this.Vue = require('vue');
this.Codex = require('@wikimedia/codex');
this.run();
});
},
icons: {},
async loadIcons() {
try {
const api = new mw.Api();
const data = await api.get({
action: 'query',
list: 'codexicons',
names: 'cdxIconCheck|cdxIconClose|cdxIconEye|cdxIconEyeClosed',
formatversion: 2
});
this.icons = (data && data.query && data.query.codexicons) || {};
} catch (error) {
this.icons = {};
}
},
async run() {
this.addStyle();
await this.loadIcons();
$('div.mw-heading h2').each((_, heading) => {
const editLinks = $(heading).parent().find('.mw-editsection');
const sectionNum = this.getSectionNumber(editLinks);
if (sectionNum !== -1) {
const nextSection = $(heading).parent().next('section');
if (nextSection.length > 0) {
nextSection.prepend($('<div>').attr('id', `script-EFFPRH-${sectionNum}`));
} else {
$(heading).parent().after($('<div>').attr('id', `script-EFFPRH-${sectionNum}`));
}
this.addHandlerLink(editLinks, $(heading).text(), sectionNum);
}
});
},
addStyle() {
mw.util.addCSS(`
.script-EFFPRH-handler {
background-color: var(--background-color-base, #fff);
border: 1px solid var(--border-color-base, #a2a9b1);
border-radius: 2px;
margin: 12px 0;
padding: 16px;
color: var(--color-base, #202122);
}
.script-EFFPRH-intro {
font-size: 0.95em;
margin: 0 0 12px;
}
.script-EFFPRH-debug {
font-size: 0.82em;
color: var(--color-subtle, #54595d);
margin: 0 0 12px;
}
.script-EFFPRH-field {
margin-bottom: 14px;
max-width: 520px;
}
.script-EFFPRH-field--wide {
max-width: 100%;
margin-right: 5px;
}
.script-EFFPRH-status {
margin: 12px 0;
max-width: 520px;
}
.script-EFFPRH-status .cdx-message {
margin-bottom: 8px;
}
.script-EFFPRH-buttons {
margin-top: 14px;
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
.script-EFFPRH-preview {
margin-top: 14px;
padding-top: 14px;
border-top: 1px solid var(--border-color-subtle, #eaecf0);
}
.script-EFFPRH-preview-content {
font-size: 0.92em;
line-height: 1.6;
}
.script-EFFPRH-preview-loading {
display: flex;
align-items: center;
gap: 8px;
max-width: 260px;
color: var(--color-subtle, #54595d);
font-size: 0.88em;
}
.cdx-menu__listbox,
.cdx-menu__group {
margin: 0 !important;
padding: 0 !important;
}
.cdx-menu-item {
margin: 0 !important;
padding: 8px 12px !important;
list-style: none !important;
}
.script-EFFPRH-buttons .cdx-icon {
margin-right: 4px;
}
`);
},
getSectionNumber(editLinks) {
const editSectionUrl = editLinks.find('a:first').attr('href');
const sectionMatch = editSectionUrl && editSectionUrl.match(/§ion=(\d+)(?:$|&)/);
return sectionMatch ? parseInt(sectionMatch[1]) : -1;
},
addHandlerLink(editLinks, reporterName, sectionNum) {
const handlerLink = $('<a>').attr('id', `script-EFFPRH-launch-${sectionNum}`).text('Review report');
const panel = this.createHandler(reporterName, sectionNum);
handlerLink.click((e) => {
e.stopPropagation();
panel.openPanel();
});
editLinks.children().last().before(' | ', handlerLink);
},
createHandler(reporterName, sectionNum) {
const vueAppInstance = EFFPRH.Vue.createMwApp({
components: {
CdxButton: EFFPRH.Codex.CdxButton,
CdxSelect: EFFPRH.Codex.CdxSelect,
CdxTextArea: EFFPRH.Codex.CdxTextArea,
CdxToggleButton: EFFPRH.Codex.CdxToggleButton,
CdxField: EFFPRH.Codex.CdxField,
CdxMessage: EFFPRH.Codex.CdxMessage,
CdxProgressBar: EFFPRH.Codex.CdxProgressBar,
CdxIcon: EFFPRH.Codex.CdxIcon,
previewRenderer: this.getPreviewComponent()
},
data() {
return {
visible: false,
reporterName,
sectionNum,
responseOptions: EFFPRH.responseOptions,
selectedResponse: 'none',
commentValue: '',
showDebug: EFFPRH.config.debug,
showPreview: false,
haveSubmitted: false,
editMade: false,
editError: false,
checkIcon: EFFPRH.icons.cdxIconCheck || null,
closeIcon: EFFPRH.icons.cdxIconClose || null,
eyeIcon: EFFPRH.icons.cdxIconEye || null,
eyeClosedIcon: EFFPRH.icons.cdxIconEyeClosed || null
};
},
computed: {
canSubmit() {
return !this.haveSubmitted && this.selectedResponse !== 'none';
},
previewToggleLabel() {
return this.showPreview ? 'Hide preview' : 'Show preview';
},
previewToggleIcon() {
return this.showPreview ? this.eyeClosedIcon : this.eyeIcon;
},
responseWikiText() {
return `: {{EFFP|${this.selectedResponse}}} ${this.commentValue} – ~~~~`;
}
},
methods: {
openPanel() {
this.visible = true;
},
reloadPage() {
location.hash = this.reporterName;
location.reload();
},
submitHelper() {
this.haveSubmitted = true;
EFFPRH.respondToReport(this.reporterName, this.sectionNum, this.responseWikiText, this.selectedResponse).then(
() => this.editMade = true,
() => this.editError = true
);
},
cancelHelper() {
this.visible = false;
this.selectedResponse = 'none';
this.commentValue = '';
this.showPreview = false;
this.haveSubmitted = false;
this.editMade = false;
this.editError = false;
}
},
template: `
<div v-if="visible" class="script-EFFPRH-handler">
<p class="script-EFFPRH-intro">Responding to report by <strong>{{ reporterName }}</strong></p>
<p v-if="showDebug" class="script-EFFPRH-debug">Section {{ sectionNum }}, selected response: {{ selectedResponse }}, comment: {{ commentValue }}.</p>
<cdx-field class="script-EFFPRH-field">
<template #label>Action</template>
<cdx-select
v-model:selected="selectedResponse"
:menu-items="responseOptions"
default-label="Response to report"
:disabled="haveSubmitted"
></cdx-select>
</cdx-field>
<cdx-field class="script-EFFPRH-field script-EFFPRH-field--wide">
<template #label>Comment</template>
<cdx-text-area
v-model="commentValue"
:rows="2"
:disabled="haveSubmitted"
></cdx-text-area>
</cdx-field>
<div v-if="haveSubmitted" class="script-EFFPRH-status">
<cdx-message v-if="!editMade && !editError" type="notice" :allow-user-dismiss="false">
Submitting…
</cdx-message>
<cdx-message v-if="editMade" type="success" :allow-user-dismiss="false">
Success! <a href="#" @click.prevent="reloadPage">Reload the page</a>
</cdx-message>
<cdx-message v-if="editError" type="error" :allow-user-dismiss="false">
Uh-oh, something went wrong. Please check the console for details.
</cdx-message>
</div>
<div class="script-EFFPRH-buttons">
<cdx-button weight="primary" action="progressive" :disabled="!canSubmit" @click="submitHelper">
<cdx-icon v-if="checkIcon" :icon="checkIcon"></cdx-icon>
Submit
</cdx-button>
<cdx-toggle-button v-model="showPreview" :disabled="!canSubmit">
<cdx-icon v-if="previewToggleIcon" :icon="previewToggleIcon"></cdx-icon>
{{ previewToggleLabel }}
</cdx-toggle-button>
<cdx-button weight="primary" action="destructive" :disabled="haveSubmitted" @click="cancelHelper">
<cdx-icon v-if="closeIcon" :icon="closeIcon"></cdx-icon>
Cancel
</cdx-button>
</div>
<preview-renderer v-if="showPreview && canSubmit" :wikitext="responseWikiText"></preview-renderer>
</div>
`
});
return vueAppInstance.mount(`#script-EFFPRH-${sectionNum}`);
},
getPreviewComponent() {
return {
components: {
CdxProgressBar: EFFPRH.Codex.CdxProgressBar
},
props: { wikitext: { type: String, default: '' } },
data() {
return { previewHtml: '', haveHtml: false };
},
methods: {
loadPreview(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 => {
if (res && res.parse && res.parse.wikitext === this.wikitext && res.parse.text) {
this.previewHtml = res.parse.text;
this.haveHtml = true;
}
});
}
},
watch: {
wikitext(newValue) {
this.previewHtml = '';
this.haveHtml = false;
this.loadPreview(newValue);
}
},
mounted() {
this.loadPreview(this.wikitext);
},
template: `
<div class="script-EFFPRH-preview">
<cdx-progress-bar v-if="!haveHtml" aria-label="Loading preview"></cdx-progress-bar>
<div v-else class="script-EFFPRH-preview-content" v-html="previewHtml"></div>
</div>
`
};
},
respondToReport(reporterName, sectionNum, responseWikiText, selectedResponse) {
return new Promise((resolve, reject) => {
const wikitextToAdd = `\n${responseWikiText}`;
let respText = EFFPRH.responseSummaryMap[selectedResponse] || selectedResponse;
if (!respText) {
respText = 'Response to false positive report';
}
const editParams = {
action: 'edit',
title: mw.config.get('wgPageName'),
section: sectionNum,
summary: `/* ${reporterName} */ ${respText} ([[User:Max/EFFPRH|EFFPRH.js]])`,
appendtext: wikitextToAdd,
token: mw.user.tokens.get('csrfToken')
};
new mw.Api().postWithEditToken(editParams).then(resolve, reject);
});
}
};
EFFPRH.init();
});
// </nowiki>