User:Codename Noreste/common.js: Difference between revisions
Content added Content deleted
No edit summary |
No edit summary Tag: Reverted |
||
Line 1: | Line 1: | ||
mw.loader.load('//meta.wikimedia.org/w/index.php?title=User:WhitePhosphorus/js/all-in-one.js&action=raw&ctype=text/javascript'); |
|||
$(function () { |
|||
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows'], function() { |
|||
let config = {}; |
|||
let inited = false; |
|||
let loaded = false; |
|||
let windowManager = new OO.ui.WindowManager(); |
|||
let aio_dialog; |
|||
const work = function() { |
|||
let api = new mw.Api(); |
|||
if (config.block) { |
|||
let data = { |
|||
action: 'block', |
|||
user: config.username, |
|||
expiry: config.blockDur, |
|||
reason: config.blockReason |
|||
}; |
|||
if (config.blockAnon) data.anononly = 1; |
|||
if (config.blockAuto) data.autoblock = 1; |
|||
if (config.blockCreate) data.nocreate = 1; |
|||
if (!config.blockTalk) data.allowusertalk = 1; |
|||
if (config.blockMail) data.noemail = 1; |
|||
if (config.blockHide) data.hidename = 1; |
|||
api.postWithEditToken(data).done(() => mw.notify('User is blocked.')).fail(function(e) { |
|||
mw.notify('Failed to block the user: ' + e, { type: 'warn' }); |
|||
console.log(e); |
|||
}); |
|||
} |
|||
let untildate = new Date(); |
|||
if (config.endtime === 'inf') { |
|||
untildate = null; |
|||
} else { |
|||
untildate.setSeconds(untildate.getSeconds() - parseInt(config.endtime)); |
|||
} |
|||
data = { |
|||
action: 'query', |
|||
list: 'usercontribs', |
|||
ucuser: config.username, |
|||
uclimit: 'max' |
|||
// TODO: continue |
|||
}; |
|||
if (untildate) data.ucend = untildate.toISOString(); |
|||
api.get(data).done(function(data) { |
|||
let contribs = data.query; |
|||
if (!contribs) { |
|||
mw.notify('Failed to get the contribs!', { type: 'warn' }); |
|||
return; |
|||
} |
|||
contribs = contribs.usercontribs; |
|||
if (!contribs || !contribs.length) { |
|||
mw.notify('No contribs found.'); |
|||
return; |
|||
} |
|||
let ids = {}; |
|||
let creation = []; |
|||
for (let edit of contribs) { |
|||
if (edit.new === '') { |
|||
creation.push(edit.title); |
|||
} else { |
|||
if (ids[edit.title] === undefined) ids[edit.title] = []; |
|||
ids[edit.title].push(edit.revid); |
|||
} |
|||
} |
|||
for (let [title, idlist] of Object.entries(ids)) { |
|||
if (!config.rollback) { |
|||
// only check revdel |
|||
if (config.rd) { |
|||
api.postWithEditToken({ |
|||
action: 'revisiondelete', |
|||
type: 'revision', |
|||
ids: idlist, |
|||
hide: config.rdHides, |
|||
reason: config.rdReason, |
|||
suppress: config.os ? "yes" : "nochange" |
|||
}).done(() => mw.notify(`${idlist.length} revisions on page ${title} hidden.`)).fail(function(e) { |
|||
mw.notify(`Failed to hide revisions on ${title}: ${e}`, { type: 'warn' }); |
|||
console.log('revisiondelete', title, idlist, e); |
|||
}); |
|||
} |
|||
continue; |
|||
} |
|||
// we'd like to rollback first then revdel - if not, attempts to revdel the content may fail |
|||
data = config.rollbackBot ? {markbot: 1} : {}; |
|||
data.summary = config.rollbackShow ? '' : 'revert edits by <username hidden>'; |
|||
api.rollback(title, config.username, data).done(function() { |
|||
mw.notify(`Page ${title} Reverted.`); |
|||
}).fail(function(e) { |
|||
mw.notify(`Failed to revert on the page ${title}: ${e}`, { type: 'warn' }); |
|||
console.log('revert', title, e); |
|||
}).always(function() { |
|||
if (config.rd) { |
|||
api.postWithEditToken({ |
|||
action: 'revisiondelete', |
|||
type: 'revision', |
|||
ids: idlist, |
|||
hide: config.rdHides, |
|||
reason: config.rdReason, |
|||
suppress: config.os ? "yes" : "nochange" |
|||
}).done(() => mw.notify(`${idlist.length} revisions on page ${title} hidden.`)).fail(function(e) { |
|||
mw.notify(`Failed to hide revisions on ${title}: ${e}`, { type: 'warn' }); |
|||
console.log('revisiondelete', title, idlist, e); |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
if (config.massdel) { |
|||
for (let title of creation) { |
|||
api.postWithEditToken({ |
|||
action: 'delete', |
|||
title: title, |
|||
reason: config.massdelReason |
|||
}).done(() => mw.notify(`Deleted page ${title}.`)).fail(function(e) { |
|||
mw.notify(`Failed to delete ${title}: ${e}`, { type: 'warn' }); |
|||
console.log('delete', title, e); |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
// Build OOUI interface |
|||
const init = function() { |
|||
if (inited) return; |
|||
inited = true; |
|||
function AIODialog(config) { |
|||
AIODialog.super.call(this, config); |
|||
} |
|||
OO.inheritClass(AIODialog, OO.ui.ProcessDialog); |
|||
AIODialog.static.name = 'p4AIODialog'; |
|||
AIODialog.static.actions = [ |
|||
{ |
|||
flags: ['primary', 'destructive'], |
|||
label: 'Start [enter]', |
|||
action: 'start', |
|||
}, |
|||
{ |
|||
flags: ['safe', 'close'], |
|||
}, |
|||
]; |
|||
AIODialog.prototype.initialize = function() { |
|||
AIODialog.super.prototype.initialize.call(this); |
|||
this.panel = new OO.ui.PanelLayout({ |
|||
classes: ['p4-aio-form'], |
|||
padded: true, |
|||
expanded: false, |
|||
}); |
|||
// General info: username, package, etc. |
|||
this.top_fieldset = new OO.ui.FieldsetLayout({ |
|||
label: 'All in One', |
|||
}); |
|||
this.top_username_input = new OO.ui.TextInputWidget({ |
|||
placeholder: 'username or IP (not range)', |
|||
}); |
|||
this.top_endtime_dropdown = new OO.ui.DropdownWidget({ |
|||
menu: { |
|||
items: [ |
|||
new OO.ui.MenuOptionWidget({ |
|||
data: 3600, |
|||
label: 'in 1 hour', |
|||
}), |
|||
new OO.ui.MenuOptionWidget({ |
|||
data: 86400, |
|||
label: 'in 1 day', |
|||
}), |
|||
new OO.ui.MenuOptionWidget({ |
|||
data: 'inf', |
|||
label: 'everything', |
|||
}), |
|||
new OO.ui.MenuOptionWidget({ |
|||
data: 'other', |
|||
label: 'Other: (in seconds)', |
|||
}), |
|||
], |
|||
}, |
|||
}); |
|||
this.top_endtime_input = new OO.ui.TextInputWidget({ |
|||
placeholder: 'newer than ... seconds', |
|||
}); |
|||
this.top_package_dropdown = new OO.ui.DropdownWidget(); |
|||
this.top_suffix_dropdown = new OO.ui.DropdownWidget({ |
|||
menu: { |
|||
items: [ new OO.ui.MenuOptionWidget({ data: '', label: '<no suffix>' }) ], |
|||
}, |
|||
}); |
|||
this.top_fieldset.addItems([ |
|||
new OO.ui.FieldLayout( |
|||
this.top_username_input, { |
|||
label: 'Target', |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
new OO.ui.Widget({ |
|||
content: [ |
|||
this.top_endtime_dropdown, |
|||
this.top_endtime_input, |
|||
] |
|||
}), { |
|||
label: 'Edits', |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.top_package_dropdown, { |
|||
label: 'Package', |
|||
help: new OO.ui.HtmlSnippet( |
|||
'<a href="https://meta.wikimedia.org/wiki/User:WhitePhosphorus/all-in-one#Add_your_own_package">how to define your packages?</a>' |
|||
), |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.top_suffix_dropdown, { |
|||
label: 'Suffix', |
|||
help: new OO.ui.HtmlSnippet( |
|||
'<a href="https://meta.wikimedia.org/wiki/User:WhitePhosphorus/all-in-one#Add_your_reasons_in_drop_down_list">how to define your rationales and suffixes?</a>' |
|||
), |
|||
} |
|||
), |
|||
]); |
|||
// Rollback |
|||
this.rollback_fieldset = new OO.ui.FieldsetLayout({ |
|||
label: 'Rollback', |
|||
invisibleLabel: true, |
|||
classes: ['p4-aio-rollback-fieldset'], |
|||
}); |
|||
this.rollback_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.rollback_bot_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.rollback_showname_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.rollback_fieldset.addItems([ |
|||
new OO.ui.FieldLayout( |
|||
this.rollback_checkbox, { |
|||
label: 'Enable rollback', |
|||
align: 'inline', |
|||
classes: ['p4-aio-checkbox', 'p4-aio-checkbox-block'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.rollback_bot_checkbox, { |
|||
label: 'Mark rollback as bot edits', |
|||
align: 'inline', |
|||
classes: ['p4-aio-checkbox'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.rollback_showname_checkbox, { |
|||
label: 'Show username in rollback summary', |
|||
align: 'inline', |
|||
help: 'For some vandals with inappropriate usernames, you may want to uncheck this and the revert summary will look like "revert edits by <username hidden>".', |
|||
classes: ['p4-aio-checkbox'], |
|||
} |
|||
), |
|||
]); |
|||
// Block |
|||
this.block_fieldset = new OO.ui.FieldsetLayout({ |
|||
label: 'Block', |
|||
invisibleLabel: true, |
|||
classes: ['p4-aio-block-fieldset'], |
|||
}); |
|||
this.block_duration_dropdown = new OO.ui.DropdownWidget({ |
|||
menu: { |
|||
items: [ |
|||
new OO.ui.MenuOptionWidget({ data: '31 hours', label: '31 hours' }), |
|||
new OO.ui.MenuOptionWidget({ data: '1 day', label: '1 day' }), |
|||
new OO.ui.MenuOptionWidget({ data: '3 days', label: '3 days' }), |
|||
new OO.ui.MenuOptionWidget({ data: '5 days', label: '5 days' }), |
|||
new OO.ui.MenuOptionWidget({ data: '1 week', label: '1 week' }), |
|||
new OO.ui.MenuOptionWidget({ data: '2 weeks', label: '2 weeks' }), |
|||
new OO.ui.MenuOptionWidget({ data: '1 month', label: '1 month' }), |
|||
new OO.ui.MenuOptionWidget({ data: '3 months', label: '3 months' }), |
|||
new OO.ui.MenuOptionWidget({ data: '6 months', label: '6 months' }), |
|||
new OO.ui.MenuOptionWidget({ data: '1 year', label: '1 year' }), |
|||
new OO.ui.MenuOptionWidget({ data: '2 years', label: '2 years' }), |
|||
new OO.ui.MenuOptionWidget({ data: 'never', label: 'indefinite' }), |
|||
new OO.ui.MenuOptionWidget({ data: 'other', label: 'Other:' }), |
|||
], |
|||
}, |
|||
}); |
|||
this.block_duration_input = new OO.ui.TextInputWidget({ |
|||
placeholder: 'duration/timestamp', |
|||
}); |
|||
this.block_reason_dropdown = new OO.ui.DropdownWidget({ |
|||
menu: { |
|||
// Data for "Other:" must be empty, since it will be directly included in the actual block reason |
|||
// Same for the (revision) delete reasons (will be added to the input box if append button is clicked) |
|||
items: [ new OO.ui.MenuOptionWidget({ data: '', label: 'Other:' }) ], |
|||
}, |
|||
}); |
|||
this.block_reason_input = new OO.ui.TextInputWidget({ |
|||
placeholder: 'other rationale to append', |
|||
}); |
|||
this.block_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.block_hardblock_checkbox = new OO.ui.CheckboxInputWidget(); |
|||
this.block_autoblock_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.block_create_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.block_talk_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.block_mail_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.block_hidename_checkbox = new OO.ui.CheckboxInputWidget(); |
|||
this.block_fieldset.addItems([ |
|||
new OO.ui.FieldLayout( |
|||
this.block_checkbox, { |
|||
label: 'Enable block', |
|||
align: 'inline', |
|||
classes: ['p4-aio-checkbox', 'p4-aio-checkbox-block'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
new OO.ui.Widget({ |
|||
content: [ |
|||
this.block_duration_dropdown, |
|||
this.block_duration_input, |
|||
] |
|||
}), { |
|||
label: 'Expiration', |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
new OO.ui.Widget({ |
|||
content: [ |
|||
this.block_reason_dropdown, |
|||
this.block_reason_input, |
|||
] |
|||
}), { |
|||
label: 'Reason', |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.block_hardblock_checkbox, { |
|||
label: 'Hard block', |
|||
align: 'inline', |
|||
help: 'Apply block to logged-in users from this IP address', |
|||
classes: ['p4-aio-checkbox', 'p4-aio-anon-only'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.block_autoblock_checkbox, { |
|||
label: 'Auto block', |
|||
align: 'inline', |
|||
help: 'Automatically block the last IP address used by this user, and any subsequent IP addresses they try to edit from, for a period of 1 day', |
|||
classes: ['p4-aio-checkbox', 'p4-aio-reg-only'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.block_create_checkbox, { |
|||
label: 'Block account creation', |
|||
align: 'inline', |
|||
classes: ['p4-aio-checkbox'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.block_talk_checkbox, { |
|||
label: 'Block editing own talk page', |
|||
align: 'inline', |
|||
classes: ['p4-aio-checkbox'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.block_mail_checkbox, { |
|||
label: 'Block email', |
|||
align: 'inline', |
|||
classes: ['p4-aio-checkbox'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.block_hidename_checkbox, { |
|||
label: 'Hide username from public logs', |
|||
align: 'inline', |
|||
help: 'needs "hideuser" right, or your action will fail', |
|||
classes: ['p4-aio-checkbox'], |
|||
} |
|||
), |
|||
]); |
|||
// Page deletion |
|||
this.pagedelete_fieldset = new OO.ui.FieldsetLayout({ |
|||
label: 'Page deletion', |
|||
invisibleLabel: true, |
|||
classes: ['p4-aio-pagedelete-fieldset'], |
|||
}); |
|||
this.pagedelete_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.pagedelete_reason_dropdown = new OO.ui.DropdownWidget({ |
|||
menu: { |
|||
items: [ new OO.ui.MenuOptionWidget({ data: '', label: 'Other:' }) ], |
|||
}, |
|||
}); |
|||
this.pagedelete_reason_button = new OO.ui.ButtonWidget({ label: 'Append' }); |
|||
this.pagedelete_reason_input = new OO.ui.TextInputWidget({ |
|||
placeholder: 'full rationale to submit', |
|||
}); |
|||
this.pagedelete_fieldset.addItems([ |
|||
new OO.ui.FieldLayout( |
|||
this.pagedelete_checkbox, { |
|||
label: 'Enable deletion', |
|||
align: 'inline', |
|||
classes: ['p4-aio-checkbox', 'p4-aio-checkbox-block'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
new OO.ui.Widget({ |
|||
content: [ |
|||
this.pagedelete_reason_dropdown, |
|||
this.pagedelete_reason_button, |
|||
this.pagedelete_reason_input, |
|||
] |
|||
}), { |
|||
label: 'Reason', |
|||
classes: ['p4-aio-reason'], |
|||
} |
|||
), |
|||
]); |
|||
// Revision deletion |
|||
this.revisiondelete_fieldset = new OO.ui.FieldsetLayout({ |
|||
label: 'Revision deletion', |
|||
invisibleLabel: true, |
|||
classes: ['p4-aio-revisiondelete-fieldset'], |
|||
}); |
|||
this.revisiondelete_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.revisiondelete_content_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.revisiondelete_summary_checkbox = new OO.ui.CheckboxInputWidget({ selected: true }); |
|||
this.revisiondelete_username_checkbox = new OO.ui.CheckboxInputWidget(); |
|||
this.revisiondelete_oversight_checkbox = new OO.ui.CheckboxInputWidget(); |
|||
this.revisiondelete_reason_dropdown = new OO.ui.DropdownWidget({ |
|||
menu: { |
|||
items: [ new OO.ui.MenuOptionWidget({ data: '', label: 'Other:' }) ], |
|||
}, |
|||
}); |
|||
this.revisiondelete_reason_button = new OO.ui.ButtonWidget({ label: 'Append' }); |
|||
this.revisiondelete_reason_input = new OO.ui.TextInputWidget({ |
|||
placeholder: 'full rationale to submit', |
|||
}); |
|||
this.revisiondelete_fieldset.addItems([ |
|||
new OO.ui.FieldLayout( |
|||
this.revisiondelete_checkbox, { |
|||
label: 'Enable revision deletion', |
|||
align: 'inline', |
|||
classes: ['p4-aio-checkbox', 'p4-aio-checkbox-block'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.revisiondelete_content_checkbox, { |
|||
label: 'Delete content', |
|||
align: 'inline', |
|||
classes: ['p4-aio-checkbox'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.revisiondelete_summary_checkbox, { |
|||
label: 'Delete edit summaries', |
|||
align: 'inline', |
|||
classes: ['p4-aio-checkbox'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.revisiondelete_username_checkbox, { |
|||
label: 'Delete username or IP address', |
|||
align: 'inline', |
|||
classes: ['p4-aio-checkbox'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
this.revisiondelete_oversight_checkbox, { |
|||
label: 'Oversight', |
|||
align: 'inline', |
|||
help: 'Suppress data from administrators as well as others. Do not check this option unless you have the "suppressrevision" right.', |
|||
classes: ['p4-aio-checkbox'], |
|||
} |
|||
), |
|||
new OO.ui.FieldLayout( |
|||
new OO.ui.Widget({ |
|||
content: [ |
|||
this.revisiondelete_reason_dropdown, |
|||
this.revisiondelete_reason_button, |
|||
this.revisiondelete_reason_input, |
|||
] |
|||
}), { |
|||
label: 'Reason', |
|||
classes: ['p4-aio-reason'], |
|||
} |
|||
), |
|||
]); |
|||
// Update here if new fieldsets are introduced |
|||
for (let content of [ |
|||
this.top_fieldset, this.rollback_fieldset, this.block_fieldset, |
|||
this.pagedelete_fieldset, this.revisiondelete_fieldset, |
|||
]) { |
|||
this.panel.$element.append(content.$element); |
|||
} |
|||
this.$body.append(this.panel.$element); |
|||
// CSS styles |
|||
// Make checkboxes stay in the same line if possible ... |
|||
$('.p4-aio-checkbox').css({ |
|||
display: 'inline-block', |
|||
'margin-right': '12px', |
|||
// workaround: the height of checkboxes with a tooltip are different from those without one |
|||
// thus set height of every checkbox to 21px to align them |
|||
height: '21px', |
|||
}); |
|||
// ... except those marked as block |
|||
$('.p4-aio-checkbox.p4-aio-checkbox-block').css({ |
|||
display: 'block', |
|||
'font-weight': 'bold', |
|||
}); |
|||
// The header consumes too much space by default (40%) |
|||
$('.p4-aio-form .oo-ui-fieldLayout-align-left .oo-ui-fieldLayout-header').css({ |
|||
width: '20%', |
|||
}); |
|||
$('.p4-aio-form .oo-ui-fieldLayout-align-left .oo-ui-fieldLayout-field').css({ |
|||
width: '80%', |
|||
}); |
|||
// Border for rollback, block, etc. |
|||
$('.p4-aio-form .oo-ui-fieldsetLayout:nth-child(n+2)').css({ |
|||
border: '1px solid #a2a9b1', |
|||
'border-radius': '2px', |
|||
padding: '12px', |
|||
}); |
|||
// Make the button nowrap in reason fields and the text input be in the new line |
|||
$('.p4-aio-reason .oo-ui-fieldLayout-field > .oo-ui-widget').css({ |
|||
display: 'flex', |
|||
'flex-wrap': 'wrap', |
|||
}); |
|||
$('.p4-aio-reason .oo-ui-fieldLayout-field > .oo-ui-widget > .oo-ui-dropdownWidget').css({ |
|||
'flex-basis': 0, |
|||
'flex-grow': 1, |
|||
}); |
|||
$('.p4-aio-reason .oo-ui-fieldLayout-field > .oo-ui-widget > .oo-ui-buttonWidget').css({ |
|||
'margin-right': 0, |
|||
}); |
|||
$('.p4-aio-reason .oo-ui-fieldLayout-field > .oo-ui-widget > .oo-ui-textInputWidget').css({ |
|||
'flex-basis': '100%', |
|||
}); |
|||
} |
|||
AIODialog.prototype.getActionProcess = function(action) { |
|||
if (action === 'start') { |
|||
return new OO.ui.Process(function() { |
|||
// Load config |
|||
config.username = this.top_username_input.getValue(); |
|||
if (!config.username) return; |
|||
config.suffix = this.top_suffix_dropdown.getMenu().findSelectedItem().getData(); |
|||
config.isIP = mw.util.isIPAddress(config.username); |
|||
config.endtime = getValueFromDropdownAndInput('top_endtime'); |
|||
config.rollback = this.rollback_checkbox.isSelected(); |
|||
config.rollbackBot = this.rollback_bot_checkbox.isSelected(); |
|||
config.rollbackShow = this.rollback_showname_checkbox.isSelected(); |
|||
config.block = this.block_checkbox.isSelected(); |
|||
config.blockDur = getValueFromDropdownAndInput('block_duration'); |
|||
config.blockReason = getValueFromDropdownAndInput('block_reason', other_data = '', check_include = true); |
|||
config.blockReason += config.suffix; |
|||
config.blockAnon = config.isIP && !this.block_hardblock_checkbox.isSelected(); |
|||
config.blockAuto = !config.isIP && this.block_autoblock_checkbox.isSelected(); |
|||
config.blockCreate = this.block_create_checkbox.isSelected(); |
|||
config.blockTalk = this.block_talk_checkbox.isSelected(); |
|||
config.blockMail = this.block_mail_checkbox.isSelected(); |
|||
config.blockHide = this.block_hidename_checkbox.isSelected(); |
|||
config.massdel = this.pagedelete_checkbox.isSelected(); |
|||
config.massdelReason = getValueFromDropdownAndInput('pagedelete_reason', other_data = '', check_include = true); |
|||
config.massdelReason += config.suffix; |
|||
config.rd = this.revisiondelete_checkbox.isSelected(); |
|||
config.rdHides = ''; |
|||
if (this.revisiondelete_content_checkbox.isSelected()) config.rdHides += 'content|'; |
|||
if (this.revisiondelete_summary_checkbox.isSelected()) config.rdHides += 'comment|'; |
|||
if (this.revisiondelete_username_checkbox.isSelected()) config.rdHides += 'user|'; |
|||
config.rdReason = getValueFromDropdownAndInput('revisiondelete_reason', other_data = '', check_include = true); |
|||
config.rdReason += config.suffix; |
|||
config.os = this.revisiondelete_oversight_checkbox.isSelected(); |
|||
// Begin execution |
|||
work(); |
|||
this.close(); |
|||
}, this); |
|||
} |
|||
return AIODialog.super.prototype.getActionProcess.call(this, action); |
|||
}; |
|||
// Set dialog width to be the same as its panel |
|||
AIODialog.prototype.getBodyWidth = function() { |
|||
return this.panel.$element.outerWidth(true); |
|||
}; |
|||
$(document.body).append(windowManager.$element); |
|||
aio_dialog = new AIODialog({ |
|||
size: 'large', |
|||
}); |
|||
windowManager.addWindows([aio_dialog]); |
|||
} |
|||
// If `data` exists in dropdown, select it; |
|||
// else fill the input box and select "other" (provided by `other_data`) |
|||
const selectDropdownOrFillInput = function(field, data, other_data = 'other') { |
|||
let dropdown = aio_dialog[field + '_dropdown']; |
|||
let input = aio_dialog[field + '_input']; |
|||
if (!dropdown || !input) return; |
|||
let menu = dropdown.getMenu(); |
|||
if (!menu) return; |
|||
menu.selectItemByData(data); |
|||
if (menu.findSelectedItem() === null) { |
|||
menu.selectItemByData(other_data); |
|||
input.setValue(data); |
|||
} |
|||
} |
|||
// If `data` selected is other, use the value from input box |
|||
const getValueFromDropdownAndInput = function(field, other_data = 'other', check_include = false) { |
|||
let dropdown = aio_dialog[field + '_dropdown']; |
|||
let input = aio_dialog[field + '_input']; |
|||
if (!dropdown || !input) return; |
|||
let menu = dropdown.getMenu(); |
|||
if (!menu) return; |
|||
let selected = menu.findSelectedItem().getData(); |
|||
let text = input.getValue(); |
|||
if (check_include) { |
|||
if (text.includes(selected)) { |
|||
// Do not include reason in dropdown again |
|||
return text; |
|||
} else { |
|||
// Construct a reason like "dropdown: input" |
|||
return selected + (text ? ': ' + text : ''); |
|||
} |
|||
} |
|||
return selected && selected === other_data ? text : selected; |
|||
} |
|||
$(mw.util.addPortletLink('p-cactions', '#', 'All-in-one')).click(function (e) { |
|||
init(); |
|||
// Avoid re-loading config when user closed and opens the dialog again |
|||
if (loaded) { |
|||
windowManager.openWindow(aio_dialog, {}); |
|||
return; |
|||
} |
|||
loaded = true; |
|||
aio_dialog.package_loaded = false; |
|||
if (typeof(p4js_all_in_one) !== 'object') p4js_all_in_one = {}; |
|||
// load drop down reasons |
|||
let suffixes = p4js_all_in_one.suffixes || [" (all-in-one)", " "]; |
|||
let reasons = p4js_all_in_one.reasons || { |
|||
block: ["Vandalism", "Spamming links to external sites", "Intimidating behavior or harassment", "Unacceptable username", "Open proxy", "Adding nonsense or gibberish content into pages"], |
|||
pagedelete: ["Blatant advertising or spam, as [[m:Category:Global policies|defined globally (by policy or convention)]]", "Vandalism"], |
|||
revisiondelete: ["Grossly insulting, degrading, or offensive material", "Purely disruptive material"], |
|||
}; |
|||
for (let suffix of suffixes) { |
|||
if (!suffix) continue; |
|||
aio_dialog.top_suffix_dropdown.getMenu().addItems(new OO.ui.MenuOptionWidget({ |
|||
data: suffix, |
|||
label: suffix, |
|||
})); |
|||
} |
|||
aio_dialog.top_suffix_dropdown.getMenu().addItems( |
|||
suffixes |
|||
.filter(e => e) // empty suffix is already included in dropdown |
|||
.map(e => new OO.ui.MenuOptionWidget({ |
|||
data: e, |
|||
label: e, |
|||
})) |
|||
); |
|||
// Select the empty suffix by default |
|||
aio_dialog.top_suffix_dropdown.getMenu().selectItemByData(''); |
|||
for (let action of ['block', 'pagedelete', 'revisiondelete']) { |
|||
aio_dialog[action + '_reason_dropdown'].getMenu().addItems( |
|||
(reasons[action] || []).map(e => new OO.ui.MenuOptionWidget({ |
|||
data: e, |
|||
label: e, |
|||
})), |
|||
0 // Prepend to option list |
|||
); |
|||
} |
|||
// load packages (or the default one) |
|||
const default_package = { |
|||
tracingedits: { |
|||
duration: 3600, |
|||
indefregistered: true, |
|||
}, |
|||
rollback: { |
|||
enabled: true, |
|||
bot: false, |
|||
showname: true, |
|||
}, |
|||
block: { |
|||
enabled: true, |
|||
duration: "1 day", |
|||
indefregistered: true, |
|||
reason: "Vandalism", |
|||
autoblock: true, |
|||
hardblock: false, |
|||
create: true, |
|||
talk: false, |
|||
mail: false, |
|||
hidename: false, |
|||
}, |
|||
pagedelete: { |
|||
enabled: true, |
|||
reason: "Vandalism", |
|||
}, |
|||
revisiondelete: { |
|||
enabled: false, |
|||
content: true, |
|||
summary: true, |
|||
username: false, |
|||
reason: "Purely disruptive material", |
|||
oversight: false, |
|||
}, |
|||
}; |
|||
let packages = p4js_all_in_one.packages || {}; |
|||
if (!packages.Default) { |
|||
packages.Default = default_package; |
|||
} |
|||
aio_dialog.top_package_dropdown.getMenu().addItems( |
|||
Object.keys(packages).map(e => new OO.ui.MenuOptionWidget({ |
|||
data: e, |
|||
label: e, |
|||
})), |
|||
0 |
|||
); |
|||
// When dropdown changes (manually or automatically), decide whether to hide corresponding text input or not |
|||
for (let field of ['top_endtime', 'block_duration']) { |
|||
['choose', 'select'].forEach(e => aio_dialog[field + '_dropdown'].getMenu().on(e, function(item) { |
|||
aio_dialog[field + '_input'].toggle(item.getData() === 'other'); |
|||
})); |
|||
} |
|||
// When package changes (manually or automatically), change various config |
|||
['choose', 'select'].forEach(e => aio_dialog.top_package_dropdown.getMenu().on(e, function(item) { |
|||
// Avoid config being changed when user closed and opens the dialog again |
|||
if (e == 'select' && aio_dialog.package_loaded) return; |
|||
aio_dialog.package_loaded = true; |
|||
let isIP = mw.util.isIPAddress(aio_dialog.top_username_input.getValue()); |
|||
let pkg = packages[item.getData()] || {}; |
|||
// checkboxes |
|||
for (let [action, action_config] of Object.entries(pkg)) { |
|||
for (let [item, item_enabled] of Object.entries(action_config)) { |
|||
if (typeof item_enabled === "boolean" && item !== "enabled" && item !== "indefregistered") { |
|||
aio_dialog[`${action}_${item}_checkbox`].setSelected(item_enabled); |
|||
} |
|||
} |
|||
} |
|||
// tracing edits |
|||
pkg.tracingedits = pkg.tracingedits || {}; |
|||
selectDropdownOrFillInput('top_endtime', pkg.tracingedits.duration || 3600); |
|||
if (pkg.tracingedits.indefregistered && !isIP) { |
|||
aio_dialog.top_endtime_dropdown.getMenu().selectItemByData('inf'); |
|||
} |
|||
// rollback: default is enabled |
|||
aio_dialog.rollback_checkbox.setSelected(pkg.rollback.enabled || true); |
|||
// block |
|||
pkg.block = pkg.block || {}; |
|||
aio_dialog.block_checkbox.setSelected(pkg.block.enabled || false); |
|||
selectDropdownOrFillInput('block_duration', pkg.block.duration || '1 day'); |
|||
if (pkg.tracingedits.indefregistered && !isIP) { |
|||
aio_dialog.block_duration_dropdown.getMenu().selectItemByData('never'); |
|||
} |
|||
selectDropdownOrFillInput('block_reason', pkg.block.reason || 'Long-term abuse', ''); |
|||
// page deletion |
|||
pkg.pagedelete = pkg.pagedelete || {}; |
|||
aio_dialog.pagedelete_checkbox.setSelected(pkg.pagedelete.enabled || false); |
|||
selectDropdownOrFillInput('pagedelete_reason', pkg.pagedelete.reason || 'Vandalism', ''); |
|||
// revision deletion |
|||
pkg.revisiondelete = pkg.revisiondelete || {}; |
|||
aio_dialog.revisiondelete_checkbox.setSelected(pkg.revisiondelete.enabled || false); |
|||
selectDropdownOrFillInput('revisiondelete_reason', pkg.revisiondelete.reason || 'Hiding blatant offending materials', ''); |
|||
})); |
|||
// select the default package |
|||
if (p4js_all_in_one.default_package && packages[p4js_all_in_one.default_package]) { |
|||
aio_dialog.top_package_dropdown.getMenu().selectItemByData(p4js_all_in_one.default_package); |
|||
} else { |
|||
aio_dialog.top_package_dropdown.getMenu().selectItemByData('Default'); |
|||
} |
|||
// Append reasons |
|||
for (let action of ['pagedelete', 'revisiondelete']) { |
|||
aio_dialog[action + '_reason_button'].on('click', function() { |
|||
let text = aio_dialog[action + '_reason_input'].getValue(); |
|||
aio_dialog[action + '_reason_input'].setValue(text + '; ' + aio_dialog[action + '_reason_dropdown'].getMenu().findSelectedItem().getData()); |
|||
aio_dialog[action + '_reason_dropdown'].getMenu().selectItemByData(''); |
|||
}) |
|||
} |
|||
aio_dialog.top_username_input.on('change', function(username) { |
|||
let isIP = mw.util.isIPAddress(username); |
|||
let pkg = packages[aio_dialog.top_package_dropdown.getMenu().getData()] || default_package; |
|||
// Check indef |
|||
if (pkg.tracingedits && pkg.tracingedits.indefregistered && !isIP) { |
|||
aio_dialog.top_endtime_dropdown.getMenu().selectItemByData('inf'); |
|||
aio_dialog.block_duration_dropdown.getMenu().selectItemByData('never'); |
|||
} else { |
|||
pkg.tracingedits = pkg.tracingedits || {}; |
|||
pkg.block = pkg.block || {}; |
|||
selectDropdownOrFillInput('top_endtime', pkg.tracingedits.duration || 3600); |
|||
selectDropdownOrFillInput('block_duration', pkg.block.duration || '1 day'); |
|||
} |
|||
// Anon block / registered user block settings |
|||
if (isIP) { |
|||
$('.p4-aio-reg-only').hide(); |
|||
$('.p4-aio-anon-only').show(); |
|||
} else { |
|||
$('.p4-aio-reg-only').show(); |
|||
$('.p4-aio-anon-only').hide(); |
|||
} |
|||
}); |
|||
// Make fieldset transparent if not enabled |
|||
for (let action of ['rollback', 'block', 'pagedelete', 'revisiondelete']) { |
|||
aio_dialog[action + '_checkbox'].on('change', function(selected, _) { |
|||
$(`.p4-aio-${action}-fieldset`).css({ |
|||
opacity: selected ? '100%': '50%', |
|||
}); |
|||
}); |
|||
// Initialize |
|||
$(`.p4-aio-${action}-fieldset`).css({ |
|||
opacity: aio_dialog[action + '_checkbox'].isSelected() ? '100%': '50%', |
|||
transition: 'opacity 0.3s', |
|||
}); |
|||
} |
|||
// enter to submit |
|||
$(document).on("keyup", event => { |
|||
if(event.key !== "Enter" || !aio_dialog.isOpened()) return; |
|||
aio_dialog.executeAction('start'); |
|||
event.preventDefault(); |
|||
}); |
|||
// fill the current username if applicable |
|||
aio_dialog.top_username_input.setValue(mw.config.get('wgRelevantUserName') || ''); |
|||
aio_dialog.top_username_input.focus(); |
|||
windowManager.openWindow(aio_dialog, {}); |
|||
}); |
|||
}); |
|||
}); |
Revision as of 05:56, 30 August 2023
$(function () {
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows'], function() {
let config = {};
let inited = false;
let loaded = false;
let windowManager = new OO.ui.WindowManager();
let aio_dialog;
const work = function() {
let api = new mw.Api();
if (config.block) {
let data = {
action: 'block',
user: config.username,
expiry: config.blockDur,
reason: config.blockReason
};
if (config.blockAnon) data.anononly = 1;
if (config.blockAuto) data.autoblock = 1;
if (config.blockCreate) data.nocreate = 1;
if (!config.blockTalk) data.allowusertalk = 1;
if (config.blockMail) data.noemail = 1;
if (config.blockHide) data.hidename = 1;
api.postWithEditToken(data).done(() => mw.notify('User is blocked.')).fail(function(e) {
mw.notify('Failed to block the user: ' + e, { type: 'warn' });
console.log(e);
});
}
let untildate = new Date();
if (config.endtime === 'inf') {
untildate = null;
} else {
untildate.setSeconds(untildate.getSeconds() - parseInt(config.endtime));
}
data = {
action: 'query',
list: 'usercontribs',
ucuser: config.username,
uclimit: 'max'
// TODO: continue
};
if (untildate) data.ucend = untildate.toISOString();
api.get(data).done(function(data) {
let contribs = data.query;
if (!contribs) {
mw.notify('Failed to get the contribs!', { type: 'warn' });
return;
}
contribs = contribs.usercontribs;
if (!contribs || !contribs.length) {
mw.notify('No contribs found.');
return;
}
let ids = {};
let creation = [];
for (let edit of contribs) {
if (edit.new === '') {
creation.push(edit.title);
} else {
if (ids[edit.title] === undefined) ids[edit.title] = [];
ids[edit.title].push(edit.revid);
}
}
for (let [title, idlist] of Object.entries(ids)) {
if (!config.rollback) {
// only check revdel
if (config.rd) {
api.postWithEditToken({
action: 'revisiondelete',
type: 'revision',
ids: idlist,
hide: config.rdHides,
reason: config.rdReason,
suppress: config.os ? "yes" : "nochange"
}).done(() => mw.notify(`${idlist.length} revisions on page ${title} hidden.`)).fail(function(e) {
mw.notify(`Failed to hide revisions on ${title}: ${e}`, { type: 'warn' });
console.log('revisiondelete', title, idlist, e);
});
}
continue;
}
// we'd like to rollback first then revdel - if not, attempts to revdel the content may fail
data = config.rollbackBot ? {markbot: 1} : {};
data.summary = config.rollbackShow ? '' : 'revert edits by <username hidden>';
api.rollback(title, config.username, data).done(function() {
mw.notify(`Page ${title} Reverted.`);
}).fail(function(e) {
mw.notify(`Failed to revert on the page ${title}: ${e}`, { type: 'warn' });
console.log('revert', title, e);
}).always(function() {
if (config.rd) {
api.postWithEditToken({
action: 'revisiondelete',
type: 'revision',
ids: idlist,
hide: config.rdHides,
reason: config.rdReason,
suppress: config.os ? "yes" : "nochange"
}).done(() => mw.notify(`${idlist.length} revisions on page ${title} hidden.`)).fail(function(e) {
mw.notify(`Failed to hide revisions on ${title}: ${e}`, { type: 'warn' });
console.log('revisiondelete', title, idlist, e);
});
}
});
}
if (config.massdel) {
for (let title of creation) {
api.postWithEditToken({
action: 'delete',
title: title,
reason: config.massdelReason
}).done(() => mw.notify(`Deleted page ${title}.`)).fail(function(e) {
mw.notify(`Failed to delete ${title}: ${e}`, { type: 'warn' });
console.log('delete', title, e);
});
}
}
});
};
// Build OOUI interface
const init = function() {
if (inited) return;
inited = true;
function AIODialog(config) {
AIODialog.super.call(this, config);
}
OO.inheritClass(AIODialog, OO.ui.ProcessDialog);
AIODialog.static.name = 'p4AIODialog';
AIODialog.static.actions = [
{
flags: ['primary', 'destructive'],
label: 'Start [enter]',
action: 'start',
},
{
flags: ['safe', 'close'],
},
];
AIODialog.prototype.initialize = function() {
AIODialog.super.prototype.initialize.call(this);
this.panel = new OO.ui.PanelLayout({
classes: ['p4-aio-form'],
padded: true,
expanded: false,
});
// General info: username, package, etc.
this.top_fieldset = new OO.ui.FieldsetLayout({
label: 'All in One',
});
this.top_username_input = new OO.ui.TextInputWidget({
placeholder: 'username or IP (not range)',
});
this.top_endtime_dropdown = new OO.ui.DropdownWidget({
menu: {
items: [
new OO.ui.MenuOptionWidget({
data: 3600,
label: 'in 1 hour',
}),
new OO.ui.MenuOptionWidget({
data: 86400,
label: 'in 1 day',
}),
new OO.ui.MenuOptionWidget({
data: 'inf',
label: 'everything',
}),
new OO.ui.MenuOptionWidget({
data: 'other',
label: 'Other: (in seconds)',
}),
],
},
});
this.top_endtime_input = new OO.ui.TextInputWidget({
placeholder: 'newer than ... seconds',
});
this.top_package_dropdown = new OO.ui.DropdownWidget();
this.top_suffix_dropdown = new OO.ui.DropdownWidget({
menu: {
items: [ new OO.ui.MenuOptionWidget({ data: '', label: '<no suffix>' }) ],
},
});
this.top_fieldset.addItems([
new OO.ui.FieldLayout(
this.top_username_input, {
label: 'Target',
}
),
new OO.ui.FieldLayout(
new OO.ui.Widget({
content: [
this.top_endtime_dropdown,
this.top_endtime_input,
]
}), {
label: 'Edits',
}
),
new OO.ui.FieldLayout(
this.top_package_dropdown, {
label: 'Package',
help: new OO.ui.HtmlSnippet(
'<a href="https://meta.wikimedia.org/wiki/User:WhitePhosphorus/all-in-one#Add_your_own_package">how to define your packages?</a>'
),
}
),
new OO.ui.FieldLayout(
this.top_suffix_dropdown, {
label: 'Suffix',
help: new OO.ui.HtmlSnippet(
'<a href="https://meta.wikimedia.org/wiki/User:WhitePhosphorus/all-in-one#Add_your_reasons_in_drop_down_list">how to define your rationales and suffixes?</a>'
),
}
),
]);
// Rollback
this.rollback_fieldset = new OO.ui.FieldsetLayout({
label: 'Rollback',
invisibleLabel: true,
classes: ['p4-aio-rollback-fieldset'],
});
this.rollback_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.rollback_bot_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.rollback_showname_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.rollback_fieldset.addItems([
new OO.ui.FieldLayout(
this.rollback_checkbox, {
label: 'Enable rollback',
align: 'inline',
classes: ['p4-aio-checkbox', 'p4-aio-checkbox-block'],
}
),
new OO.ui.FieldLayout(
this.rollback_bot_checkbox, {
label: 'Mark rollback as bot edits',
align: 'inline',
classes: ['p4-aio-checkbox'],
}
),
new OO.ui.FieldLayout(
this.rollback_showname_checkbox, {
label: 'Show username in rollback summary',
align: 'inline',
help: 'For some vandals with inappropriate usernames, you may want to uncheck this and the revert summary will look like "revert edits by <username hidden>".',
classes: ['p4-aio-checkbox'],
}
),
]);
// Block
this.block_fieldset = new OO.ui.FieldsetLayout({
label: 'Block',
invisibleLabel: true,
classes: ['p4-aio-block-fieldset'],
});
this.block_duration_dropdown = new OO.ui.DropdownWidget({
menu: {
items: [
new OO.ui.MenuOptionWidget({ data: '31 hours', label: '31 hours' }),
new OO.ui.MenuOptionWidget({ data: '1 day', label: '1 day' }),
new OO.ui.MenuOptionWidget({ data: '3 days', label: '3 days' }),
new OO.ui.MenuOptionWidget({ data: '5 days', label: '5 days' }),
new OO.ui.MenuOptionWidget({ data: '1 week', label: '1 week' }),
new OO.ui.MenuOptionWidget({ data: '2 weeks', label: '2 weeks' }),
new OO.ui.MenuOptionWidget({ data: '1 month', label: '1 month' }),
new OO.ui.MenuOptionWidget({ data: '3 months', label: '3 months' }),
new OO.ui.MenuOptionWidget({ data: '6 months', label: '6 months' }),
new OO.ui.MenuOptionWidget({ data: '1 year', label: '1 year' }),
new OO.ui.MenuOptionWidget({ data: '2 years', label: '2 years' }),
new OO.ui.MenuOptionWidget({ data: 'never', label: 'indefinite' }),
new OO.ui.MenuOptionWidget({ data: 'other', label: 'Other:' }),
],
},
});
this.block_duration_input = new OO.ui.TextInputWidget({
placeholder: 'duration/timestamp',
});
this.block_reason_dropdown = new OO.ui.DropdownWidget({
menu: {
// Data for "Other:" must be empty, since it will be directly included in the actual block reason
// Same for the (revision) delete reasons (will be added to the input box if append button is clicked)
items: [ new OO.ui.MenuOptionWidget({ data: '', label: 'Other:' }) ],
},
});
this.block_reason_input = new OO.ui.TextInputWidget({
placeholder: 'other rationale to append',
});
this.block_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.block_hardblock_checkbox = new OO.ui.CheckboxInputWidget();
this.block_autoblock_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.block_create_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.block_talk_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.block_mail_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.block_hidename_checkbox = new OO.ui.CheckboxInputWidget();
this.block_fieldset.addItems([
new OO.ui.FieldLayout(
this.block_checkbox, {
label: 'Enable block',
align: 'inline',
classes: ['p4-aio-checkbox', 'p4-aio-checkbox-block'],
}
),
new OO.ui.FieldLayout(
new OO.ui.Widget({
content: [
this.block_duration_dropdown,
this.block_duration_input,
]
}), {
label: 'Expiration',
}
),
new OO.ui.FieldLayout(
new OO.ui.Widget({
content: [
this.block_reason_dropdown,
this.block_reason_input,
]
}), {
label: 'Reason',
}
),
new OO.ui.FieldLayout(
this.block_hardblock_checkbox, {
label: 'Hard block',
align: 'inline',
help: 'Apply block to logged-in users from this IP address',
classes: ['p4-aio-checkbox', 'p4-aio-anon-only'],
}
),
new OO.ui.FieldLayout(
this.block_autoblock_checkbox, {
label: 'Auto block',
align: 'inline',
help: 'Automatically block the last IP address used by this user, and any subsequent IP addresses they try to edit from, for a period of 1 day',
classes: ['p4-aio-checkbox', 'p4-aio-reg-only'],
}
),
new OO.ui.FieldLayout(
this.block_create_checkbox, {
label: 'Block account creation',
align: 'inline',
classes: ['p4-aio-checkbox'],
}
),
new OO.ui.FieldLayout(
this.block_talk_checkbox, {
label: 'Block editing own talk page',
align: 'inline',
classes: ['p4-aio-checkbox'],
}
),
new OO.ui.FieldLayout(
this.block_mail_checkbox, {
label: 'Block email',
align: 'inline',
classes: ['p4-aio-checkbox'],
}
),
new OO.ui.FieldLayout(
this.block_hidename_checkbox, {
label: 'Hide username from public logs',
align: 'inline',
help: 'needs "hideuser" right, or your action will fail',
classes: ['p4-aio-checkbox'],
}
),
]);
// Page deletion
this.pagedelete_fieldset = new OO.ui.FieldsetLayout({
label: 'Page deletion',
invisibleLabel: true,
classes: ['p4-aio-pagedelete-fieldset'],
});
this.pagedelete_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.pagedelete_reason_dropdown = new OO.ui.DropdownWidget({
menu: {
items: [ new OO.ui.MenuOptionWidget({ data: '', label: 'Other:' }) ],
},
});
this.pagedelete_reason_button = new OO.ui.ButtonWidget({ label: 'Append' });
this.pagedelete_reason_input = new OO.ui.TextInputWidget({
placeholder: 'full rationale to submit',
});
this.pagedelete_fieldset.addItems([
new OO.ui.FieldLayout(
this.pagedelete_checkbox, {
label: 'Enable deletion',
align: 'inline',
classes: ['p4-aio-checkbox', 'p4-aio-checkbox-block'],
}
),
new OO.ui.FieldLayout(
new OO.ui.Widget({
content: [
this.pagedelete_reason_dropdown,
this.pagedelete_reason_button,
this.pagedelete_reason_input,
]
}), {
label: 'Reason',
classes: ['p4-aio-reason'],
}
),
]);
// Revision deletion
this.revisiondelete_fieldset = new OO.ui.FieldsetLayout({
label: 'Revision deletion',
invisibleLabel: true,
classes: ['p4-aio-revisiondelete-fieldset'],
});
this.revisiondelete_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.revisiondelete_content_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.revisiondelete_summary_checkbox = new OO.ui.CheckboxInputWidget({ selected: true });
this.revisiondelete_username_checkbox = new OO.ui.CheckboxInputWidget();
this.revisiondelete_oversight_checkbox = new OO.ui.CheckboxInputWidget();
this.revisiondelete_reason_dropdown = new OO.ui.DropdownWidget({
menu: {
items: [ new OO.ui.MenuOptionWidget({ data: '', label: 'Other:' }) ],
},
});
this.revisiondelete_reason_button = new OO.ui.ButtonWidget({ label: 'Append' });
this.revisiondelete_reason_input = new OO.ui.TextInputWidget({
placeholder: 'full rationale to submit',
});
this.revisiondelete_fieldset.addItems([
new OO.ui.FieldLayout(
this.revisiondelete_checkbox, {
label: 'Enable revision deletion',
align: 'inline',
classes: ['p4-aio-checkbox', 'p4-aio-checkbox-block'],
}
),
new OO.ui.FieldLayout(
this.revisiondelete_content_checkbox, {
label: 'Delete content',
align: 'inline',
classes: ['p4-aio-checkbox'],
}
),
new OO.ui.FieldLayout(
this.revisiondelete_summary_checkbox, {
label: 'Delete edit summaries',
align: 'inline',
classes: ['p4-aio-checkbox'],
}
),
new OO.ui.FieldLayout(
this.revisiondelete_username_checkbox, {
label: 'Delete username or IP address',
align: 'inline',
classes: ['p4-aio-checkbox'],
}
),
new OO.ui.FieldLayout(
this.revisiondelete_oversight_checkbox, {
label: 'Oversight',
align: 'inline',
help: 'Suppress data from administrators as well as others. Do not check this option unless you have the "suppressrevision" right.',
classes: ['p4-aio-checkbox'],
}
),
new OO.ui.FieldLayout(
new OO.ui.Widget({
content: [
this.revisiondelete_reason_dropdown,
this.revisiondelete_reason_button,
this.revisiondelete_reason_input,
]
}), {
label: 'Reason',
classes: ['p4-aio-reason'],
}
),
]);
// Update here if new fieldsets are introduced
for (let content of [
this.top_fieldset, this.rollback_fieldset, this.block_fieldset,
this.pagedelete_fieldset, this.revisiondelete_fieldset,
]) {
this.panel.$element.append(content.$element);
}
this.$body.append(this.panel.$element);
// CSS styles
// Make checkboxes stay in the same line if possible ...
$('.p4-aio-checkbox').css({
display: 'inline-block',
'margin-right': '12px',
// workaround: the height of checkboxes with a tooltip are different from those without one
// thus set height of every checkbox to 21px to align them
height: '21px',
});
// ... except those marked as block
$('.p4-aio-checkbox.p4-aio-checkbox-block').css({
display: 'block',
'font-weight': 'bold',
});
// The header consumes too much space by default (40%)
$('.p4-aio-form .oo-ui-fieldLayout-align-left .oo-ui-fieldLayout-header').css({
width: '20%',
});
$('.p4-aio-form .oo-ui-fieldLayout-align-left .oo-ui-fieldLayout-field').css({
width: '80%',
});
// Border for rollback, block, etc.
$('.p4-aio-form .oo-ui-fieldsetLayout:nth-child(n+2)').css({
border: '1px solid #a2a9b1',
'border-radius': '2px',
padding: '12px',
});
// Make the button nowrap in reason fields and the text input be in the new line
$('.p4-aio-reason .oo-ui-fieldLayout-field > .oo-ui-widget').css({
display: 'flex',
'flex-wrap': 'wrap',
});
$('.p4-aio-reason .oo-ui-fieldLayout-field > .oo-ui-widget > .oo-ui-dropdownWidget').css({
'flex-basis': 0,
'flex-grow': 1,
});
$('.p4-aio-reason .oo-ui-fieldLayout-field > .oo-ui-widget > .oo-ui-buttonWidget').css({
'margin-right': 0,
});
$('.p4-aio-reason .oo-ui-fieldLayout-field > .oo-ui-widget > .oo-ui-textInputWidget').css({
'flex-basis': '100%',
});
}
AIODialog.prototype.getActionProcess = function(action) {
if (action === 'start') {
return new OO.ui.Process(function() {
// Load config
config.username = this.top_username_input.getValue();
if (!config.username) return;
config.suffix = this.top_suffix_dropdown.getMenu().findSelectedItem().getData();
config.isIP = mw.util.isIPAddress(config.username);
config.endtime = getValueFromDropdownAndInput('top_endtime');
config.rollback = this.rollback_checkbox.isSelected();
config.rollbackBot = this.rollback_bot_checkbox.isSelected();
config.rollbackShow = this.rollback_showname_checkbox.isSelected();
config.block = this.block_checkbox.isSelected();
config.blockDur = getValueFromDropdownAndInput('block_duration');
config.blockReason = getValueFromDropdownAndInput('block_reason', other_data = '', check_include = true);
config.blockReason += config.suffix;
config.blockAnon = config.isIP && !this.block_hardblock_checkbox.isSelected();
config.blockAuto = !config.isIP && this.block_autoblock_checkbox.isSelected();
config.blockCreate = this.block_create_checkbox.isSelected();
config.blockTalk = this.block_talk_checkbox.isSelected();
config.blockMail = this.block_mail_checkbox.isSelected();
config.blockHide = this.block_hidename_checkbox.isSelected();
config.massdel = this.pagedelete_checkbox.isSelected();
config.massdelReason = getValueFromDropdownAndInput('pagedelete_reason', other_data = '', check_include = true);
config.massdelReason += config.suffix;
config.rd = this.revisiondelete_checkbox.isSelected();
config.rdHides = '';
if (this.revisiondelete_content_checkbox.isSelected()) config.rdHides += 'content|';
if (this.revisiondelete_summary_checkbox.isSelected()) config.rdHides += 'comment|';
if (this.revisiondelete_username_checkbox.isSelected()) config.rdHides += 'user|';
config.rdReason = getValueFromDropdownAndInput('revisiondelete_reason', other_data = '', check_include = true);
config.rdReason += config.suffix;
config.os = this.revisiondelete_oversight_checkbox.isSelected();
// Begin execution
work();
this.close();
}, this);
}
return AIODialog.super.prototype.getActionProcess.call(this, action);
};
// Set dialog width to be the same as its panel
AIODialog.prototype.getBodyWidth = function() {
return this.panel.$element.outerWidth(true);
};
$(document.body).append(windowManager.$element);
aio_dialog = new AIODialog({
size: 'large',
});
windowManager.addWindows([aio_dialog]);
}
// If `data` exists in dropdown, select it;
// else fill the input box and select "other" (provided by `other_data`)
const selectDropdownOrFillInput = function(field, data, other_data = 'other') {
let dropdown = aio_dialog[field + '_dropdown'];
let input = aio_dialog[field + '_input'];
if (!dropdown || !input) return;
let menu = dropdown.getMenu();
if (!menu) return;
menu.selectItemByData(data);
if (menu.findSelectedItem() === null) {
menu.selectItemByData(other_data);
input.setValue(data);
}
}
// If `data` selected is other, use the value from input box
const getValueFromDropdownAndInput = function(field, other_data = 'other', check_include = false) {
let dropdown = aio_dialog[field + '_dropdown'];
let input = aio_dialog[field + '_input'];
if (!dropdown || !input) return;
let menu = dropdown.getMenu();
if (!menu) return;
let selected = menu.findSelectedItem().getData();
let text = input.getValue();
if (check_include) {
if (text.includes(selected)) {
// Do not include reason in dropdown again
return text;
} else {
// Construct a reason like "dropdown: input"
return selected + (text ? ': ' + text : '');
}
}
return selected && selected === other_data ? text : selected;
}
$(mw.util.addPortletLink('p-cactions', '#', 'All-in-one')).click(function (e) {
init();
// Avoid re-loading config when user closed and opens the dialog again
if (loaded) {
windowManager.openWindow(aio_dialog, {});
return;
}
loaded = true;
aio_dialog.package_loaded = false;
if (typeof(p4js_all_in_one) !== 'object') p4js_all_in_one = {};
// load drop down reasons
let suffixes = p4js_all_in_one.suffixes || [" (all-in-one)", " "];
let reasons = p4js_all_in_one.reasons || {
block: ["Vandalism", "Spamming links to external sites", "Intimidating behavior or harassment", "Unacceptable username", "Open proxy", "Adding nonsense or gibberish content into pages"],
pagedelete: ["Blatant advertising or spam, as [[m:Category:Global policies|defined globally (by policy or convention)]]", "Vandalism"],
revisiondelete: ["Grossly insulting, degrading, or offensive material", "Purely disruptive material"],
};
for (let suffix of suffixes) {
if (!suffix) continue;
aio_dialog.top_suffix_dropdown.getMenu().addItems(new OO.ui.MenuOptionWidget({
data: suffix,
label: suffix,
}));
}
aio_dialog.top_suffix_dropdown.getMenu().addItems(
suffixes
.filter(e => e) // empty suffix is already included in dropdown
.map(e => new OO.ui.MenuOptionWidget({
data: e,
label: e,
}))
);
// Select the empty suffix by default
aio_dialog.top_suffix_dropdown.getMenu().selectItemByData('');
for (let action of ['block', 'pagedelete', 'revisiondelete']) {
aio_dialog[action + '_reason_dropdown'].getMenu().addItems(
(reasons[action] || []).map(e => new OO.ui.MenuOptionWidget({
data: e,
label: e,
})),
0 // Prepend to option list
);
}
// load packages (or the default one)
const default_package = {
tracingedits: {
duration: 3600,
indefregistered: true,
},
rollback: {
enabled: true,
bot: false,
showname: true,
},
block: {
enabled: true,
duration: "1 day",
indefregistered: true,
reason: "Vandalism",
autoblock: true,
hardblock: false,
create: true,
talk: false,
mail: false,
hidename: false,
},
pagedelete: {
enabled: true,
reason: "Vandalism",
},
revisiondelete: {
enabled: false,
content: true,
summary: true,
username: false,
reason: "Purely disruptive material",
oversight: false,
},
};
let packages = p4js_all_in_one.packages || {};
if (!packages.Default) {
packages.Default = default_package;
}
aio_dialog.top_package_dropdown.getMenu().addItems(
Object.keys(packages).map(e => new OO.ui.MenuOptionWidget({
data: e,
label: e,
})),
0
);
// When dropdown changes (manually or automatically), decide whether to hide corresponding text input or not
for (let field of ['top_endtime', 'block_duration']) {
['choose', 'select'].forEach(e => aio_dialog[field + '_dropdown'].getMenu().on(e, function(item) {
aio_dialog[field + '_input'].toggle(item.getData() === 'other');
}));
}
// When package changes (manually or automatically), change various config
['choose', 'select'].forEach(e => aio_dialog.top_package_dropdown.getMenu().on(e, function(item) {
// Avoid config being changed when user closed and opens the dialog again
if (e == 'select' && aio_dialog.package_loaded) return;
aio_dialog.package_loaded = true;
let isIP = mw.util.isIPAddress(aio_dialog.top_username_input.getValue());
let pkg = packages[item.getData()] || {};
// checkboxes
for (let [action, action_config] of Object.entries(pkg)) {
for (let [item, item_enabled] of Object.entries(action_config)) {
if (typeof item_enabled === "boolean" && item !== "enabled" && item !== "indefregistered") {
aio_dialog[`${action}_${item}_checkbox`].setSelected(item_enabled);
}
}
}
// tracing edits
pkg.tracingedits = pkg.tracingedits || {};
selectDropdownOrFillInput('top_endtime', pkg.tracingedits.duration || 3600);
if (pkg.tracingedits.indefregistered && !isIP) {
aio_dialog.top_endtime_dropdown.getMenu().selectItemByData('inf');
}
// rollback: default is enabled
aio_dialog.rollback_checkbox.setSelected(pkg.rollback.enabled || true);
// block
pkg.block = pkg.block || {};
aio_dialog.block_checkbox.setSelected(pkg.block.enabled || false);
selectDropdownOrFillInput('block_duration', pkg.block.duration || '1 day');
if (pkg.tracingedits.indefregistered && !isIP) {
aio_dialog.block_duration_dropdown.getMenu().selectItemByData('never');
}
selectDropdownOrFillInput('block_reason', pkg.block.reason || 'Long-term abuse', '');
// page deletion
pkg.pagedelete = pkg.pagedelete || {};
aio_dialog.pagedelete_checkbox.setSelected(pkg.pagedelete.enabled || false);
selectDropdownOrFillInput('pagedelete_reason', pkg.pagedelete.reason || 'Vandalism', '');
// revision deletion
pkg.revisiondelete = pkg.revisiondelete || {};
aio_dialog.revisiondelete_checkbox.setSelected(pkg.revisiondelete.enabled || false);
selectDropdownOrFillInput('revisiondelete_reason', pkg.revisiondelete.reason || 'Hiding blatant offending materials', '');
}));
// select the default package
if (p4js_all_in_one.default_package && packages[p4js_all_in_one.default_package]) {
aio_dialog.top_package_dropdown.getMenu().selectItemByData(p4js_all_in_one.default_package);
} else {
aio_dialog.top_package_dropdown.getMenu().selectItemByData('Default');
}
// Append reasons
for (let action of ['pagedelete', 'revisiondelete']) {
aio_dialog[action + '_reason_button'].on('click', function() {
let text = aio_dialog[action + '_reason_input'].getValue();
aio_dialog[action + '_reason_input'].setValue(text + '; ' + aio_dialog[action + '_reason_dropdown'].getMenu().findSelectedItem().getData());
aio_dialog[action + '_reason_dropdown'].getMenu().selectItemByData('');
})
}
aio_dialog.top_username_input.on('change', function(username) {
let isIP = mw.util.isIPAddress(username);
let pkg = packages[aio_dialog.top_package_dropdown.getMenu().getData()] || default_package;
// Check indef
if (pkg.tracingedits && pkg.tracingedits.indefregistered && !isIP) {
aio_dialog.top_endtime_dropdown.getMenu().selectItemByData('inf');
aio_dialog.block_duration_dropdown.getMenu().selectItemByData('never');
} else {
pkg.tracingedits = pkg.tracingedits || {};
pkg.block = pkg.block || {};
selectDropdownOrFillInput('top_endtime', pkg.tracingedits.duration || 3600);
selectDropdownOrFillInput('block_duration', pkg.block.duration || '1 day');
}
// Anon block / registered user block settings
if (isIP) {
$('.p4-aio-reg-only').hide();
$('.p4-aio-anon-only').show();
} else {
$('.p4-aio-reg-only').show();
$('.p4-aio-anon-only').hide();
}
});
// Make fieldset transparent if not enabled
for (let action of ['rollback', 'block', 'pagedelete', 'revisiondelete']) {
aio_dialog[action + '_checkbox'].on('change', function(selected, _) {
$(`.p4-aio-${action}-fieldset`).css({
opacity: selected ? '100%': '50%',
});
});
// Initialize
$(`.p4-aio-${action}-fieldset`).css({
opacity: aio_dialog[action + '_checkbox'].isSelected() ? '100%': '50%',
transition: 'opacity 0.3s',
});
}
// enter to submit
$(document).on("keyup", event => {
if(event.key !== "Enter" || !aio_dialog.isOpened()) return;
aio_dialog.executeAction('start');
event.preventDefault();
});
// fill the current username if applicable
aio_dialog.top_username_input.setValue(mw.config.get('wgRelevantUserName') || '');
aio_dialog.top_username_input.focus();
windowManager.openWindow(aio_dialog, {});
});
});
});