MediaWiki:Gadget-twinklearv.js: Difference between revisions
Repo at 3825f474: Remove duplicate sockpuppets from report
m (1 revision imported) |
imported>Amorymeltzer (Repo at 3825f474: Remove duplicate sockpuppets from report) |
||
(3 intermediate revisions by one other user not shown) | |||
Line 1: | Line 1: | ||
//<nowiki> | // <nowiki> | ||
(function($){ | (function($) { | ||
Line 10: | Line 10: | ||
**************************************** | **************************************** | ||
* Mode of invocation: Tab ("ARV") | * Mode of invocation: Tab ("ARV") | ||
* Active on: | * Active on: Any page with relevant user name (userspace, contribs, etc.) | ||
*/ | */ | ||
Twinkle.arv = function twinklearv() { | Twinkle.arv = function twinklearv() { | ||
var username = mw.config.get('wgRelevantUserName'); | var username = mw.config.get('wgRelevantUserName'); | ||
if ( !username ) { | if (!username || username === mw.config.get('wgUserName')) { | ||
return; | return; | ||
} | } | ||
var | 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); }, | Twinkle.addPortletLink(function() { | ||
Twinkle.arv.callback(username, isIP); | |||
}, 'ARV', 'tw-arv', 'Report ' + userType + ' to administrators'); | |||
}; | }; | ||
Twinkle.arv.callback = function ( uid ) { | 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 | 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 | |||
}); | |||
form.append( { type: 'submit' } ); | |||
form.append( { | |||
var result = form.render(); | var result = form.render(); | ||
Window.setContent( result ); | Window.setContent(result); | ||
Window.display(); | 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; | |||
} | |||
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); | |||
} | |||
}).post(); | |||
// We must init the | // We must init the | ||
var evt = document.createEvent( | var evt = document.createEvent('Event'); | ||
evt.initEvent( 'change', true, true ); | evt.initEvent('change', true, true); | ||
result.category.dispatchEvent( evt ); | result.category.dispatchEvent(evt); | ||
}; | }; | ||
Line 96: | Line 136: | ||
var value = e.target.value; | var value = e.target.value; | ||
var root = e.target.form; | var root = e.target.form; | ||
var old_area = Morebits.quickForm.getElements(root, | var old_area = Morebits.quickForm.getElements(root, 'work_area')[0]; | ||
var work_area = null; | var work_area = null; | ||
switch( value ) { | switch (value) { | ||
case 'aiv': | |||
/* falls through */ | /* falls through */ | ||
default: | |||
work_area = new Morebits.quickForm.element({ | |||
type: 'field', | type: 'field', | ||
label: 'Report user for vandalism', | label: 'Report user for vandalism', | ||
name: 'work_area' | name: 'work_area' | ||
} ); | }); | ||
work_area.append({ | |||
type: 'input', | type: 'input', | ||
name: 'page', | name: 'page', | ||
label: 'Primary linked page: ', | label: 'Primary linked page: ', | ||
tooltip: 'Leave blank to not link to the page in the report', | tooltip: 'Leave blank to not link to the page in the report', | ||
value: | value: mw.util.getParamValue('vanarticle') || '', | ||
event: function(e) { | event: function(e) { | ||
var value = e.target.value; | var value = e.target.value; | ||
var root = e.target.form; | var root = e.target.form; | ||
if( value === '' ) { | if (value === '') { | ||
root.badid.disabled = root.goodid.disabled = true; | root.badid.disabled = root.goodid.disabled = true; | ||
} else { | } else { | ||
Line 124: | Line 164: | ||
} | } | ||
} | } | ||
} ); | }); | ||
work_area.append({ | |||
type: 'input', | type: 'input', | ||
name: 'badid', | name: 'badid', | ||
label: 'Revision ID for target page when vandalised: ', | label: 'Revision ID for target page when vandalised: ', | ||
tooltip: 'Leave blank for no diff link', | tooltip: 'Leave blank for no diff link', | ||
value: | value: mw.util.getParamValue('vanarticlerevid') || '', | ||
disabled: ! | disabled: !mw.util.getParamValue('vanarticle'), | ||
event: function(e) { | event: function(e) { | ||
var value = e.target.value; | var value = e.target.value; | ||
Line 137: | Line 177: | ||
root.goodid.disabled = value === ''; | root.goodid.disabled = value === ''; | ||
} | } | ||
} ); | }); | ||
work_area.append({ | |||
type: 'input', | type: 'input', | ||
name: 'goodid', | name: 'goodid', | ||
label: 'Last good revision ID before vandalism of target page: ', | label: 'Last good revision ID before vandalism of target page: ', | ||
tooltip: 'Leave blank for diff link to previous revision', | tooltip: 'Leave blank for diff link to previous revision', | ||
value: | value: mw.util.getParamValue('vanarticlegoodrevid') || '', | ||
disabled: ! | disabled: !mw.util.getParamValue('vanarticle') || mw.util.getParamValue('vanarticlerevid') | ||
} ); | }); | ||
work_area.append({ | |||
type: 'checkbox', | type: 'checkbox', | ||
name: 'arvtype', | name: 'arvtype', | ||
Line 161: | Line 201: | ||
label: 'Evidently a vandalism-only account', | label: 'Evidently a vandalism-only account', | ||
value: 'vandalonly', | value: 'vandalonly', | ||
disabled: | 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', | label: 'Account is evidently a spambot or a compromised account', | ||
value: 'spambot' | value: 'spambot' | ||
} | } | ||
] | ] | ||
} ); | }); | ||
work_area.append({ | |||
type: 'textarea', | type: 'textarea', | ||
name: 'reason', | name: 'reason', | ||
label: 'Comment: ' | 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', | type: 'field', | ||
label: 'Report username violation', | label: 'Report username violation', | ||
name: 'work_area' | name: 'work_area' | ||
} ); | }); | ||
work_area.append({ | |||
type: 'header', | type: 'header', | ||
label: 'Type(s) of inappropriate username', | 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.' | 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', | type: 'checkbox', | ||
name: 'arvtype', | name: 'arvtype', | ||
Line 222: | Line 263: | ||
} | } | ||
] | ] | ||
} ); | }); | ||
work_area.append({ | |||
type: 'textarea', | type: 'textarea', | ||
name: 'reason', | name: 'reason', | ||
label: 'Comment:' | 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', | type: 'field', | ||
label: 'Report suspected sockpuppet', | label: 'Report suspected sockpuppet', | ||
name: 'work_area' | 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', | type: 'textarea', | ||
label: 'Evidence:', | label: 'Evidence:', | ||
name: 'evidence', | name: 'evidence', | ||
tooltip: ' | 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', | type: 'checkbox', | ||
list: [ | list: [ | ||
{ | { | ||
label: 'Request CheckUser | label: 'Request CheckUser', | ||
name: 'checkuser', | name: 'checkuser', | ||
tooltip: 'CheckUser is a tool used to obtain technical evidence related to a | 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.' | ||
}, | }, | ||
{ | { | ||
label: 'Notify reported users', | label: 'Notify reported users', | ||
name: 'notify', | name: 'notify', | ||
tooltip: 'Notification is not mandatory. In many cases, especially of chronic sockpuppeteers, notification may be counterproductive. However, especially in less egregious cases involving users who | tooltip: 'Notification is not mandatory. In many cases, especially of chronic sockpuppeteers, notification may be counterproductive. However, especially in less egregious cases involving users who have not been reported before, notification may make the cases fairer and also appear to be fairer in the eyes of the accused. Use your judgment.' | ||
} | } | ||
] | ] | ||
} ); | }); | ||
work_area = work_area.render(); | |||
old_area.parentNode.replaceChild(work_area, old_area); | |||
break; | |||
case 'sock': | |||
work_area = new Morebits.quickForm.element({ | |||
type: 'field', | type: 'field', | ||
label: 'Report suspected sockpuppeteer', | label: 'Report suspected sockpuppeteer', | ||
name: 'work_area' | 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', | type: 'textarea', | ||
label: 'Evidence:', | label: 'Evidence:', | ||
name: 'evidence', | name: 'evidence', | ||
tooltip: ' | 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', | type: 'checkbox', | ||
list: [ { | list: [ { | ||
label: 'Request CheckUser | label: 'Request CheckUser', | ||
name: 'checkuser', | name: 'checkuser', | ||
tooltip: 'CheckUser is a tool used to obtain technical evidence related to a | 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.' | ||
}, { | }, { | ||
label: 'Notify reported users', | label: 'Notify reported users', | ||
name: 'notify', | name: 'notify', | ||
tooltip: 'Notification is not mandatory. In many cases, especially of chronic sockpuppeteers, notification may be counterproductive. However, especially in less egregious cases involving users who | tooltip: 'Notification is not mandatory. In many cases, especially of chronic sockpuppeteers, notification may be counterproductive. However, especially in less egregious cases involving users who have not been reported before, notification may make the cases fairer and also appear to be fairer in the eyes of the accused. Use your judgment.' | ||
} ] | } ] | ||
} ); | }); | ||
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' | 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) { | |||
$('<span class="entry">None found</span>').appendTo($field); | |||
} else { | |||
for (var i = 0; i < page.revisions.length; ++i) { | |||
var rev = page.revisions[i]; | |||
var $entry = $('<div/>', { | |||
class: 'entry' | |||
}); | |||
var $input = $('<input/>', { | |||
type: 'checkbox', | |||
name: 's_' + field, | |||
value: rev.revid | |||
}); | |||
$input.data('revinfo', rev); | |||
$input.appendTo($entry); | |||
var comment = '<span>'; | |||
// revdel/os | |||
if (typeof rev.commenthidden === 'string') { | |||
comment += '(comment hidden)'; | |||
} else { | |||
comment += '"' + rev.parsedcomment + '"'; | |||
} | |||
comment += ' at <a href="' + mw.config.get('wgScript') + '?diff=' + rev.revid + '">' + new Morebits.date(rev.timestamp).calendar() + '</a></span>'; | |||
$entry.append(comment).appendTo($field); | |||
} | |||
} | |||
// add free form input for resolves | |||
if (field === 'resolves') { | |||
var $free_entry = $('<div/>', { | |||
class: 'entry' | |||
}); | |||
var $free_input = $('<input/>', { | |||
type: 'text', | |||
name: 's_resolves_free' | |||
}); | |||
var $free_label = $('<label/>', { | |||
for: 's_resolves_free', | |||
html: 'URL link of diff with additional discussions: ' | |||
}); | |||
$free_entry.append($free_label).append($free_input).appendTo($field); | |||
} | |||
}).fail(function() { | |||
$('<span class="entry">API failure, reload page and try again</span>').appendTo($field); | |||
}); | }); | ||
}; | |||
} | |||
// warnings | |||
var uid = root.uid.value; | |||
getAN3Entries('warnings', mw.config.get('wgUserName'), 'User talk:' + uid); | |||
// diffs and resolves require a valid page | |||
var page = root.page.value; | |||
if (page) { | |||
// diffs | |||
getAN3Entries('diffs', uid, page); | |||
// resolutions | |||
var t = new mw.Title(page); | |||
var talk_page = t.getTalkPage().getPrefixedText(); | |||
getAN3Entries('resolves', mw.config.get('wgUserName'), talk_page); | |||
} else { | |||
$(root).find('[name=diffs]').find('.entry').remove(); | |||
$(root).find('[name=resolves]').find('.entry').remove(); | |||
} | |||
$ | |||
} | } | ||
} | |||
}); | |||
work_area.append({ | |||
type: 'field', | |||
name: 'diffs', | |||
label: 'User\'s reverts', | |||
tooltip: 'Select the edits you believe are reverts' | |||
}); | |||
work_area.append({ | |||
type: 'field', | |||
name: 'warnings', | |||
label: 'Warnings given to subject', | |||
tooltip: 'You must have warned the subject before reporting' | |||
}); | |||
work_area.append({ | |||
type: 'field', | |||
name: 'resolves', | |||
label: 'Resolution initiatives', | |||
tooltip: 'You should have tried to resolve the issue on the talk page first' | |||
}); | |||
work_area.append({ | |||
type: 'textarea', | |||
label: 'Comment:', | |||
name: 'comment' | |||
}); | |||
work_area = work_area.render(); | |||
old_area.parentNode.replaceChild(work_area, old_area); | |||
break; | |||
} | } | ||
}; | }; | ||
Line 498: | Line 487: | ||
Twinkle.arv.callback.evaluate = function(e) { | Twinkle.arv.callback.evaluate = function(e) { | ||
var form = e.target; | var form = e.target; | ||
var reason = | var reason = ''; | ||
var comment = | var comment = ''; | ||
if ( form.reason ) { | if (form.reason) { | ||
comment = form.reason.value; | comment = form.reason.value; | ||
} | } | ||
Line 506: | Line 495: | ||
var types; | var types; | ||
switch( form.category.value ) { | switch (form.category.value) { | ||
// Report user for vandalism | // Report user for vandalism | ||
Line 512: | Line 501: | ||
/* falls through */ | /* falls through */ | ||
default: | default: | ||
types = form.getChecked( 'arvtype' ); | types = form.getChecked('arvtype'); | ||
if( !types.length && comment === '' ) { | if (!types.length && comment === '') { | ||
alert( 'You must specify some reason' ); | alert('You must specify some reason'); | ||
return; | return; | ||
} | } | ||
types = types.map( function(v) { | types = types.map(function(v) { | ||
switch (v) { | |||
case 'final': | |||
return 'vandalism after final warning'; | |||
case 'postblock': | |||
return 'vandalism after recent release of block'; | |||
case 'vandalonly': | |||
return 'actions evidently indicate a vandalism-only account'; | |||
case 'promoonly': | |||
return 'account is being used only for promotional purposes'; | |||
case 'spambot': | |||
return 'account is evidently a spambot or a compromised account'; | |||
default: | default: | ||
return 'unknown reason'; | |||
} | |||
}).join('; '); | |||
// | if (form.page.value !== '') { | ||
reason = 'On | // Allow links to redirects, files, and categories | ||
reason = 'On {{No redirect|:' + form.page.value + '}}'; | |||
if ( form.badid.value !== '' ) { | if (form.badid.value !== '') { | ||
reason += ' ({{diff|' + form.page.value + '|' + form.badid.value + '|' + form.goodid.value + '|diff}})'; | reason += ' ({{diff|' + form.page.value + '|' + form.badid.value + '|' + form.goodid.value + '|diff}})'; | ||
} | } | ||
Line 547: | Line 535: | ||
} | } | ||
if ( types ) { | if (types) { | ||
reason += | reason += ' ' + types; | ||
} | } | ||
if (comment !== | if (comment !== '') { | ||
reason += (reason === | reason += (reason === '' ? '' : '. ') + comment; | ||
} | } | ||
reason = reason.trim(); | reason = reason.trim(); | ||
if ( | if (!/[.?!;]$/.test(reason)) { | ||
reason += | reason += '.'; | ||
} | } | ||
reason += | reason += ' ~~~~'; | ||
reason = reason.replace(/\r?\n/g, | reason = reason.replace(/\r?\n/g, '\n*:'); // indent newlines | ||
Morebits.simpleWindow.setButtonsEnabled( false ); | Morebits.simpleWindow.setButtonsEnabled(false); | ||
Morebits.status.init( form ); | Morebits.status.init(form); | ||
Morebits.wiki.actionCompleted.redirect = | Morebits.wiki.actionCompleted.redirect = 'Wikipedia:Administrator intervention against vandalism'; | ||
Morebits.wiki.actionCompleted.notice = | Morebits.wiki.actionCompleted.notice = 'Reporting complete'; | ||
var aivPage = new Morebits.wiki.page( 'Wikipedia:Administrator intervention against vandalism', 'Processing AIV request' ); | var aivPage = new Morebits.wiki.page('Wikipedia:Administrator intervention against vandalism', 'Processing AIV request'); | ||
aivPage.setPageSection( 1 ); | aivPage.setPageSection(1); | ||
aivPage.setFollowRedirect( true ); | aivPage.setFollowRedirect(true); | ||
aivPage.load( function() { | aivPage.load(function() { | ||
var text = aivPage.getPageText(); | var text = aivPage.getPageText(); | ||
var $aivLink = '<a target="_blank" href="/wiki/WP:AIV">WP:AIV</a>'; | |||
// check if user has already been reported | // check if user has already been reported | ||
if (new RegExp( | if (new RegExp('\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*' + Morebits.string.escapeRegExp(uid) + '\\s*\\}\\}').test(text)) { | ||
aivPage.getStatusElement().error( 'Report already present, will not add a new one' ); | aivPage.getStatusElement().error('Report already present, will not add a new one'); | ||
Morebits.status.printUserText( reason, 'The comments you typed are provided below, in case you wish to manually post them under the existing report for this user at | Morebits.status.printUserText(reason, 'The comments you typed are provided below, in case you wish to manually post them under the existing report for this user at ' + $aivLink + ':'); | ||
return; | return; | ||
} | } | ||
aivPage.getStatusElement().status( 'Adding new report...' ); | |||
// then check for any bot reports | |||
var tb2Page = new Morebits.wiki.page('Wikipedia:Administrator intervention against vandalism/TB2', 'Checking bot reports'); | |||
tb2Page.load(function() { | |||
} ); | var tb2Text = tb2Page.getPageText(); | ||
var tb2statelem = tb2Page.getStatusElement(); | |||
if (new RegExp('\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*' + Morebits.string.escapeRegExp(uid) + '\\s*\\}\\}').test(tb2Text)) { | |||
if (confirm('The user ' + uid + ' has already been reported by a bot. Do you wish to make the report anyway?')) { | |||
tb2statelem.info('Proceeded despite bot report'); | |||
} else { | |||
tb2statelem.error('Report from a bot is already present, stopping'); | |||
Morebits.status.printUserText(reason, 'The comments you typed are provided below, in case you wish to manually post them at ' + $aivLink + ':'); | |||
return; | |||
} | |||
} else { | |||
tb2statelem.info('No conflicting bot reports'); | |||
} | |||
aivPage.getStatusElement().status('Adding new report...'); | |||
aivPage.setEditSummary('Reporting [[Special:Contributions/' + uid + '|' + uid + ']].'); | |||
aivPage.setChangeTags(Twinkle.changeTags); | |||
aivPage.setAppendText('\n*{{' + (mw.util.isIPAddress(uid, true) ? 'IPvandal' : 'vandal') + '|' + (/=/.test(uid) ? '1=' : '') + uid + '}} – ' + reason); | |||
aivPage.append(); | |||
}); | |||
}); | |||
break; | break; | ||
// Report inappropriate username | // Report inappropriate username | ||
case 'username': | case 'username': | ||
types = form.getChecked( 'arvtype' ).map( Morebits.string.toLowerCaseFirstChar ); | types = form.getChecked('arvtype').map(Morebits.string.toLowerCaseFirstChar); | ||
var hasShared = types.indexOf( 'shared' ) > -1; | var hasShared = types.indexOf('shared') > -1; | ||
if ( hasShared ) { | if (hasShared) { | ||
types.splice( types.indexOf( 'shared' ), 1 ); | types.splice(types.indexOf('shared'), 1); | ||
} | } | ||
if ( types.length <= 2 ) { | if (types.length <= 2) { | ||
types = types.join( ' and ' ); | types = types.join(' and '); | ||
} else { | } else { | ||
types = [ types.slice( 0, -1 ).join( ', ' ), types.slice( -1 ) ].join( ' and ' ); | types = [ types.slice(0, -1).join(', '), types.slice(-1) ].join(' and '); | ||
} | } | ||
var article = 'a'; | var article = 'a'; | ||
if ( /[aeiouwyh]/.test( types[0] || '' ) ) { // non 100% correct, but whatever, including 'h' for Cockney | if (/[aeiouwyh]/.test(types[0] || '')) { // non 100% correct, but whatever, including 'h' for Cockney | ||
article = 'an'; | article = 'an'; | ||
} | } | ||
reason = | reason = '*{{user-uaa|1=' + uid + '}} – '; | ||
if ( types.length || hasShared ) { | if (types.length || hasShared) { | ||
reason += | reason += 'Violation of the username policy as ' + article + ' ' + types + ' username' + | ||
( hasShared ? | (hasShared ? ' that implies shared use. ' : '. '); | ||
} | } | ||
if ( comment !== '' ) { | if (comment !== '') { | ||
reason += Morebits.string.toUpperCaseFirstChar(comment) + | reason += Morebits.string.toUpperCaseFirstChar(comment) + '. '; | ||
} | } | ||
reason += | reason += '~~~~'; | ||
reason = reason.replace(/\r?\n/g, | reason = reason.replace(/\r?\n/g, '\n*:'); // indent newlines | ||
Morebits.simpleWindow.setButtonsEnabled( false ); | Morebits.simpleWindow.setButtonsEnabled(false); | ||
Morebits.status.init( form ); | Morebits.status.init(form); | ||
Morebits.wiki.actionCompleted.redirect = | Morebits.wiki.actionCompleted.redirect = 'Wikipedia:Usernames for administrator attention'; | ||
Morebits.wiki.actionCompleted.notice = | Morebits.wiki.actionCompleted.notice = 'Reporting complete'; | ||
var uaaPage = new Morebits.wiki.page( 'Wikipedia:Usernames for administrator attention', 'Processing UAA request' ); | var uaaPage = new Morebits.wiki.page('Wikipedia:Usernames for administrator attention', 'Processing UAA request'); | ||
uaaPage.setFollowRedirect( true ); | uaaPage.setFollowRedirect(true); | ||
uaaPage.load( function() { | uaaPage.load(function() { | ||
var text = uaaPage.getPageText(); | var text = uaaPage.getPageText(); | ||
// check if user has already been reported | // check if user has already been reported | ||
if (new RegExp( | if (new RegExp('\\{\\{\\s*user-uaa\\s*\\|\\s*(1\\s*=\\s*)?' + Morebits.string.escapeRegExp(uid) + '\\s*(\\||\\})').test(text)) { | ||
uaaPage.getStatusElement().error( 'User is already listed.' ); | uaaPage.getStatusElement().error('User is already listed.'); | ||
Morebits.status.printUserText( reason, 'The comments you typed are provided below, in case you wish to manually post them under the existing report for this user at | var $uaaLink = '<a target="_blank" href="/wiki/WP:UAA">WP:UAA</a>'; | ||
Morebits.status.printUserText(reason, 'The comments you typed are provided below, in case you wish to manually post them under the existing report for this user at ' + $uaaLink + ':'); | |||
return; | return; | ||
} | } | ||
uaaPage.getStatusElement().status( 'Adding new report...' ); | uaaPage.getStatusElement().status('Adding new report...'); | ||
uaaPage.setEditSummary( 'Reporting [[Special:Contributions/' + uid + '|' + uid + ']].' | uaaPage.setEditSummary('Reporting [[Special:Contributions/' + uid + '|' + uid + ']].'); | ||
uaaPage.setPageText( text + | uaaPage.setChangeTags(Twinkle.changeTags); | ||
// Blank newline per [[Special:Permalink/996949310#Spacing]]; see also [[WP:LISTGAP]] and [[WP:INDENTGAP]] | |||
uaaPage.setPageText(text + '\n' + reason + '\n*'); | |||
uaaPage.save(); | uaaPage.save(); | ||
} ); | }); | ||
break; | break; | ||
// WP:SPI | // WP:SPI | ||
case | case 'sock': | ||
/* falls through */ | /* falls through */ | ||
case | case 'puppet': | ||
var sockParameters = { | var sockParameters = { | ||
evidence: form.evidence.value.trim(), | evidence: form.evidence.value.trim(), | ||
Line 650: | Line 664: | ||
}; | }; | ||
var puppetReport = form.category.value === | var puppetReport = form.category.value === 'puppet'; | ||
if (puppetReport && ! | if (puppetReport && !form.sockmaster.value.trim()) { | ||
alert('You have not entered a sockmaster account for this puppet. Consider reporting this account as a sockpuppeteer instead.'); | |||
return; | |||
} else if (!puppetReport && !form.sockpuppet[0].value.trim()) { | |||
alert('You have not entered any sockpuppet account(s) for this sockmaster. Consider reporting this account as a sockpuppet instead.'); | |||
return; | |||
} | } | ||
sockParameters.uid = puppetReport ? form.sockmaster.value.trim() : uid; | sockParameters.uid = puppetReport ? form.sockmaster.value.trim() : uid; | ||
sockParameters.sockpuppets = puppetReport ? [uid] : $.map( $('input:text[name=sockpuppet]',form), function(o){ return $(o).val() || null; }); | sockParameters.sockpuppets = puppetReport ? [uid] : Morebits.array.uniq($.map($('input:text[name=sockpuppet]', form), function(o) { | ||
return $(o).val() || null; | |||
})); | |||
Morebits.simpleWindow.setButtonsEnabled( false ); | Morebits.simpleWindow.setButtonsEnabled(false); | ||
Morebits.status.init( form ); | Morebits.status.init(form); | ||
Twinkle.arv.processSock( sockParameters ); | Twinkle.arv.processSock(sockParameters); | ||
break; | break; | ||
case 'an3': | case 'an3': | ||
var diffs = $.map( $('input:checkbox[name=s_diffs]:checked',form), function(o){ return $(o).data('revinfo'); }); | var diffs = $.map($('input:checkbox[name=s_diffs]:checked', form), function(o) { | ||
return $(o).data('revinfo'); | |||
}); | |||
if (diffs.length < 3 && !confirm( | if (diffs.length < 3 && !confirm('You have selected fewer than three offending edits. Do you wish to make the report anyway?')) { | ||
return; | return; | ||
} | } | ||
var warnings = $.map( $('input:checkbox[name=s_warnings]:checked',form), function(o){ return $(o).data('revinfo'); }); | var warnings = $.map($('input:checkbox[name=s_warnings]:checked', form), function(o) { | ||
return $(o).data('revinfo'); | |||
}); | |||
if(!warnings.length && !confirm( | if (!warnings.length && !confirm('You have not selected any edits where you warned the offender. Do you wish to make the report anyway?')) { | ||
return; | return; | ||
} | } | ||
var resolves = $.map( $('input:checkbox[name=s_resolves]:checked',form), function(o){ return $(o).data('revinfo'); }); | var resolves = $.map($('input:checkbox[name=s_resolves]:checked', form), function(o) { | ||
return $(o).data('revinfo'); | |||
}); | |||
var free_resolves = $('input[name=s_resolves_free]').val(); | var free_resolves = $('input[name=s_resolves_free]').val(); | ||
var an3_next = function(free_resolves) { | var an3_next = function(free_resolves) { | ||
if(!resolves.length && !free_resolves && !confirm( | if (!resolves.length && !free_resolves && !confirm('You have not selected any edits where you tried to resolve the issue. Do you wish to make the report anyway?')) { | ||
return; | return; | ||
} | } | ||
var an3Parameters = { | var an3Parameters = { | ||
uid: uid, | |||
page: form.page.value.trim(), | |||
comment: form.comment.value.trim(), | |||
diffs: diffs, | |||
warnings: warnings, | |||
resolves: resolves, | |||
free_resolves: free_resolves | |||
}; | }; | ||
Morebits.simpleWindow.setButtonsEnabled( false ); | Morebits.simpleWindow.setButtonsEnabled(false); | ||
Morebits.status.init( form ); | Morebits.status.init(form); | ||
Twinkle.arv.processAN3( an3Parameters ); | Twinkle.arv.processAN3(an3Parameters); | ||
}; | }; | ||
if(free_resolves) { | if (free_resolves) { | ||
var oldid=mw.util.getParamValue('oldid',free_resolves); | var query; | ||
var | var diff, oldid; | ||
var specialDiff = /Special:Diff\/(\d+)(?:\/(\S+))?/i.exec(free_resolves); | |||
action: ' | if (specialDiff) { | ||
if (specialDiff[2]) { | |||
oldid = specialDiff[1]; | |||
diff = specialDiff[2]; | |||
} else { | |||
revids | diff = specialDiff[1]; | ||
}).done(function(data){ | } | ||
var pageid = data.query.pageids[0]; | } else { | ||
diff = mw.util.getParamValue('diff', free_resolves); | |||
oldid = mw.util.getParamValue('oldid', free_resolves); | |||
} | |||
var title = mw.util.getParamValue('title', free_resolves); | |||
var diffNum = /^\d+$/.test(diff); // used repeatedly | |||
// rvdiffto in prop=revisions is deprecated, but action=compare doesn't return | |||
// timestamps ([[phab:T247686]]) so we can't rely on it unless necessary. | |||
// Likewise, we can't rely on a meaningful comment for diff=cur. | |||
// Additionally, links like Special:Diff/123/next, Special:Diff/123/456, or ?diff=next&oldid=123 | |||
// would each require making use of rvdir=newer in the revisions API. | |||
// That requires a title parameter, so we have to use compare instead of revisions. | |||
if (oldid && (diff === 'cur' || (!title && (diff === 'next' || diffNum)))) { | |||
query = { | |||
action: 'compare', | |||
fromrev: oldid, | |||
prop: 'ids|title', | |||
format: 'json' | |||
}; | |||
if (diffNum) { | |||
query.torev = diff; | |||
} else { | |||
query.torelative = diff; | |||
} | |||
} else { | |||
query = { | |||
action: 'query', | |||
prop: 'revisions', | |||
rvprop: 'ids|timestamp|comment', | |||
format: 'json', | |||
indexpageids: true | |||
}; | |||
if (diff && oldid) { | |||
if (diff === 'prev') { | |||
query.revids = oldid; | |||
} else { | |||
query.titles = title; | |||
query.rvdir = 'newer'; | |||
query.rvstartid = oldid; | |||
if (diff === 'next' && title) { | |||
query.rvlimit = 2; | |||
} else if (diffNum) { | |||
// Diffs may or may not be consecutive, no limit | |||
query.rvendid = diff; | |||
} | |||
} | |||
} else { | |||
// diff=next|prev|cur with no oldid | |||
// Implies title= exists otherwise it's not a valid diff link (well, it is, but to the Main Page) | |||
if (diff && /^\D+$/.test(diff)) { | |||
query.titles = title; | |||
} else { | |||
query.revids = diff || oldid; | |||
} | |||
} | |||
} | |||
new mw.Api().get(query).done(function(data) { | |||
var page; | |||
if (data.compare && data.compare.fromtitle === data.compare.totitle) { | |||
page = data; | |||
} else if (data.query) { | |||
var pageid = data.query.pageids[0]; | |||
page = data.query.pages[pageid]; | |||
} else { | |||
return; | |||
} | |||
an3_next(page); | an3_next(page); | ||
}).fail(function(data){ | }).fail(function(data) { | ||
console.log( 'API failed :(', data ); | console.log('API failed :(', data); // eslint-disable-line no-console | ||
}); | }); | ||
} else { | } else { | ||
Line 726: | Line 817: | ||
}; | }; | ||
Twinkle.arv.processSock = function( params ) { | Twinkle.arv.processSock = function(params) { | ||
Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed" | Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed" | ||
// notify all user accounts if requested | // notify all user accounts if requested | ||
if (params.notify && params.sockpuppets.length>0) { | if (params.notify && params.sockpuppets.length > 0) { | ||
var notifyEditSummary = | var notifyEditSummary = 'Notifying about suspicion of sockpuppeteering.'; | ||
var notifyText = | var notifyText = '\n\n{{subst:socksuspectnotice|1=' + params.uid + '}} ~~~~'; | ||
// notify user's master account | // notify user's master account | ||
var masterTalkPage = new Morebits.wiki.page( 'User talk:' + params.uid, 'Notifying suspected sockpuppeteer' ); | var masterTalkPage = new Morebits.wiki.page('User talk:' + params.uid, 'Notifying suspected sockpuppeteer'); | ||
masterTalkPage.setFollowRedirect( true ); | masterTalkPage.setFollowRedirect(true); | ||
masterTalkPage.setEditSummary( notifyEditSummary ); | masterTalkPage.setEditSummary(notifyEditSummary); | ||
masterTalkPage.setAppendText( notifyText ); | masterTalkPage.setChangeTags(Twinkle.changeTags); | ||
masterTalkPage.setAppendText(notifyText); | |||
masterTalkPage.append(); | masterTalkPage.append(); | ||
var statusIndicator = new Morebits.status( 'Notifying suspected sockpuppets', '0%' ); | var statusIndicator = new Morebits.status('Notifying suspected sockpuppets', '0%'); | ||
var total = params.sockpuppets.length; | var total = params.sockpuppets.length; | ||
var current = | var current = 0; | ||
// display status of notifications as they progress | // display status of notifications as they progress | ||
var onSuccess = function( sockTalkPage ) { | var onSuccess = function(sockTalkPage) { | ||
var now = parseInt( 100 * ++ | var now = parseInt(100 * ++current / total, 10) + '%'; | ||
statusIndicator.update( now ); | statusIndicator.update(now); | ||
sockTalkPage.getStatusElement().unlink(); | sockTalkPage.getStatusElement().unlink(); | ||
if ( current >= total ) { | if (current >= total) { | ||
statusIndicator.info( now + ' (completed)' ); | statusIndicator.info(now + ' (completed)'); | ||
} | } | ||
}; | }; | ||
Line 759: | Line 851: | ||
// notify each puppet account | // notify each puppet account | ||
for( var i = 0; i < socks.length; ++i ) { | for (var i = 0; i < socks.length; ++i) { | ||
var sockTalkPage = new Morebits.wiki.page( 'User talk:' + socks[i], | var sockTalkPage = new Morebits.wiki.page('User talk:' + socks[i], 'Notification for ' + socks[i]); | ||
sockTalkPage.setFollowRedirect( true ); | sockTalkPage.setFollowRedirect(true); | ||
sockTalkPage.setEditSummary( notifyEditSummary ); | sockTalkPage.setEditSummary(notifyEditSummary); | ||
sockTalkPage.setAppendText( notifyText ); | sockTalkPage.setChangeTags(Twinkle.changeTags); | ||
sockTalkPage.append( onSuccess ); | sockTalkPage.setAppendText(notifyText); | ||
sockTalkPage.append(onSuccess); | |||
} | } | ||
} | } | ||
// prepare the SPI report | // prepare the SPI report | ||
var text = | var text = '\n\n{{subst:SPI report|socksraw=' + | ||
params.sockpuppets.map( function(v) { | params.sockpuppets.map(function(v) { | ||
return '* {{' + (mw.util.isIPAddress(v, true) ? 'checkip' : 'checkuser') + '|1=' + v + '}}'; | |||
}).join('\n') + '\n|evidence=' + params.evidence + ' \n'; | |||
if ( params.checkuser ) { | if (params.checkuser) { | ||
text += | text += '|checkuser=yes'; | ||
} | } | ||
text += | text += '}}'; | ||
var reportpage = 'Wikipedia:Sockpuppet investigations/' + params.uid; | var reportpage = 'Wikipedia:Sockpuppet investigations/' + params.uid; | ||
Morebits.wiki.actionCompleted.redirect = reportpage; | Morebits.wiki.actionCompleted.redirect = reportpage; | ||
Morebits.wiki.actionCompleted.notice = | Morebits.wiki.actionCompleted.notice = 'Reporting complete'; | ||
var spiPage = new Morebits.wiki.page( reportpage, 'Retrieving discussion page' ); | var spiPage = new Morebits.wiki.page(reportpage, 'Retrieving discussion page'); | ||
spiPage.setFollowRedirect( true ); | spiPage.setFollowRedirect(true); | ||
spiPage.setEditSummary( 'Adding new report for [[Special:Contributions/' + params.uid + '|' + params.uid + ']].' | spiPage.setEditSummary('Adding new report for [[Special:Contributions/' + params.uid + '|' + params.uid + ']].'); | ||
spiPage.setAppendText( text ); | spiPage.setChangeTags(Twinkle.changeTags); | ||
spiPage.setAppendText(text); | |||
spiPage.setWatchlist(Twinkle.getPref('spiWatchReport')); | |||
spiPage.append(); | spiPage.append(); | ||
Line 804: | Line 888: | ||
}; | }; | ||
Twinkle.arv.processAN3 = function( params ) { | Twinkle.arv.processAN3 = function(params) { | ||
// prepare the AN3 report | // prepare the AN3 report | ||
var minid; | var minid; | ||
for(var i = 0; i < params.diffs.length; ++i) { | for (var i = 0; i < params.diffs.length; ++i) { | ||
if( params.diffs[i].parentid && (!minid || params.diffs[i].parentid < minid)) { | if (params.diffs[i].parentid && (!minid || params.diffs[i].parentid < minid)) { | ||
minid = params.diffs[i].parentid; | minid = params.diffs[i].parentid; | ||
} | } | ||
} | } | ||
new mw.Api().get({ | |||
action: 'query', | action: 'query', | ||
prop: 'revisions', | prop: 'revisions', | ||
format: 'json', | format: 'json', | ||
rvprop: 'sha1|ids|timestamp|comment', | rvprop: 'sha1|ids|timestamp|comment', | ||
rvlimit: 100, | rvlimit: 100, // intentionally limited | ||
rvstartid: minid, | rvstartid: minid, | ||
rvexcludeuser: params.uid, | rvexcludeuser: params.uid, | ||
indexpageids: true, | indexpageids: true, | ||
titles: params.page | titles: params.page | ||
}).done(function(data){ | }).done(function(data) { | ||
Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed" | Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed" | ||
// In case an edit summary was revdel'd | |||
var hasHiddenComment = function(rev) { | |||
if (!rev.comment && typeof rev.commenthidden === 'string') { | |||
return '(comment hidden)'; | |||
} | |||
return '"' + rev.comment + '"'; | |||
}; | |||
var orig; | var orig; | ||
if(data.length) { | if (data.length) { | ||
var sha1 = data[0].sha1; | var sha1 = data[0].sha1; | ||
for(var i = 1; i < data.length; ++i) { | for (var i = 1; i < data.length; ++i) { | ||
if(data[i].sha1 == sha1) { | if (data[i].sha1 === sha1) { | ||
orig = data[i]; | orig = data[i]; | ||
break; | break; | ||
Line 837: | Line 929: | ||
} | } | ||
if(!orig) { | if (!orig) { | ||
orig = data[0]; | orig = data[0]; | ||
} | } | ||
} | } | ||
var origtext = | var origtext = ''; | ||
if(orig) { | if (orig) { | ||
origtext = '{{diff2|' + orig.revid + '|' + orig.timestamp + '}} | origtext = '{{diff2|' + orig.revid + '|' + orig.timestamp + '}} ' + hasHiddenComment(orig); | ||
} | } | ||
Line 850: | Line 942: | ||
var parentid, lastid; | var parentid, lastid; | ||
for(var j = 0; j < params.diffs.length; ++j) { | for (var j = 0; j < params.diffs.length; ++j) { | ||
var cur = params.diffs[j]; | var cur = params.diffs[j]; | ||
if( cur.revid && cur.revid != parentid || lastid === null ) { | if ((cur.revid && cur.revid !== parentid) || lastid === null) { | ||
lastid = cur.revid; | lastid = cur.revid; | ||
grouped_diffs[lastid] = []; | grouped_diffs[lastid] = []; | ||
Line 860: | Line 952: | ||
} | } | ||
var difftext = $.map(grouped_diffs, function(sub | var difftext = $.map(grouped_diffs, function(sub) { | ||
var ret = | var ret = ''; | ||
if(sub.length >= 2) { | if (sub.length >= 2) { | ||
var last = sub[0]; | var last = sub[0]; | ||
var first = sub.slice(-1)[0]; | var first = sub.slice(-1)[0]; | ||
var label = | var label = 'Consecutive edits made from ' + new Morebits.date(first.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) to ' + new Morebits.date(last.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)'; | ||
ret = | ret = '# {{diff|oldid=' + first.parentid + '|diff=' + last.revid + '|label=' + label + '}}\n'; | ||
} | } | ||
ret += sub.reverse().map(function(v){ | ret += sub.reverse().map(function(v) { | ||
return (sub.length >= 2 ? '#' : '') + '# {{diff2|' + v.revid + '|' + | return (sub.length >= 2 ? '#' : '') + '# {{diff2|' + v.revid + '|' + new Morebits.date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v); | ||
}).join( | }).join('\n'); | ||
return ret; | return ret; | ||
}).reverse().join( | }).reverse().join('\n'); | ||
var warningtext = params.warnings.reverse().map(function(v){ | var warningtext = params.warnings.reverse().map(function(v) { | ||
return '# ' + ' {{diff2|' + v.revid + '|' + | return '# ' + ' {{diff2|' + v.revid + '|' + new Morebits.date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v); | ||
}).join( | }).join('\n'); | ||
var resolvetext = params.resolves.reverse().map(function(v){ | var resolvetext = params.resolves.reverse().map(function(v) { | ||
return '# ' + ' {{diff2|' + v.revid + '|' + | return '# ' + ' {{diff2|' + v.revid + '|' + new Morebits.date(v.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC)}} ' + hasHiddenComment(v); | ||
}).join( | }).join('\n'); | ||
if(params.free_resolves) { | if (params.free_resolves) { | ||
var page = params.free_resolves; | var page = params.free_resolves; | ||
var rev = page.revisions[0]; | if (page.compare) { | ||
resolvetext += '\n# ' + ' {{diff|oldid=' + page.compare.fromrevid + '|diff=' + page.compare.torevid + '|label=Consecutive edits on ' + page.compare.totitle + '}}'; | |||
} else if (page.revisions) { | |||
var revCount = page.revisions.length; | |||
var rev; | |||
if (revCount < 3) { // diff=prev or next | |||
rev = revCount === 1 ? page.revisions[0] : page.revisions[1]; | |||
resolvetext += '\n# ' + ' {{diff2|' + rev.revid + '|' + new Morebits.date(rev.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) on ' + page.title + '}} ' + hasHiddenComment(rev); | |||
} else { // diff and oldid are nonconsecutive | |||
rev = page.revisions[0]; | |||
var revLatest = page.revisions[revCount - 1]; | |||
var label = 'Consecutive edits made from ' + new Morebits.date(rev.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) to ' + new Morebits.date(revLatest.timestamp).format('HH:mm, D MMMM YYYY', 'utc') + ' (UTC) on ' + page.title; | |||
resolvetext += '\n# {{diff|oldid=' + rev.revid + '|diff=' + revLatest.revid + '|label=' + label + '}}\n'; | |||
} | |||
} | |||
} | } | ||
var comment = params.comment.replace(/~*$/g, '').trim(); | var comment = params.comment.replace(/~*$/g, '').trim(); | ||
if(comment) { | if (comment) { | ||
comment += | comment += ' ~~~~'; | ||
} | } | ||
var text = | var text = '\n\n' + '{{subst:AN3 report|diffs=' + difftext + '|warnings=' + warningtext + '|resolves=' + resolvetext + '|pagename=' + params.page + '|orig=' + origtext + '|comment=' + comment + '|uid=' + params.uid + '}}'; | ||
var reportpage = 'Wikipedia:Administrators\' noticeboard/Edit warring'; | var reportpage = 'Wikipedia:Administrators\' noticeboard/Edit warring'; | ||
Morebits.wiki.actionCompleted.redirect = reportpage; | Morebits.wiki.actionCompleted.redirect = reportpage; | ||
Morebits.wiki.actionCompleted.notice = | Morebits.wiki.actionCompleted.notice = 'Reporting complete'; | ||
var an3Page = new Morebits.wiki.page( reportpage, 'Retrieving discussion page' ); | var an3Page = new Morebits.wiki.page(reportpage, 'Retrieving discussion page'); | ||
an3Page.setFollowRedirect( true ); | an3Page.setFollowRedirect(true); | ||
an3Page.setEditSummary( 'Adding new report for [[Special:Contributions/' + params.uid + '|' + params.uid + ']].' | an3Page.setEditSummary('Adding new report for [[Special:Contributions/' + params.uid + '|' + params.uid + ']].'); | ||
an3Page.setAppendText( text ); | an3Page.setChangeTags(Twinkle.changeTags); | ||
an3Page.setAppendText(text); | |||
an3Page.append(); | an3Page.append(); | ||
// notify user | // notify user | ||
var | var notifyText = '\n\n{{subst:an3-notice|1=' + mw.util.wikiUrlencode(params.uid) + '|auto=1}} ~~~~'; | ||
var talkPage = new Morebits.wiki.page( 'User talk:' + params.uid, 'Notifying edit warrior' ); | var talkPage = new Morebits.wiki.page('User talk:' + params.uid, 'Notifying edit warrior'); | ||
talkPage.setFollowRedirect( true ); | talkPage.setFollowRedirect(true); | ||
talkPage.setEditSummary( | talkPage.setEditSummary('Notifying about edit warring noticeboard discussion.'); | ||
talkPage.setAppendText( notifyText ); | talkPage.setChangeTags(Twinkle.changeTags); | ||
talkPage.setAppendText(notifyText); | |||
talkPage.append(); | talkPage.append(); | ||
Morebits.wiki.removeCheckpoint(); // all page updates have been started | Morebits.wiki.removeCheckpoint(); // all page updates have been started | ||
}).fail(function(data){ | }).fail(function(data) { | ||
console.log( 'API failed :(', data ); | console.log('API failed :(', data); // eslint-disable-line no-console | ||
}); | }); | ||
}; | }; | ||
Twinkle.addInitCallback(Twinkle.arv, 'arv'); | |||
})(jQuery); | })(jQuery); | ||
//</nowiki> | // </nowiki> |