From 9316ea56c3054bbc2856281315937b96809c8d9a Mon Sep 17 00:00:00 2001 From: DarmaniLink Date: Tue, 20 Feb 2024 20:53:10 -0800 Subject: [PATCH] Issue #1945 Implementation --- modules/twinklearv.js | 1862 ++++++++++++++++++++--------------------- 1 file changed, 931 insertions(+), 931 deletions(-) diff --git a/modules/twinklearv.js b/modules/twinklearv.js index 7b4603554..a465c056e 100644 --- a/modules/twinklearv.js +++ b/modules/twinklearv.js @@ -4,984 +4,984 @@ (function($) { -/* - **************************************** - *** twinklearv.js: ARV module - **************************************** - * Mode of invocation: Tab ("ARV") - * Active on: Any page with relevant user name (userspace, contribs, etc.) - */ - -Twinkle.arv = function twinklearv() { - var username = mw.config.get('wgRelevantUserName'); - if (!username || username === mw.config.get('wgUserName')) { - return; - } - - var isIP = mw.util.isIPAddress(username, true); - // Ignore ranges wider than the CIDR limit - if (Morebits.ip.isRange(username) && !Morebits.ip.validCIDR(username)) { - return; - } - var userType = isIP ? 'IP' + (Morebits.ip.isRange(username) ? ' range' : '') : 'user'; - - Twinkle.addPortletLink(function() { - Twinkle.arv.callback(username, isIP); - }, 'ARV', 'tw-arv', 'Report ' + userType + ' to administrators'); -}; - -Twinkle.arv.callback = function (uid, isIP) { - var Window = new Morebits.simpleWindow(600, 500); - Window.setTitle('Advance Reporting and Vetting'); // Backronym - Window.setScriptName('Twinkle'); - Window.addFooterLink('AIV guide', 'WP:GAIV'); - Window.addFooterLink('UAA guide', 'WP:UAAI'); - Window.addFooterLink('SPI guide', 'Wikipedia:Sockpuppet investigations/SPI/Guide to filing cases'); - Window.addFooterLink('ARV prefs', 'WP:TW/PREF#arv'); - Window.addFooterLink('Twinkle help', 'WP:TW/DOC#arv'); - Window.addFooterLink('Give feedback', 'WT:TW'); - - var form = new Morebits.quickForm(Twinkle.arv.callback.evaluate); - var categories = form.append({ - type: 'select', - name: 'category', - label: 'Select report type:', - event: Twinkle.arv.callback.changeCategory - }); - categories.append({ - type: 'option', - label: 'Vandalism (WP:AIV)', - value: 'aiv' - }); - categories.append({ - type: 'option', - label: 'Username (WP:UAA)', - value: 'username', - disabled: isIP - }); - categories.append({ - type: 'option', - label: 'Sockpuppeteer (WP:SPI)', - value: 'sock' - }); - categories.append({ - type: 'option', - label: 'Sockpuppet (WP:SPI)', - value: 'puppet' - }); - categories.append({ - type: 'option', - label: 'Edit warring (WP:AN3)', - value: 'an3', - disabled: Morebits.ip.isRange(uid) // rvuser template doesn't support ranges - }); - form.append({ - type: 'div', - label: '', - style: 'color: red', - id: 'twinkle-arv-blockwarning' - }); - - form.append({ - type: 'field', - label: 'Work area', - name: 'work_area' - }); - form.append({ type: 'submit' }); - form.append({ - type: 'hidden', - name: 'uid', - value: uid - }); - - var result = form.render(); - Window.setContent(result); - Window.display(); - - // Check if the user is blocked, update notice - var query = { - action: 'query', - list: 'blocks', - bkprop: 'range|flags', - format: 'json' + /* + **************************************** + *** twinklearv.js: ARV module + **************************************** + * Mode of invocation: Tab ("ARV") + * Active on: Any page with relevant user name (userspace, contribs, etc.) + */ + + Twinkle.arv = function twinklearv() { + var username = mw.config.get('wgRelevantUserName'); + if (!username || username === mw.config.get('wgUserName')) { + return; + } + + var isIP = mw.util.isIPAddress(username, true); + // Ignore ranges wider than the CIDR limit + if (Morebits.ip.isRange(username) && !Morebits.ip.validCIDR(username)) { + return; + } + var userType = isIP ? 'IP' + (Morebits.ip.isRange(username) ? ' range' : '') : 'user'; + + Twinkle.addPortletLink(function() { + Twinkle.arv.callback(username, isIP); + }, 'ARV', 'tw-arv', 'Report ' + userType + ' to administrators'); }; - if (isIP) { - query.bkip = uid; - } else { - query.bkusers = uid; - } - new Morebits.wiki.api("Checking the user's block status", query, function(apiobj) { - var blocklist = apiobj.getResponse().query.blocks; - if (blocklist.length) { - // If an IP is blocked *and* rangeblocked, only use whichever is more recent - var block = blocklist[0]; - var message = (isIP ? 'This IP ' + (Morebits.ip.isRange(uid) ? 'range' : 'address') : 'This account') + ' is ' + (block.partial ? 'partially' : 'already') + ' blocked'; - // Start and end differ, range blocked - message += block.rangestart !== block.rangeend ? ' as part of a rangeblock.' : '.'; - if (block.partial) { - $('#twinkle-arv-blockwarning').css('color', 'black'); // Less severe - } - $('#twinkle-arv-blockwarning').text(message); + + Twinkle.arv.callback = function (uid, isIP) { + var Window = new Morebits.simpleWindow(600, 500); + Window.setTitle('Advance Reporting and Vetting'); // Backronym + Window.setScriptName('Twinkle'); + Window.addFooterLink('AIV guide', 'WP:GAIV'); + Window.addFooterLink('UAA guide', 'WP:UAAI'); + Window.addFooterLink('SPI guide', 'Wikipedia:Sockpuppet investigations/SPI/Guide to filing cases'); + Window.addFooterLink('ARV prefs', 'WP:TW/PREF#arv'); + Window.addFooterLink('Twinkle help', 'WP:TW/DOC#arv'); + Window.addFooterLink('Give feedback', 'WT:TW'); + + var form = new Morebits.quickForm(Twinkle.arv.callback.evaluate); + var categories = form.append({ + type: 'select', + name: 'category', + label: 'Select report type:', + event: Twinkle.arv.callback.changeCategory + }); + categories.append({ + type: 'option', + label: 'Vandalism (WP:AIV)', + value: 'aiv' + }); + categories.append({ + type: 'option', + label: 'Username (WP:UAA)', + value: 'username', + disabled: isIP + }); + categories.append({ + type: 'option', + label: 'Sockpuppeteer (WP:SPI)', + value: 'sock' + }); + categories.append({ + type: 'option', + label: 'Sockpuppet (WP:SPI)', + value: 'puppet' + }); + categories.append({ + type: 'option', + label: 'Edit warring (WP:AN3)', + value: 'an3', + disabled: Morebits.ip.isRange(uid) // rvuser template doesn't support ranges + }); + form.append({ + type: 'div', + label: '', + style: 'color: red', + id: 'twinkle-arv-blockwarning' + }); + + form.append({ + type: 'field', + label: 'Work area', + name: 'work_area' + }); + form.append({ type: 'submit' }); + form.append({ + type: 'hidden', + name: 'uid', + value: uid + }); + + var result = form.render(); + Window.setContent(result); + Window.display(); + + // Check if the user is blocked, update notice + var query = { + action: 'query', + list: 'blocks', + bkprop: 'range|flags', + format: 'json' + }; + if (isIP) { + query.bkip = uid; + } else { + query.bkusers = uid; } - }).post(); - - - // We must init the - var evt = document.createEvent('Event'); - evt.initEvent('change', true, true); - result.category.dispatchEvent(evt); -}; - -Twinkle.arv.callback.changeCategory = function (e) { - var value = e.target.value; - var root = e.target.form; - var old_area = Morebits.quickForm.getElements(root, 'work_area')[0]; - var work_area = null; - - switch (value) { - case 'aiv': - /* falls through */ - default: - work_area = new Morebits.quickForm.element({ - type: 'field', - label: 'Report user for vandalism', - name: 'work_area' - }); - work_area.append({ - type: 'input', - name: 'page', - label: 'Primary linked page:', - tooltip: 'Leave blank to not link to the page in the report', - value: Twinkle.getPrefill('vanarticle') || '', - event: function(e) { - var value = e.target.value; - var root = e.target.form; - if (value === '') { - root.badid.disabled = root.goodid.disabled = true; - } else { - root.badid.disabled = false; - root.goodid.disabled = root.badid.value === ''; - } + new Morebits.wiki.api("Checking the user's block status", query, function(apiobj) { + var blocklist = apiobj.getResponse().query.blocks; + if (blocklist.length) { + // If an IP is blocked *and* rangeblocked, only use whichever is more recent + var block = blocklist[0]; + var message = (isIP ? 'This IP ' + (Morebits.ip.isRange(uid) ? 'range' : 'address') : 'This account') + ' is ' + (block.partial ? 'partially' : 'already') + ' blocked'; + // Start and end differ, range blocked + message += block.rangestart !== block.rangeend ? ' as part of a rangeblock.' : '.'; + if (block.partial) { + $('#twinkle-arv-blockwarning').css('color', 'black'); // Less severe } - }); - work_area.append({ - type: 'input', - name: 'badid', - label: 'Revision ID for target page when vandalised:', - tooltip: 'Leave blank for no diff link', - value: Twinkle.getPrefill('vanarticlerevid') || '', - disabled: !Twinkle.getPrefill('vanarticle'), - event: function(e) { - var value = e.target.value; - var root = e.target.form; - root.goodid.disabled = value === ''; - } - }); - work_area.append({ - type: 'input', - name: 'goodid', - label: 'Last good revision ID before vandalism of target page:', - tooltip: 'Leave blank for diff link to previous revision', - value: Twinkle.getPrefill('vanarticlegoodrevid') || '', - disabled: !Twinkle.getPrefill('vanarticle') || Twinkle.getPrefill('vanarticlerevid') - }); - work_area.append({ - type: 'checkbox', - name: 'arvtype', - list: [ - { - label: 'Vandalism after final (level 4 or 4im) warning given', - value: 'final' - }, - { - label: 'Vandalism after recent (within 1 day) release of block', - value: 'postblock' - }, - { - label: 'Evidently a vandalism-only account', - value: 'vandalonly', - disabled: mw.util.isIPAddress(root.uid.value, true) - }, - { - label: 'Account is a promotion-only account', - value: 'promoonly', - disabled: mw.util.isIPAddress(root.uid.value, true) - }, - { - label: 'Account is evidently a spambot or a compromised account', - value: 'spambot' + $('#twinkle-arv-blockwarning').text(message); + } + }).post(); + + + // We must init the + var evt = document.createEvent('Event'); + evt.initEvent('change', true, true); + result.category.dispatchEvent(evt); + }; + + Twinkle.arv.callback.changeCategory = function (e) { + var value = e.target.value; + var root = e.target.form; + var old_area = Morebits.quickForm.getElements(root, 'work_area')[0]; + var work_area = null; + + switch (value) { + case 'aiv': + /* falls through */ + default: + work_area = new Morebits.quickForm.element({ + type: 'field', + label: 'Report user for vandalism', + name: 'work_area' + }); + work_area.append({ + type: 'input', + name: 'page', + label: 'Primary linked page:', + tooltip: 'Leave blank to not link to the page in the report', + value: Twinkle.getPrefill('vanarticle') || '', + event: function(e) { + var value = e.target.value; + var root = e.target.form; + if (value === '') { + root.badid.disabled = root.goodid.disabled = true; + } else { + root.badid.disabled = false; + root.goodid.disabled = root.badid.value === ''; + } } - ] - }); - work_area.append({ - type: 'textarea', - name: 'reason', - label: 'Comment:' - }); - work_area = work_area.render(); - old_area.parentNode.replaceChild(work_area, old_area); - break; - case 'username': - work_area = new Morebits.quickForm.element({ - type: 'field', - label: 'Report username violation', - name: 'work_area' - }); - work_area.append({ - type: 'header', - label: 'Type(s) of inappropriate username', - tooltip: 'Wikipedia does not allow usernames that are misleading, promotional, offensive or disruptive. Domain names and email addresses are likewise prohibited. These criteria apply to both usernames and signatures. Usernames that are inappropriate in another language, or that represent an inappropriate name with misspellings and substitutions, or do so indirectly or by implication, are still considered inappropriate.' - }); - work_area.append({ - type: 'checkbox', - name: 'arvtype', - list: [ - { - label: 'Misleading username', - value: 'misleading', - tooltip: 'Misleading usernames imply relevant, misleading things about the contributor. For example, misleading points of fact, an impression of undue authority, or usernames giving the impression of a bot account.' - }, - { - label: 'Promotional username', - value: 'promotional', - tooltip: 'Promotional usernames are advertisements for a company, website or group. Please do not report these names to UAA unless the user has also made promotional edits related to the name.' - }, - { - label: 'Offensive username', - value: 'offensive', - tooltip: 'Offensive usernames make harmonious editing difficult or impossible.' - }, - { - label: 'Disruptive username', - value: 'disruptive', - tooltip: 'Disruptive usernames include outright trolling or personal attacks, or otherwise show a clear intent to disrupt Wikipedia.' + }); + work_area.append({ + type: 'input', + name: 'badid', + label: 'Revision ID for target page when vandalised:', + tooltip: 'Leave blank for no diff link', + value: Twinkle.getPrefill('vanarticlerevid') || '', + disabled: !Twinkle.getPrefill('vanarticle'), + event: function(e) { + var value = e.target.value; + var root = e.target.form; + root.goodid.disabled = value === ''; } - ] - }); - work_area.append({ - type: 'textarea', - name: 'reason', - label: 'Comment:' - }); - work_area = work_area.render(); - old_area.parentNode.replaceChild(work_area, old_area); - break; - - case 'puppet': - work_area = new Morebits.quickForm.element({ - type: 'field', - label: 'Report suspected sockpuppet', - name: 'work_area' - }); - work_area.append( - { + }); + work_area.append({ type: 'input', - name: 'sockmaster', - label: 'Sockpuppeteer', - tooltip: 'The username of the sockpuppeteer (sockmaster) without the "User:" prefix' - } - ); - work_area.append({ - type: 'textarea', - label: 'Evidence:', - name: 'evidence', - tooltip: 'Your evidence should make it clear that each of these users is likely to be abusing multiple accounts. Usually this means diffs, page histories or other information that justifies why the users are a) the same and b) disruptive. This should be just evidence and information needed to judge the matter. Avoid all other discussion that is not evidence of sockpuppetry.' - }); - work_area.append({ - type: 'checkbox', - list: [ + name: 'goodid', + label: 'Last good revision ID before vandalism of target page:', + tooltip: 'Leave blank for diff link to previous revision', + value: Twinkle.getPrefill('vanarticlegoodrevid') || '', + disabled: !Twinkle.getPrefill('vanarticle') || Twinkle.getPrefill('vanarticlerevid') + }); + work_area.append({ + type: 'checkbox', + name: 'arvtype', + list: [ + { + label: 'Vandalism after final (level 4 or 4im) warning given', + value: 'final' + }, + { + label: 'Vandalism after recent (within 1 day) release of block', + value: 'postblock' + }, + { + label: 'Evidently a vandalism-only account', + value: 'vandalonly', + disabled: mw.util.isIPAddress(root.uid.value, true) + }, + { + label: 'Account is a promotion-only account', + value: 'promoonly', + disabled: mw.util.isIPAddress(root.uid.value, true) + }, + { + label: 'Account is evidently a spambot or a compromised account', + value: 'spambot' + } + ] + }); + work_area.append({ + type: 'textarea', + name: 'reason', + label: 'Comment:' + }); + work_area = work_area.render(); + old_area.parentNode.replaceChild(work_area, old_area); + break; + case 'username': + work_area = new Morebits.quickForm.element({ + type: 'field', + label: 'Report username violation', + name: 'work_area' + }); + work_area.append({ + type: 'header', + label: 'Type(s) of inappropriate username', + tooltip: 'Wikipedia does not allow usernames that are misleading, promotional, offensive or disruptive. Domain names and email addresses are likewise prohibited. These criteria apply to both usernames and signatures. Usernames that are inappropriate in another language, or that represent an inappropriate name with misspellings and substitutions, or do so indirectly or by implication, are still considered inappropriate.' + }); + work_area.append({ + type: 'checkbox', + name: 'arvtype', + list: [ + { + label: 'Misleading username', + value: 'misleading', + tooltip: 'Misleading usernames imply relevant, misleading things about the contributor. For example, misleading points of fact, an impression of undue authority, or usernames giving the impression of a bot account.' + }, + { + label: 'Promotional username', + value: 'promotional', + tooltip: 'Promotional usernames are advertisements for a company, website or group. Please do not report these names to UAA unless the user has also made promotional edits related to the name.' + }, + { + label: 'Offensive username', + value: 'offensive', + tooltip: 'Offensive usernames make harmonious editing difficult or impossible.' + }, + { + label: 'Disruptive username', + value: 'disruptive', + tooltip: 'Disruptive usernames include outright trolling or personal attacks, or otherwise show a clear intent to disrupt Wikipedia.' + } + ] + }); + work_area.append({ + type: 'textarea', + name: 'reason', + label: 'Comment:' + }); + work_area = work_area.render(); + old_area.parentNode.replaceChild(work_area, old_area); + break; + + case 'puppet': + work_area = new Morebits.quickForm.element({ + type: 'field', + label: 'Report suspected sockpuppet', + name: 'work_area' + }); + work_area.append( { + type: 'input', + name: 'sockmaster', + label: 'Sockpuppeteer', + tooltip: 'The username of the sockpuppeteer (sockmaster) without the "User:" prefix' + } + ); + work_area.append({ + type: 'textarea', + label: 'Evidence:', + name: 'evidence', + tooltip: 'Your evidence should make it clear that each of these users is likely to be abusing multiple accounts. Usually this means diffs, page histories or other information that justifies why the users are a) the same and b) disruptive. This should be just evidence and information needed to judge the matter. Avoid all other discussion that is not evidence of sockpuppetry.' + }); + work_area.append({ + type: 'checkbox', + list: [ + { + label: 'Request CheckUser', + name: 'checkuser', + tooltip: 'CheckUser is a tool used to obtain technical evidence related to a sockpuppetry allegation. It will not be used without good cause, which you must clearly demonstrate. Make sure your evidence explains why using the tool is appropriate. It will not be used to publicly connect user accounts and IP addresses.' + } + ] + }); + work_area = work_area.render(); + old_area.parentNode.replaceChild(work_area, old_area); + break; + case 'sock': + work_area = new Morebits.quickForm.element({ + type: 'field', + label: 'Report suspected sockpuppeteer', + name: 'work_area' + }); + work_area.append( + { + type: 'dyninput', + name: 'sockpuppet', + label: 'Sockpuppets', + sublabel: 'Sock:', + tooltip: 'The username of the sockpuppet without the "User:" prefix', + min: 2 + }); + work_area.append({ + type: 'textarea', + label: 'Evidence:', + name: 'evidence', + tooltip: 'Your evidence should make it clear that each of these users is likely to be abusing multiple accounts. Usually this means diffs, page histories or other information that justifies why the users are a) the same and b) disruptive. This should be just evidence and information needed to judge the matter. Avoid all other discussion that is not evidence of sockpuppetry.' + }); + work_area.append({ + type: 'checkbox', + list: [ { label: 'Request CheckUser', name: 'checkuser', tooltip: 'CheckUser is a tool used to obtain technical evidence related to a sockpuppetry allegation. It will not be used without good cause, which you must clearly demonstrate. Make sure your evidence explains why using the tool is appropriate. It will not be used to publicly connect user accounts and IP addresses.' - } - ] - }); - work_area = work_area.render(); - old_area.parentNode.replaceChild(work_area, old_area); - break; - case 'sock': - work_area = new Morebits.quickForm.element({ - type: 'field', - label: 'Report suspected sockpuppeteer', - name: 'work_area' - }); - work_area.append( - { - type: 'dyninput', - name: 'sockpuppet', - label: 'Sockpuppets', - sublabel: 'Sock:', - tooltip: 'The username of the sockpuppet without the "User:" prefix', - min: 2 + } ] }); - work_area.append({ - type: 'textarea', - label: 'Evidence:', - name: 'evidence', - tooltip: 'Your evidence should make it clear that each of these users is likely to be abusing multiple accounts. Usually this means diffs, page histories or other information that justifies why the users are a) the same and b) disruptive. This should be just evidence and information needed to judge the matter. Avoid all other discussion that is not evidence of sockpuppetry.' - }); - work_area.append({ - type: 'checkbox', - list: [ { - label: 'Request CheckUser', - name: 'checkuser', - tooltip: 'CheckUser is a tool used to obtain technical evidence related to a sockpuppetry allegation. It will not be used without good cause, which you must clearly demonstrate. Make sure your evidence explains why using the tool is appropriate. It will not be used to publicly connect user accounts and IP addresses.' - } ] - }); - work_area = work_area.render(); - old_area.parentNode.replaceChild(work_area, old_area); - break; - case 'an3': - work_area = new Morebits.quickForm.element({ - type: 'field', - label: 'Report edit warring', - name: 'work_area' - }); - work_area.append({ - type: 'input', - name: 'page', - label: 'Page', - tooltip: 'The page being reported' - }); - work_area.append({ - type: 'button', - name: 'load', - label: 'Load', - event: function(e) { - var root = e.target.form; - - var date = new Morebits.date().subtract(48, 'hours'); // all since 48 hours - - // Run for each AN3 field - var getAN3Entries = function(field, rvuser, titles) { - var $field = $(root).find('[name=' + field + ']'); - $field.find('.entry').remove(); - - new mw.Api().get({ - action: 'query', - prop: 'revisions', - format: 'json', - rvprop: 'sha1|ids|timestamp|parsedcomment|comment', - rvlimit: 500, // intentionally limited - rvend: date.toISOString(), - rvuser: rvuser, - indexpageids: true, - titles: titles - }).done(function(data) { - var pageid = data.query.pageids[0]; - var page = data.query.pages[pageid]; - if (!page.revisions) { - $('None found').appendTo($field); - } else { - for (var i = 0; i < page.revisions.length; ++i) { - var rev = page.revisions[i]; - var $entry = $('
', { + work_area = work_area.render(); + old_area.parentNode.replaceChild(work_area, old_area); + break; + case 'an3': + work_area = new Morebits.quickForm.element({ + type: 'field', + label: 'Report edit warring', + name: 'work_area' + }); + work_area.append({ + type: 'input', + name: 'page', + label: 'Page', + tooltip: 'The page being reported' + }); + work_area.append({ + type: 'button', + name: 'load', + label: 'Load', + event: function(e) { + var root = e.target.form; + + var date = new Morebits.date().subtract(48, 'hours'); // all since 48 hours + + // Run for each AN3 field + var getAN3Entries = function(field, rvuser, titles) { + var $field = $(root).find('[name=' + field + ']'); + $field.find('.entry').remove(); + + new mw.Api().get({ + action: 'query', + prop: 'revisions', + format: 'json', + rvprop: 'sha1|ids|timestamp|parsedcomment|comment', + rvlimit: 500, // intentionally limited + rvend: date.toISOString(), + rvuser: rvuser, + indexpageids: true, + titles: titles + }).done(function(data) { + var pageid = data.query.pageids[0]; + var page = data.query.pages[pageid]; + if (!page.revisions) { + $('None found').appendTo($field); + } else { + for (var i = 0; i < page.revisions.length; ++i) { + var rev = page.revisions[i]; + var $entry = $('
', { + class: 'entry' + }); + var $input = $('', { + type: 'checkbox', + name: 's_' + field, + value: rev.revid + }); + $input.data('revinfo', rev); + $input.appendTo($entry); + var comment = ''; + // revdel/os + if (typeof rev.commenthidden === 'string') { + comment += '(comment hidden)'; + } else { + comment += '"' + rev.parsedcomment + '"'; + } + comment += ' at ' + new Morebits.date(rev.timestamp).calendar() + ''; + $entry.append(comment).appendTo($field); + } + } + + // add free form input for resolves + if (field === 'resolves') { + var $free_entry = $('
', { class: 'entry' }); - var $input = $('', { - type: 'checkbox', - name: 's_' + field, - value: rev.revid + var $free_input = $('', { + type: 'text', + name: 's_resolves_free' }); - $input.data('revinfo', rev); - $input.appendTo($entry); - var comment = ''; - // revdel/os - if (typeof rev.commenthidden === 'string') { - comment += '(comment hidden)'; - } else { - comment += '"' + rev.parsedcomment + '"'; - } - comment += ' at ' + new Morebits.date(rev.timestamp).calendar() + ''; - $entry.append(comment).appendTo($field); + + var $free_label = $('