User:Mr. Stradivarius/gadgets/SpamUserPage.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
This user script seems to have a documentation page at User:Mr. Stradivarius/gadgets/SpamUserPage and an accompanying .css page at User:Mr. Stradivarius/gadgets/SpamUserPage.css. |
// <nowiki>
/*
* SpamUserPage
*
* This gadget deletes a user page, blocks the user, and notifies them on their
* talk page. See [[User:Mr. Stradivarius/gadgets/SpamUserPage]] for
* documentation.
*
* To install the script, add the following to your personal .js page:
importScript( 'User:Mr. Stradivarius/gadgets/SpamUserPage.js' ); // Linkback: [[User:Mr. Stradivarius/gadgets/SpamUserPage.js]]
* Author: Mr. Stradivarius
* Licence: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015 Mr. Stradivarius
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
mw.loader.using( [
'mediawiki.api',
'mediawiki.Title',
'mediawiki.util',
'oojs-ui'
], function () {
"use strict";
var config = mw.config.get( [
'wgTitle',
'wgNamespaceNumber',
'wgUserName'
] );
// Exit if we are not in user or user talk space.
if ( config.wgNamespaceNumber !== 2 && config.wgNamespaceNumber !== 3 ) {
return;
}
/**************************************************************************
* ApiManager class
**************************************************************************/
var ApiManager = function () {
var i, len, preset, key, customPresetIds, defaultPreset, hasUserPreference;
this.api = new mw.Api();
this.currentTitle = new mw.Title( config.wgTitle, config.wgNamespaceNumber );
this.userName = this.currentTitle.getMainText().replace( /\/.*$/, '' );
this.userTalkTitle = new mw.Title( this.userName, 3 );
this.userPreferences = typeof window.SpamUserPage === 'object' ? window.SpamUserPage : {};
// Set up caches
this.menuItemCache = {};
// Get presets
this.presets = {};
this.presetIds = [];
customPresetIds = []; // Track custom preset IDs separately so that we can put them first.
function setPreset( obj, thisArg ) {
var ret = thisArg.presets[ obj.id ] || {};
ret.id = obj.id;
for ( key in ApiManager.static.presetDefaults ) {
if ( ApiManager.static.presetDefaults.hasOwnProperty( key ) ) {
if ( obj[ key ] !== undefined ) {
ret[ key ] = obj[ key ];
}
if ( ret[ key ] === undefined ) {
ret[ key ] = ApiManager.static.presetDefaults[ key ];
}
}
}
thisArg.presets[ ret.id ] = ret;
}
for ( i = 0, len = ApiManager.static.presets.length; i < len; i++ ) {
preset = ApiManager.static.presets[ i ];
this.presetIds.push( preset.id );
setPreset( preset, this );
}
if ( $.isArray( this.userPreferences.presets ) ) {
for ( i = 0, len = this.userPreferences.presets.length; i < len; i++ ) {
preset = this.userPreferences.presets[ i ];
if ( typeof preset === 'object' ) {
if ( typeof preset.id === 'string' ) {
if ( !this.presets[ preset.id ] ) {
customPresetIds.push( preset.id );
}
setPreset( preset, this );
} else {
throw new Error( "missing or invalid 'id' field in custom preset #" + i );
}
}
}
}
this.presetIds = customPresetIds.concat( this.presetIds );
// Get defaults
this.defaults = {};
hasUserPreference = false;
if ( typeof this.userPreferences.preset === 'string' ) {
defaultPreset = this.userPreferences.preset;
if ( !this.presets[ defaultPreset ] ) {
throw new Error( "'" + defaultPreset + "' is not a valid preset ID" );
}
} else {
defaultPreset = ApiManager.static.otherDefaults.preset;
}
function setDefaults( obj, thisArg ) {
for ( key in obj ) {
if ( obj.hasOwnProperty( key ) ) {
if ( thisArg.userPreferences[ key ] !== undefined ) {
hasUserPreference = true;
thisArg.defaults[ key ] = thisArg.userPreferences[ key ];
} else if ( thisArg.presets[ defaultPreset ][ key ] !== undefined ) {
thisArg.defaults[ key ] = thisArg.presets[ defaultPreset ][ key ];
} else {
thisArg.defaults[ key ] = ApiManager.static.otherDefaults[ key ];
}
}
}
}
setDefaults( ApiManager.static.presetDefaults, this );
setDefaults( ApiManager.static.otherDefaults, this );
if ( hasUserPreference ) {
this.defaults.preset = null;
} else {
this.defaults.preset = defaultPreset;
}
};
OO.initClass( ApiManager );
ApiManager.static.presets = [
{
id: 'spamublock',
label: 'Spam-only account, username violation',
deletesummary: '[[WP:CSD#G11|G11]]: Unambiguous [[WP:NOTADVERTISING|advertising]] or promotion',
blocksummary: '{{uw-spamublock}}',
editsummary: 'You have been indefinitely blocked from editing because your account is being ' +
'used only for [[WP:SPAM|spam or advertising]] and your username is a violation of the ' +
'[[WP:U|username policy]].',
template: '{{subst:uw-spamublock|sig=yes}}'
},
{
id: 'soablock',
label: 'Spam-only account',
deletesummary: '[[WP:CSD#G11|G11]]: Unambiguous [[WP:NOTADVERTISING|advertising]] or promotion',
blocksummary: '[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account',
editsummary: 'You have been indefinitely blocked from editing because your account is being ' +
'used only for [[WP:SPAM|spam, advertising, or promotion]]',
template: '{{subst:uw-soablock|sig=yes}}'
},
{
id: 'sblock',
label: 'Spamming',
deletesummary: '[[WP:CSD#G11|G11]]: Unambiguous [[WP:NOTADVERTISING|advertising]] or promotion',
expiry: '31 hours',
blocksummary: 'Using Wikipedia for [[WP:SPAM|spam]] purposes',
editsummary: 'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes',
template: '{{subst:uw-sblock|sig=yes}}'
},
{
id: 'vaublock',
label: 'Vandalism-only account, username violation',
deletesummary: '[[WP:CSD#G3|G3]]: [[WP:Vandalism|Vandalism]]',
blocksummary: '{{uw-vaublock}}',
editsummary: 'You have been indefinitely blocked from editing because your account is being ' +
'[[WP:VOA|used only for vandalism]] and your username is a blatant violation of the ' +
'[[WP:U|username policy]]',
template: '{{subst:uw-vaublock|sig=yes}}'
},
{
id: 'voablock',
label: 'Vandalism-only account',
deletesummary: '[[WP:CSD#G3|G3]]: [[WP:Vandalism|Vandalism]]',
blocksummary: '[[WP:Vandalism-only account|Vandalism-only account]]',
editsummary: 'You have been indefinitely blocked from editing because your account is being ' +
'[[WP:VOA|used only for vandalism]]',
template: '{{subst:uw-voablock|sig=yes}}'
},
{
id: 'vblock',
label: 'Vandalism',
deletesummary: '[[WP:CSD#G3|G3]]: [[WP:Vandalism|Vandalism]]',
expiry: '31 hours',
blocksummary: '[[WP:Vandalism|Vandalism]]',
editsummary: 'You have been blocked from editing for persistent [[WP:VAND|vandalism]]',
template: '{{subst:uw-vblock|sig=yes}}'
},
{
id: 'softerblock',
label: 'Promotional username, soft block',
deletesummary: '[[WP:CSD#G11|G11]]: Unambiguous [[WP:NOTADVERTISING|advertising]] or promotion',
blocksummary: '{{uw-softerblock}}',
editsummary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] ' +
'gives the impression that the account represents a group, organization or website.',
template: '{{subst:uw-softerblock|sig=yes}}',
nocreate: false,
autoblock: false
},
{
id: 'causeblock',
label: 'Username of a non-profit, soft block',
deletesummary: '[[WP:CSD#G11|G11]]: Unambiguous [[WP:NOTADVERTISING|advertising]] or promotion',
blocksummary: '{{uw-causeblock}}',
editsummary: 'You have been indefinitely blocked from editing because your [[WP:U|username]] ' +
'gives the impression that the account represents a group, organization or website.',
template: '{{subst:uw-causeblock|sig=yes}}',
nocreate: false,
autoblock: false
},
{
id: 'copyrightblock',
label: 'Copyright violations',
deletesummary: '[[WP:CSD#G12|G12]]: Unambiguous [[WP:CV|copyright infringement]]',
expiry: '24 hours',
blocksummary: '[[WP:Copyright violations|Copyright violations]]',
editsummary: 'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]',
template: '{{subst:uw-copyrightblock|sig=yes}}'
},
{
id: 'myblock',
label: 'Using Wikipedia as a blog or web host',
deletesummary: '[[WP:CSD#U5|U5]]: [[WP:NOTWEBHOST|Misuse of Wikipedia as a web host]]',
expiry: '24 hours',
blocksummary: 'Using Wikipedia as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]',
editsummary: 'You have been blocked from editing for using user and/or article pages ' +
'as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]',
template: '{{subst:uw-myblock|sig=yes}}'
},
{
id: 'npblock',
label: 'Creating nonsense pages',
deletesummary: '[[WP:CSD#G1|G1]]: [[WP:PN|Patent nonsense]], meaningless, or incomprehensible',
expiry: '24 hours',
blocksummary: 'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages',
editsummary: 'You have been blocked from editing for creating [[WP:PN|nonsense pages]]',
template: '{{subst:uw-npblock|sig=yes}}'
}
];
ApiManager.static.menuDefaults = {
expiry: [
'indefinite',
'3 hours',
'12 hours',
'24 hours',
'31 hours',
'36 hours',
'48 hours',
'60 hours',
'72 hours',
'1 week',
'2 weeks',
'1 month',
'3 months',
'6 months',
'1 year',
'2 years',
'3 years'
],
deletesummary: [
// If you change the order of these, please also update the number of the
// default deletion summary in ApiManager.static.presetDefaults.
'[[WP:CSD#G1|G1]]: [[WP:PN|Patent nonsense]], meaningless, or incomprehensible',
'[[WP:CSD#G2|G2]]: Test page',
'[[WP:CSD#G3|G3]]: [[WP:Vandalism|Vandalism]]',
'[[WP:CSD#G3|G3]]: Blatant [[WP:Do not create hoaxes|hoax]]',
'[[WP:CSD#G4|G4]]: Recreation of a page that was [[WP:DEL|deleted]] per a [[WP:XFD|deletion discussion]]',
'[[WP:CSD#G5|G5]]: Creation by a [[WP:BLOCK|blocked]] or [[WP:BAN|banned]] user in violation of block or ban',
'[[WP:CSD#G6|G6]]: Housekeeping and routine (non-controversial) cleanup',
'[[WP:CSD#G7|G7]]: One author who has requested deletion or blanked the page',
'[[WP:CSD#G8|G8]]: Page dependent on a deleted or nonexistent page',
'[[WP:CSD#G10|G10]]: [[WP:ATP|Attack page]] or negative unsourced [[WP:BLP|BLP]]',
'[[WP:CSD#G11|G11]]: Unambiguous [[WP:NOTADVERTISING|advertising]] or promotion',
'[[WP:CSD#G12|G12]]: Unambiguous [[WP:CV|copyright infringement]]',
'[[WP:CSD#G13|G13]]: Abandoned [[WP:AFC|Article for creation]]? to retrieve it, see [[WP:REFUND/G13]]',
'[[WP:CSD#U1|U1]]: User request to delete page in own userspace',
'[[WP:CSD#U2|U2]]: Userpage or subpage of a nonexistent user',
'[[WP:CSD#U3|U3]]: [[WP:NFC|Non-free]] [[Help:Gallery|gallery]]',
'[[WP:CSD#U5|U5]]: [[WP:NOTWEBHOST|Misuse of Wikipedia as a web host]]'
],
blocksummary: [
'{{uw-spamublock}}',
'[[WP:Spam|Spam]] / [[WP:NOTADVERTISING|advertising]]-only account',
'Using Wikipedia for [[WP:Spam|spam]] or [[WP:NOTADVERTISING|advertising]] purposes',
'{{uw-vaublock}}',
'[[WP:Vandalism-only account|Vandalism-only account]]',
'[[WP:Vandalism|Vandalism]]',
'[[WP:Copyright violations|Copyright violations]]',
'{{uw-softerblock}}',
'{{uw-causeblock}}',
'Creating [[WP:Attack page|attack pages]]',
'Violations of the [[WP:Biographies of living persons|Biographies of living persons]] policy',
'Creating [[WP:Patent nonsense|patent nonsense]] or other inappropriate pages',
'[[WP:Disruptive editing|Disruptive editing]]',
'[[WP:No personal attacks|Personal attacks]] or [[WP:Harassment|harassment]]',
'[[WP:Blocking policy#Evasion of blocks|Block evasion]]',
'Abusing [[WP:Sock puppetry|multiple accounts]]',
'[[WP:Long-term abuse|Long-term abuse]]',
],
editsummary: [
'You have been blocked from editing because your account ' +
'is being used only for [[WP:SPAM|spam or advertising]] and your ' +
'username is a violation of the [[WP:U|username policy]].',
'You have been blocked from editing for [[WP:SOAP|advertising or self-promotion]]',
'You have been blocked from editing for using Wikipedia for [[WP:SPAM|spam]] purposes',
'You have been blocked from editing because your account is being ' +
'used only for [[WP:SPAM|spam, advertising, or promotion]]',
'You have been indefinitely blocked from editing because your account ' +
'is being [[WP:VOA|used only for vandalism]] and your username is ' +
'a blatant violation of the [[WP:U|username policy]]',
'You have been blocked from editing for persistent [[WP:VAND|vandalism]]',
'You have been blocked from editing because your account is being ' +
'[[WP:VOA|used only for vandalism]]',
'You have been indefinitely blocked from editing because your [[WP:U|username]] ' +
'gives the impression that the account represents a group, organization or website.',
'You have been blocked from editing for continued [[WP:COPYVIO|copyright infringement]]',
'You have been blocked from editing for using user and/or article ' +
'pages as a [[WP:NOTMYSPACE|blog, web host, social networking site or forum]]',
],
template: [
'{{subst:uw-spamublock|sig=yes}}',
'{{subst:uw-sblock|sig=yes}}',
'{{subst:uw-soablock|sig=yes}}',
'{{subst:uw-adblock|sig=yes}}',
'{{subst:uw-aoablock|sig=yes}}',
'{{subst:uw-vaublock|sig=yes}}',
'{{subst:uw-vblock|sig=yes}}',
'{{subst:uw-voablock|sig=yes}}',
'{{subst:uw-softerblock|sig=yes}}',
'{{subst:uw-causeblock|sig=yes}}',
'{{subst:uw-copyrightblock|sig=yes}}',
'{{subst:uw-myblock|sig=yes}}'
]
};
ApiManager.static.presetDefaults = {
label: '<label missing>',
deletesummary: ApiManager.static.menuDefaults.deletesummary[ 10 ],
expiry: ApiManager.static.menuDefaults.expiry[ 0 ],
blocksummary: ApiManager.static.menuDefaults.blocksummary[ 0 ],
nocreate: true,
autoblock: true,
noemail: false,
nousertalk: false,
template: ApiManager.static.menuDefaults.template[ 0 ],
editsummary: ApiManager.static.menuDefaults.editsummary[ 0 ]
};
ApiManager.static.otherDefaults = {
preset :ApiManager.static.presets[ 0 ].id,
watchlist: 'preferences',
menulocation: 'p-cactions',
menuposition: null,
oneclick: false
};
ApiManager.static.months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
ApiManager.static.summarySuffix = '([[WP:SUPG|SUPG]])';
ApiManager.prototype.getCurrentTitle = function () {
return this.currentTitle;
};
ApiManager.prototype.getUserName = function () {
return this.userName;
};
ApiManager.prototype.getUserTalkTitle = function () {
return this.userTalkTitle;
};
ApiManager.prototype._getNotificationDate = function () {
// We cache the result of this method so that we will always get the
// same date string as we used for the talk notification, even if a
// user happened to have the page open over a month boundary.
var date, month;
if ( !this.notificationDate ) {
date = new Date();
month = this.constructor.static.months[ date.getMonth() ];
this.notificationDate = month + ' ' + date.getFullYear();
}
return this.notificationDate;
};
ApiManager.prototype.getDefault = function ( key ) {
return this.defaults[ key ];
};
ApiManager.prototype.getPresetIds = function () {
return this.presetIds;
};
ApiManager.prototype.getPresetValue = function ( presetId, key ) {
return this.presets[ presetId ][ key ];
};
ApiManager.prototype.getMenuItems = function ( key ) {
var menuItems, pref, keyItems, isDupe, i, len, val;
if ( typeof key !== 'string' ) {
throw new TypeError( "argument #1 to 'getMenuItems' was not a string" );
}
function copyArray ( arr ) {
var ret = [];
for ( var i = 0, len = arr.length; i < len; i++ ) {
ret[ i ] = arr[ i ];
}
return ret;
}
if ( this.menuItemCache[ key ] === undefined ) {
menuItems = this.constructor.static.menuDefaults[ key ];
if ( menuItems ) {
this.menuItemCache[ key ] = menuItems;
pref = this.userPreferences[ key ];
if ( pref ) {
isDupe = false;
for ( i = 0, len = menuItems.length; i < len; i++ ) {
val = menuItems[ i ];
if ( val === pref ) {
isDupe = true;
break;
}
}
if ( !isDupe ) {
// Create a copy of the static array so we can manipulate it.
menuItems = copyArray( menuItems );
menuItems.unshift( pref );
this.menuItemCache[ key ] = menuItems;
}
}
} else {
this.menuItemCache[ key ] = null;
}
}
return this.menuItemCache[ key ];
};
ApiManager.prototype.addPortletLink = function () {
return mw.util.addPortletLink(
this.getDefault( 'menulocation' ),
'#',
'Delete and block',
'ca-spamuserpage',
'Delete this page and block this user',
null,
this.getDefault( 'menuposition' )
);
};
ApiManager.prototype.openTalkPage = function () {
window.open(
this.getUserTalkTitle().getUrl() +
'#' +
mw.util.wikiUrlencode( this._getNotificationDate() ),
'_top'
);
};
ApiManager.prototype._deleteCurrentPage = function ( options ) {
options = options || {};
var reason = options.deletesummary !== undefined ?
options.deletesummary :
this.getDefault( 'deletesummary' );
reason += ' ' + this.constructor.static.summarySuffix;
return this.api.postWithToken( 'delete', {
format: 'json',
action: 'delete',
title: this.currentTitle.getPrefixedText(),
reason: reason,
watchlist: options.watchlist || this.getDefault( 'watchlist' )
} );
};
ApiManager.prototype._blockUser = function ( options, deletePromise ) {
var deleteFinishedPromise, reason;
var apiManager = this;
options = options || {};
// Make a new promise that is resolved when the delete promise is
// either resolved or rejected.
deleteFinishedPromise = $.Deferred( function ( deferred ) {
deletePromise.always( function () {
deferred.resolve();
} );
} ).promise();
reason = options.blocksummary !== undefined ?
options.blocksummary :
apiManager.getDefault( 'blocksummary' );
reason += ' <!-- ' + apiManager.constructor.static.summarySuffix + ' -->';
return deleteFinishedPromise.then( function () {
return apiManager.api.postWithToken( 'block', {
format: 'json',
action: 'block',
user: apiManager.getUserName(),
expiry: options.expiry !== undefined ?
options.expiry :
apiManager.getDefault( 'expiry' ),
reason: reason,
nocreate: options.nocreate ? '' : undefined,
autoblock: options.autoblock ? '' : undefined,
noemail: options.noemail ? '' : undefined,
allowusertalk: !options.nousertalk ? '' : undefined
} );
} );
};
ApiManager.prototype._postTalkNotification = function ( options, blockPromise ) {
var self = this;
options = options || {};
return $.Deferred( function ( deferred ) {
// Reject the deferred if the block failed.
blockPromise.fail( function () {
return deferred.reject( 'usernotblocked', { error: {
id: 'usernotblocked',
info: 'There was an error while trying to block the user, ' +
'so notification was aborted'
} } );
} );
blockPromise.done( function () {
// Get the talk page content. We will use this to check if there is
// already a section heading for the current month.
return self.api.get( {
format: 'json',
action: 'query',
prop: 'revisions',
rvprop: 'content',
indexpageids: '',
titles: self.getUserTalkTitle().getPrefixedText(),
redirects: ''
} ).then( function ( obj ) {
var title, content, headings, lastHeading,
notificationDate, editPromise, summary;
var template = options.template !== undefined ?
options.template :
self.getDefault( 'template' );
var pageid = obj.query.pageids[ 0 ];
// If we got a redirect from the GET request, follow it.
if ( obj.query.redirects ) {
title = obj.query.redirects[ 0 ].to;
} else {
title = self.getUserTalkTitle().getPrefixedText();
}
if ( pageid === '-1' ) {
// The page doesn't exist.
content = '';
} else {
// The page exists; get the content.
content = obj.query.pages[ pageid ].revisions[ 0 ][ '*' ];
// Separate our notification from whatever the previous content was.
if ( content.match( /\S/ ) ) {
content += '\n\n';
}
}
// Add a heading for the current month if it is not already the
// last heading on the page.
headings = content.match( /^==[^=].*==[ \t]*$/gm );
if ( headings ) {
lastHeading = headings[ headings.length - 1 ].slice( 2, -2 ).trim();
}
notificationDate = self._getNotificationDate();
if ( !lastHeading || lastHeading !== notificationDate ) {
content += '== ' + notificationDate + ' ==\n\n';
}
// Add the block template.
content += template;
// Generate the edit summary.
summary = options.editsummary !== undefined ?
options.editsummary :
self.getDefault( 'editsummary' );
summary += ' ' + self.constructor.static.summarySuffix;
// Overwrite the page with our new content.
editPromise = self.api.postWithToken( 'edit', {
format: 'json',
action: 'edit',
title: title,
summary: summary,
notminor: '',
text: content,
watchlist: options.watchlist || self.getDefault( 'watchlist' )
} );
editPromise.done( function () {
return deferred.resolve();
} );
editPromise.fail( function ( id, obj ) {
return deferred.reject( id, obj );
} );
} );
} );
} ).promise();
};
ApiManager.prototype.submit = function ( options ) {
var promises = {};
options = options || {};
promises[ 'delete' ] = this._deleteCurrentPage( options );
promises.block = this._blockUser( options, promises[ 'delete' ] );
promises.notify = this._postTalkNotification( options, promises.block );
promises.all = $.when(
promises[ 'delete' ],
promises.block,
promises.notify
);
return promises;
};
/**************************************************************************
* Dialog class
**************************************************************************/
var Dialog = function ( options ) {
options = options || {};
this.apiManager = new ApiManager();
this.hasBeenSubmitted = false;
Dialog.super.call( this, options );
};
OO.inheritClass( Dialog, OO.ui.ProcessDialog );
Dialog.static.name = 'SpamUserPageDialog';
Dialog.static.title = 'Delete, block and notify';
Dialog.static.actions = [
{ action: 'submit', label: 'Submit', flags: [ 'primary', 'destructive' ] },
{ label: 'Cancel', flags: 'safe' }
];
Dialog.prototype.getApiManager = function () {
return this.apiManager;
};
Dialog.prototype.getBodyHeight = function () {
return 440;
};
Dialog.prototype.initialize = function () {
Dialog.super.prototype.initialize.apply( this, arguments );
var apiManager = this.getApiManager();
// Converts an array of data from ApiManager into a format usable with
// OOjs-ui menus.
function makeMenu( arr, labelCallback ) {
var i, len;
var items = [];
for ( i = 0, len = arr.length; i < len; i++ ) {
items[ i ] = new OO.ui.MenuOptionWidget( {
data: arr[ i ],
label: labelCallback ? labelCallback( arr[ i ] ) : arr[ i ]
} );
}
return { items: items };
}
// Initialize edit panel
this.editPanel = new OO.ui.PanelLayout( {
expanded: false
} );
this.editFieldset = new OO.ui.FieldsetLayout( {
classes: [ 'container' ]
} );
this.editPanel.$element.append( this.editFieldset.$element );
// Initialize preset widget
this.presetDropdown = new OO.ui.DropdownWidget( {
data: { label: 'Select a preset' },
menu: makeMenu( apiManager.getPresetIds(), function ( id ) {
return apiManager.getPresetValue( id, 'label' );
} )
} );
if ( apiManager.getDefault( 'preset' ) ) {
this.presetDropdown.getMenu().selectItemByData(
apiManager.getDefault( 'preset' )
);
} else {
this.presetDropdown.setLabel( this.presetDropdown.getData().label );
}
this.editFieldset.addItems( [
new OO.ui.FieldLayout( this.presetDropdown, {
label: 'Preset',
} )
] );
// Initialize deletion widgets
this.deleteSummaryInput = new OO.ui.ComboBoxInputWidget( {
value: apiManager.getDefault( 'deletesummary' ),
menu: makeMenu( apiManager.getMenuItems( 'deletesummary' ) )
} );
this.editFieldset.addItems( [
new OO.ui.FieldLayout( this.deleteSummaryInput, {
label: 'Deletion summary'
} )
] );
// Initialize notification widgets
this.editSummaryInput = new OO.ui.ComboBoxInputWidget( {
value: apiManager.getDefault( 'editsummary' ),
menu: makeMenu( apiManager.getMenuItems( 'editsummary' ) )
} );
this.templateInput = new OO.ui.ComboBoxInputWidget( {
value: apiManager.getDefault( 'template' ),
menu: makeMenu( apiManager.getMenuItems( 'template' ) )
} );
this.editFieldset.addItems( [
new OO.ui.FieldLayout( this.templateInput, {
label: 'Notification template'
} ),
new OO.ui.FieldLayout( this.editSummaryInput, {
label: 'Edit summary'
} )
] );
// Initialize block widgets
this.blockSummaryInput = new OO.ui.ComboBoxInputWidget( {
value: apiManager.getDefault( 'blocksummary' ),
menu: makeMenu( apiManager.getMenuItems( 'blocksummary' ) )
} );
this.expiryInput = new OO.ui.ComboBoxInputWidget( {
value: apiManager.getDefault( 'expiry' ),
menu: makeMenu( apiManager.getMenuItems( 'expiry' ) )
} );
this.nocreateToggleButton = new OO.ui.ToggleButtonWidget( {
label: 'Block account creation',
value: apiManager.getDefault( 'nocreate' )
} );
this.noemailToggleButton = new OO.ui.ToggleButtonWidget( {
label: 'Block email',
value: apiManager.getDefault( 'noemail' )
} );
this.nousertalkToggleButton = new OO.ui.ToggleButtonWidget( {
label: 'Block talk',
value: apiManager.getDefault( 'nousertalk' )
} );
this.autoblockToggleButton = new OO.ui.ToggleButtonWidget( {
label: 'Autoblock',
value: apiManager.getDefault( 'autoblock' )
} );
this.blockButtonGroup = new OO.ui.ButtonGroupWidget( {
items: [
this.nocreateToggleButton,
this.noemailToggleButton,
this.nousertalkToggleButton,
this.autoblockToggleButton
]
} );
this.editFieldset.addItems( [
new OO.ui.FieldLayout( this.blockSummaryInput, {
label: 'Block summary'
} ),
new OO.ui.FieldLayout( this.expiryInput, {
label: 'Block expiry'
} ),
new OO.ui.FieldLayout( this.blockButtonGroup, {
label: 'Block options',
align: 'top'
} ),
] );
// Initialize watchlist widgets
this.watchlistOptions = {};
this.watchlistOptions.watch = new OO.ui.ButtonOptionWidget( {
data: 'watch',
label: 'Watch',
title: 'Watch option'
} );
this.watchlistOptions.unwatch = new OO.ui.ButtonOptionWidget( {
data: 'unwatch',
label: 'Unwatch',
title: 'Unwatch option'
} );
this.watchlistOptions.preferences = new OO.ui.ButtonOptionWidget( {
data: 'preferences',
label: 'Follow preferences',
title: 'Follow preferences option'
} );
this.watchlistOptions.nochange = new OO.ui.ButtonOptionWidget( {
data: 'nochange',
label: 'No change',
title: 'No change option'
} );
this.watchlistButtonSelect = new OO.ui.ButtonSelectWidget( {
items: [
this.watchlistOptions.watch,
this.watchlistOptions.unwatch,
this.watchlistOptions.preferences,
this.watchlistOptions.nochange
]
} );
this.watchlistButtonSelect.selectItemByData(
apiManager.getDefault( 'watchlist' )
);
this.editFieldset.addItems( [
new OO.ui.FieldLayout( this.watchlistButtonSelect, {
label: 'Watchlist options',
align: 'top'
} )
] );
// Initialize submit panel
this.submitPanel = new OO.ui.PanelLayout( {
$: this.$,
expanded: false
} );
this.submitFieldset = new OO.ui.FieldsetLayout( {
classes: [ 'container' ]
} );
this.submitPanel.$element.append( this.submitFieldset.$element );
this.deleteProgressLabel = new OO.ui.LabelWidget();
this.deleteProgressField = new OO.ui.FieldLayout( this.deleteProgressLabel );
this.blockProgressLabel = new OO.ui.LabelWidget();
this.blockProgressField = new OO.ui.FieldLayout( this.blockProgressLabel );
this.notifyProgressLabel = new OO.ui.LabelWidget();
this.notifyProgressField = new OO.ui.FieldLayout( this.notifyProgressLabel );
this.openTalkProgressField = new OO.ui.FieldLayout( new OO.ui.LabelWidget() );
this.submitFieldset.addItems( [
this.deleteProgressField,
this.blockProgressField,
this.notifyProgressField,
this.openTalkProgressField
] );
// Initialize stack widget
this.stackLayout= new OO.ui.StackLayout( {
items: [ this.editPanel, this.submitPanel ],
padded: true
} );
// Add widgets to the DOM
this.$body.append( this.stackLayout.$element );
// Add event handlers
if ( apiManager.getDefault( 'preset' ) ) {
this.changeInputEventHandlers( 'on', this.onFirstChangeAfterPreset, null, this );
}
this.presetDropdown.getMenu().on( 'select', this.onPresetSelect, null, this );
};
Dialog.prototype.onPresetSelect = function () {
var presetId = this.presetDropdown.getMenu().findSelectedItem().getData();
var apiManager = this.getApiManager();
// Detach any input event handlers. If they are still attached, they will
// clear the preset selection, which we don't want.
this.changeInputEventHandlers( 'off', this.onFirstChangeAfterPreset, this );
// Set values
this.deleteSummaryInput.getInput().setValue(
apiManager.getPresetValue( presetId, 'deletesummary' )
);
this.deleteSummaryInput.getMenu().toggle( false );
this.expiryInput.getInput().setValue(
apiManager.getPresetValue( presetId, 'expiry' )
);
this.expiryInput.getMenu().toggle( false );
this.templateInput.getInput().setValue(
apiManager.getPresetValue( presetId, 'template' )
);
this.templateInput.getMenu().toggle( false );
this.editSummaryInput.getInput().setValue(
apiManager.getPresetValue( presetId, 'editsummary' )
);
this.editSummaryInput.getMenu().toggle( false );
this.blockSummaryInput.getInput().setValue(
apiManager.getPresetValue( presetId, 'blocksummary' )
);
this.blockSummaryInput.getMenu().toggle( false );
this.nocreateToggleButton.setValue(
apiManager.getPresetValue( presetId, 'nocreate' )
);
this.autoblockToggleButton.setValue(
apiManager.getPresetValue( presetId, 'autoblock' )
);
this.noemailToggleButton.setValue(
apiManager.getPresetValue( presetId, 'noemail' )
);
this.nousertalkToggleButton.setValue(
apiManager.getPresetValue( presetId, 'nousertalk' )
);
// Attach one-time event handler
this.changeInputEventHandlers( 'on', this.onFirstChangeAfterPreset, null, this );
};
Dialog.prototype.changeInputEventHandlers = function ( method, func, arg3, arg4 ) {
this.deleteSummaryInput.getInput()[ method ]( 'change', func, arg3, arg4 );
this.deleteSummaryInput.getMenu()[ method ]( 'select', func, arg3, arg4 );
this.expiryInput.getInput()[ method ]( 'change', func, arg3, arg4 );
this.expiryInput.getMenu()[ method ]( 'select', func, arg3, arg4 );
this.templateInput.getInput()[ method ]( 'change', func, arg3, arg4 );
this.templateInput.getMenu()[ method ]( 'select', func, arg3, arg4 );
this.editSummaryInput.getInput()[ method ]( 'change', func, arg3, arg4 );
this.editSummaryInput.getMenu()[ method ]( 'select', func, arg3, arg4 );
this.blockSummaryInput.getInput()[ method ]( 'change', func, arg3, arg4 );
this.blockSummaryInput.getMenu()[ method ]( 'select', func, arg3, arg4 );
this.nocreateToggleButton[ method ]( 'change', func, arg3, arg4 );
this.autoblockToggleButton[ method ]( 'change', func, arg3, arg4 );
this.noemailToggleButton[ method ]( 'change', func, arg3, arg4 );
this.nousertalkToggleButton[ method ]( 'change', func, arg3, arg4 );
};
Dialog.prototype.onFirstChangeAfterPreset = function () {
// Detach all input event handlers
this.changeInputEventHandlers( 'off', this.onFirstChangeAfterPreset, this );
// Clear the selected preset. Before doing this we detach its select event
// listener, as that will cause problems if it's still attached.
this.presetDropdown.getMenu().off( 'select', this.onPresetSelect, this );
this.presetDropdown.getMenu().selectItem();
this.presetDropdown.setLabel( this.presetDropdown.getData().label );
this.presetDropdown.getMenu().on( 'select', this.onPresetSelect, null, this );
};
Dialog.prototype.onSubmit = function () {
var self = this;
var options, promises;
var apiManager = this.getApiManager();
var title = apiManager.getCurrentTitle().getPrefixedText();
var user = apiManager.getUserName();
// Record that the dialog has been submitted. This is necessary to
// prevent the "Submit" button from being reactivated after the window
// is closed and reopened.
self.hasBeenSubmitted = true;
// Disable input
self.actions.setAbilities( { submit: false } );
// Increase the pending level by 4: one each for the delete, block,
// notify, and all promises.
self.pushPending();
self.pushPending();
self.pushPending();
self.pushPending();
// Set progress labels
self.deleteProgressField.setLabel( 'Deleting "' + title + '"...' );
self.blockProgressField.setLabel( 'Blocking ' + user + '...' );
self.notifyProgressField.setLabel( 'Notifying ' + user + '...' );
self.stackLayout.setItem( self.submitPanel );
// Get options and submit
options = {
watchlist: self.watchlistButtonSelect.findSelectedItem().getData(),
deletesummary: self.deleteSummaryInput.getInput().getValue(),
expiry: self.expiryInput.getInput().getValue(),
template: self.templateInput.getInput().getValue(),
editsummary: self.editSummaryInput.getInput().getValue(),
blocksummary: self.blockSummaryInput.getInput().getValue(),
nocreate: self.nocreateToggleButton.getValue(),
autoblock: self.autoblockToggleButton.getValue(),
noemail: self.noemailToggleButton.getValue(),
nousertalk: self.nousertalkToggleButton.getValue()
};
promises = self.getApiManager().submit( options );
// Update progress
// This involves editing the progress labels and reducing the pending
// level.
function updateProgress( promise, label ) {
promise.done( function () {
label.setLabel( $( '<span>' )
.addClass( 'spamuserpage-success' )
.text( 'Done.' )
);
} );
promise.fail( function ( id, obj ) {
if ( obj && obj.error && obj.error.info ) {
label.setLabel( $( '<span>' )
.addClass( 'spamuserpage-error' )
.text( 'Error: ' + obj.error.info + '.' )
);
} else {
label.setLabel( 'An unknown error occurred.' );
}
} );
promise.always( function () {
self.popPending();
} );
}
updateProgress( promises[ 'delete' ], self.deleteProgressLabel );
updateProgress( promises.block, self.blockProgressLabel );
updateProgress( promises.notify, self.notifyProgressLabel );
// Clean up when everything is done
promises.all.done( function () {
self.openTalkProgressField.setLabel(
'Opening "' +
apiManager.getUserTalkTitle().getPrefixedText() +
'"...'
);
apiManager.openTalkPage();
} );
promises.all.fail( function () {
// Pop pending only on failure, so that it still looks like we're
// loading something while the talk page is being opened.
self.popPending();
} );
};
Dialog.prototype.getReadyProcess = function ( data ) {
// Parent getReadyProcess method
return Dialog.super.prototype.getReadyProcess.call( this, data )
.next( function () {
if ( this.hasBeenSubmitted ) {
// The dialog has already been submitted when it was previously
// opened, so disable the Submit button.
this.actions.setAbilities( { submit: false } );
} else if ( this.getApiManager().getDefault( 'oneclick' ) ) {
this.executeAction( 'submit' );
}
}, this );
};
Dialog.prototype.getActionProcess = function ( action ) {
return Dialog.super.prototype.getActionProcess.call( this, action )
.next( function () {
if ( action === 'submit' ) {
return this.onSubmit();
} else {
return Dialog.super.prototype.getActionProcess.call( this, action );
}
}, this );
};
/**************************************************************************
* Main
**************************************************************************/
function main() {
var portletLink;
var supDialog = new Dialog( { size: 'large' } );
var apiManager = supDialog.getApiManager();
var userName = apiManager.getUserName();
if (
// Don't run in the current user's userspace or on any user talk pages.
userName !== config.wgUserName &&
!(
config.wgNamespaceNumber === 3 &&
config.wgTitle === userName
)
) {
// Load CSS
importStylesheet( "User:Mr. Stradivarius/gadgets/SpamUserPage.css" );
// Set up window manager
var windowManager = new OO.ui.WindowManager();
$( 'body' ).append( windowManager.$element );
windowManager.addWindows( [ supDialog ] );
// Add portlet link
portletLink = apiManager.addPortletLink();
$( portletLink ).click( function ( event ) {
event.preventDefault();
windowManager.openWindow( supDialog );
} );
}
}
main();
} );
// </nowiki>