diff --git a/modules/twinklearv.js b/modules/twinklearv.js index 9c62fa7ea..e172ce996 100644 --- a/modules/twinklearv.js +++ b/modules/twinklearv.js @@ -471,74 +471,25 @@ Twinkle.arv.callback.changeCategory = function (e) { }; Twinkle.arv.callback.evaluate = function(e) { - const form = e.target; - let reason = ''; - let comment = ''; - if (form.reason) { - comment = form.reason.value; - } - const uid = form.uid.value; + var form = e.target; + var reason = ''; + var input = Morebits.quickForm.getInputData(form); + + var uid = form.uid.value; - let types; - switch (form.category.value) { + switch (input.category) { // Report user for vandalism case 'aiv': /* falls through */ default: - types = form.getChecked('arvtype'); - if (!types.length && comment === '') { + reason = Twinkle.arv.callback.getAivReasonWikitext(input); + + if (reason === null) { alert('You must specify some reason'); return; } - types = types.map((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: - return 'unknown reason'; - } - }).join('; '); - - if (form.page.value !== '') { - // Allow links to redirects, files, and categories - reason = 'On {{No redirect|:' + form.page.value + '}}'; - if (form.badid.value !== '') { - reason += ' ({{diff|' + form.page.value + '|' + form.badid.value + '|' + form.goodid.value + '|diff}})'; - } - reason += ':'; - } - - if (types) { - reason += ' ' + types; - } - - if (comment !== '') { - const reasonEndsInPunctuationOrBlank = /([.?!;:]|^)$/.test(reason); - reason += reasonEndsInPunctuationOrBlank ? '' : '.'; - const reasonIsBlank = reason === ''; - reason += reasonIsBlank ? '' : ' '; - reason += comment; - } - - reason = reason.trim(); - var reasonEndsInPunctuation = /[.?!;]$/.test(reason); - if (!reasonEndsInPunctuation) { - reason += '.'; - } - - reason += ' ~~~~'; - reason = reason.replace(/\r?\n/g, '\n*:'); // indent newlines - Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(form); @@ -554,7 +505,7 @@ Twinkle.arv.callback.evaluate = function(e) { const $aivLink = 'WP:AIV'; // check if user has already been reported - if (new RegExp('\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*' + Morebits.string.escapeRegExp(uid) + '\\s*\\}\\}').test(text)) { + if (new RegExp('\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*' + Morebits.string.escapeRegExp(input.uid) + '\\s*\\}\\}').test(text)) { 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 ' + $aivLink + ':'); return; @@ -566,8 +517,8 @@ Twinkle.arv.callback.evaluate = function(e) { const tb2Text = tb2Page.getPageText(); const 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?')) { + if (new RegExp('\\{\\{\\s*(?:(?:[Ii][Pp])?[Vv]andal|[Uu]serlinks)\\s*\\|\\s*(?:1=)?\\s*' + Morebits.string.escapeRegExp(input.uid) + '\\s*\\}\\}').test(tb2Text)) { + if (confirm('The user ' + input.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'); @@ -579,9 +530,9 @@ Twinkle.arv.callback.evaluate = function(e) { } aivPage.getStatusElement().status('Adding new report...'); - aivPage.setEditSummary('Reporting [[Special:Contributions/' + uid + '|' + uid + ']].'); + aivPage.setEditSummary('Reporting [[Special:Contributions/' + input.uid + '|' + input.uid + ']].'); aivPage.setChangeTags(Twinkle.changeTags); - aivPage.setAppendText('\n*{{vandal|' + (/=/.test(uid) ? '1=' : '') + uid + '}} – ' + reason); + aivPage.setAppendText(Twinkle.arv.callback.buildAivReport(input)); aivPage.append(); }); }); @@ -589,37 +540,9 @@ Twinkle.arv.callback.evaluate = function(e) { // Report inappropriate username case 'username': - types = form.getChecked('arvtype').map(Morebits.string.toLowerCaseFirstChar); - var censorUsername = types.includes('offensive'); // check if the username is marked offensive + var censorUsername = input.arvtype.includes('offensive'); // check if the username is marked offensive - // generate human-readable string, e.g. "misleading and promotional username" - if (types.length <= 2) { - types = types.join(' and '); - } else { - types = [ types.slice(0, -1).join(', '), types.slice(-1) ].join(' and '); - } - - // a or an? - var adjective = 'a'; - if (/[aeiouwyh]/.test(types[0] || '')) { // non 100% correct, but whatever, including 'h' for Cockney - adjective = 'an'; - } - - // generate wikicode to place on [[WP:UAA]] page - reason = '*{{user-uaa|1=' + uid + '}} – '; - if (types.length) { - reason += 'Violation of the username policy as ' + adjective + ' ' + types + ' username. '; - } - if (comment !== '') { - reason += Morebits.string.toUpperCaseFirstChar(comment); - const endsInPeriod = /\.$/.test(comment); - if (!endsInPeriod) { - reason += '.'; - } - reason += ' '; - } - reason += '~~~~'; - reason = reason.replace(/\r?\n/g, '\n*:'); // indent newlines + reason = Twinkle.arv.callback.getUsernameReportWikitext(input); Morebits.simpleWindow.setButtonsEnabled(false); Morebits.status.init(form); @@ -634,14 +557,14 @@ Twinkle.arv.callback.evaluate = function(e) { const text = uaaPage.getPageText(); // check if user has already been reported - if (new RegExp('\\{\\{\\s*user-uaa\\s*\\|\\s*(1\\s*=\\s*)?' + Morebits.string.escapeRegExp(uid) + '\\s*(\\||\\})').test(text)) { + if (new RegExp('\\{\\{\\s*user-uaa\\s*\\|\\s*(1\\s*=\\s*)?' + Morebits.string.escapeRegExp(input.uid) + '\\s*(\\||\\})').test(text)) { uaaPage.getStatusElement().error('User is already listed.'); const $uaaLink = 'WP:UAA'; 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; } uaaPage.getStatusElement().status('Adding new report...'); - uaaPage.setEditSummary('Reporting ' + (censorUsername ? 'an offensive username.' : '[[Special:Contributions/' + uid + '|' + uid + ']].')); + uaaPage.setEditSummary('Reporting ' + (censorUsername ? 'an offensive username.' : '[[Special:Contributions/' + input.uid + '|' + input.uid + ']].')); uaaPage.setChangeTags(Twinkle.changeTags); // Blank newline per [[Special:Permalink/996949310#Spacing]]; see also [[WP:LISTGAP]] and [[WP:INDENTGAP]] @@ -804,6 +727,100 @@ Twinkle.arv.callback.evaluate = function(e) { } }; +Twinkle.arv.callback.getAivReasonWikitext = function(input) { + var text = ''; + var type = input.arvtype; + + if (!type.length && input.reason === '') { + return null; + } + + type = type.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: + return 'unknown reason'; + } + }).join('; '); + + if (input.page !== '') { + // Allow links to redirects, files, and categories + text = 'On {{No redirect|:' + input.page + '}}'; + if (input.badid !== '') { + text += ' ({{diff|' + input.page + '|' + input.badid + '|' + input.goodid + '|diff}})'; + } + text += ':'; + } + + if (type) { + text += ' ' + type; + } + + if (input.reason !== '') { + var textEndsInPunctuationOrBlank = /([.?!;:]|^)$/.test(text); + text += textEndsInPunctuationOrBlank ? '' : '.'; + var textIsBlank = text === ''; + text += textIsBlank ? '' : ' '; + text += input.reason; + } + + text = text.trim(); + var textEndsInPunctuation = /[.?!;]$/.test(text); + if (!textEndsInPunctuation) { + text += '.'; + } + + text += ' ~~~~'; + text = text.replace(/\r?\n/g, '\n*:'); // indent newlines + + return text; +}; + +Twinkle.arv.callback.buildAivReport = function(input) { + return '\n*{{vandal|' + (/=/.test(input.uid) ? '1=' : '') + input.uid + '}} – ' + Twinkle.arv.callback.getAivReasonWikitext(input); +}; + +Twinkle.arv.callback.getUsernameReportWikitext = function(input) { + // generate human-readable string, e.g. "misleading and promotional username" + if (input.arvtype.length <= 2) { + input.arvtype = input.arvtype.join(' and '); + } else { + input.arvtype = [ input.arvtype.slice(0, -1).join(', '), input.arvtype.slice(-1) ].join(' and '); + } + + // a or an? + var adjective = 'a'; + if (/[aeiouwyh]/.test(input.arvtype[0] || '')) { // non 100% correct, but whatever, including 'h' for Cockney + adjective = 'an'; + } + + var text = '*{{user-uaa|1=' + input.uid + '}} – '; + if (input.arvtype.length) { + text += 'Violation of the username policy as ' + adjective + ' ' + input.arvtype + ' username. '; + } + if (input.reason !== '') { + text += Morebits.string.toUpperCaseFirstChar(input.reason); + var endsInPeriod = /\.$/.test(input.reason); + if (!endsInPeriod) { + text += '.'; + } + text += ' '; + } + text += '~~~~'; + text = text.replace(/\r?\n/g, '\n*:'); // indent newlines + + return text; +}; + Twinkle.arv.processSock = function(params) { Morebits.wiki.addCheckpoint(); // prevent notification events from causing an erronous "action completed" diff --git a/modules/twinklerollback.js b/modules/twinklerollback.js index caa20e9ef..6b2d7d77c 100644 --- a/modules/twinklerollback.js +++ b/modules/twinklerollback.js @@ -673,10 +673,18 @@ Twinkle.rollback.callbacks = { break; } - if ((Twinkle.getPref('confirmOnRollback') || - // Mobile user agent taken from [[en:MediaWiki:Gadget-confirmationRollback-mobile.js]] - (Twinkle.getPref('confirmOnMobileRollback') && /Android|webOS|iPhone|iPad|iPod|BlackBerry|Mobile|Opera Mini/i.test(navigator.userAgent))) && - !userHasAlreadyConfirmedAction && !confirm('Reverting page: are you sure?')) { + const needToDisplayConfirmation = + ( + Twinkle.getPref('confirmOnRollback') || + ( + Twinkle.getPref('confirmOnMobileRollback') && + // Mobile user agent taken from [[en:MediaWiki:Gadget-confirmationRollback-mobile.js]] + /Android|webOS|iPhone|iPad|iPod|BlackBerry|Mobile|Opera Mini/i.test(navigator.userAgent) + ) + ) && + !userHasAlreadyConfirmedAction; + + if (needToDisplayConfirmation && !confirm('Reverting page: are you sure?')) { statelem.error('Aborted by user.'); return; } diff --git a/twinkle.js b/twinkle.js index 4e2cb33e7..59d471ff7 100644 --- a/twinkle.js +++ b/twinkle.js @@ -179,6 +179,7 @@ Twinkle.getPref = function twinkleGetPref(name) { if (typeof Twinkle.prefs === 'object' && Twinkle.prefs[name] !== undefined) { return Twinkle.prefs[name]; } + // Old preferences format, used before twinkleoptions.js was a thing if (typeof window.TwinkleConfig === 'object' && window.TwinkleConfig[name] !== undefined) { return window.TwinkleConfig[name]; @@ -186,6 +187,14 @@ Twinkle.getPref = function twinkleGetPref(name) { if (typeof window.FriendlyConfig === 'object' && window.FriendlyConfig[name] !== undefined) { return window.FriendlyConfig[name]; } + + // Backwards compatibility code because we renamed confirmOnFluff to confirmOnRollback, and confirmOnMobileFluff to confirmOnMobileRollback + if (name === 'confirmOnRollback' && Twinkle.prefs.confirmOnFluff !== undefined) { + return Twinkle.prefs.confirmOnFluff; + } else if (name === 'confirmOnMobileRollback' && Twinkle.prefs.confirmOnMobileFluff !== undefined) { + return Twinkle.prefs.confirmOnMobileFluff; + } + return Twinkle.defaultConfig[name]; };