diff --git a/AUTHORS b/AUTHORS index 74d9136..04927e6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -16,3 +16,4 @@ divdavem (https://github.com/divdavem) maxkueng (https://github.com/maxkueng) stdob (https://github.com/stdob) Technical-13 (https://github.com/Technical-13) +Andrew Lyons (https://github.com/andrewnicols) diff --git a/README.md b/README.md index ec4e378..6cf0b1c 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,22 @@ Purge a given list of articles (titles or page IDs can be provided) - [read more > By providing `Category:Foo` as `titles` argument you can purge all pages in a given category (available since [MW 1.21](https://github.com/wikimedia/mediawiki/commit/62216932c197f1c248ca2d95bc230f87a79ccd71)) +### bot.protect(title, protections, options, callback) + +Protect a page (A title or page ID can be provided) - [read more](https://www.mediawiki.org/wiki/API:Protect) + +The `protections` value is an Array of protection information in the format: + +``` +{ + action: string, + level?: string = 'all', + expiry?: string | number = 'never' +} +``` + +Calls to the Protect endpoint are not additive. Each call must include a list of _all_ intended protections, including any already in place. Each call will _replace_ all existing protections. + ### bot.sendEmail(username, subject, text, callback) Send an email to an user - [read more](http://www.mediawiki.org/wiki/API:Email) diff --git a/examples/protect.js b/examples/protect.js new file mode 100755 index 0000000..dc8944b --- /dev/null +++ b/examples/protect.js @@ -0,0 +1,26 @@ +#!/usr/bin/env node +/** + * Example script getting current user information + * + * @see http://www.mediawiki.org/wiki/API:Protect + */ +'use strict'; + +const Bot = require( '..' ); +const client = new Bot( 'examples/config.js' ); + +client.logIn( function () { + // Protections takes an array so allow multiple protections to be configured. + const protections = []; + + // Protect the page from edits. + protections.push( { + type: 'edit', + level: 'sysop', + expiry: 'never' + } ); + + client.protect( 'Albert_Einstein', protections, ( err, data ) => { + console.log( JSON.stringify( data, null, '\t' ) ); + } ); +} ); diff --git a/lib/bot.js b/lib/bot.js index 4085f6f..4e15028 100644 --- a/lib/bot.js +++ b/lib/bot.js @@ -711,6 +711,100 @@ Bot.prototype = { } ); }, + protect( title, protections, options, callback ) { + // @see https://www.mediawiki.org/wiki/API:Protect + if ( this.dryRun ) { + callback( new Error( 'In dry-run mode' ) ); + return; + } + + if ( typeof options === 'function' ) { + // This is the callback; options was nonexistent. + callback = options; + options = {}; + } + + if ( !options ) { + options = {}; + } + + const params = { + action: 'protect' + }; + + if ( typeof title === 'number' ) { + params.pageid = title; + } else { + params.title = title; + } + + const formattedProtections = []; + const expiries = []; + let failed = false; + Array.from( protections ).forEach( ( protection ) => { + if ( !protection.type ) { + callback( new Error( 'Invalid protection. An action type must be specified.' ) ); + failed = true; + return; + } + + const level = protection.level ? protection.level : 'all'; + formattedProtections.push( `${protection.type}=${level}` ); + + if ( protection.expiry ) { + expiries.push( protection.expiry ); + } else { + // If no expiry was specified, then set the expiry to never. + expiries.push( 'never' ); + } + } ); + + if ( failed ) { + return; + } + + params.protections = formattedProtections.join( '|' ); + params.expiry = expiries.join( '|' ); + + if ( options.reason ) { + params.reason = options.reason; + } + + if ( options.tags ) { + if ( Array.isArray( options.tags ) ) { + params.tags = options.tags.join( '|' ); + } else if ( typeof options.tags === 'string' ) { + params.tags = options.tags; + } + } + + if ( options.cascade ) { + params.cascade = options.cascade ? 1 : 1; + } + + if ( options.watchlist && typeof options.watchlist === 'string' ) { + params.watchlist = options.watchlist; + } + + // Params have been generated. Now fetch the csrf token and call the API. + this.getToken( title, 'csrf', ( err, token ) => { + if ( err ) { + callback( err ); + return; + } + + params.token = token; + + this.api.call( params, ( _err, data ) => { + if ( !_err && data.title && data.protections ) { + callback( null, data ); + } else { + callback( _err ); + } + }, 'POST' ); + } ); + }, + purge( titles, callback ) { // @see https://www.mediawiki.org/wiki/API:Purge const params = { diff --git a/test/mediawiki-api-test.js b/test/mediawiki-api-test.js index ff4f997..7179845 100644 --- a/test/mediawiki-api-test.js +++ b/test/mediawiki-api-test.js @@ -155,5 +155,32 @@ vows.describe( 'Mediawiki API' ).addBatch( { assert.isTrue( firstItem.ns === 0 ); assert.isTrue( firstItem.title.includes( 'Albert Einstein' ) ); } + }, + 'protect()': { + 'Command cannot run in dry-run mode.': { + topic: function () { + client.dryRun = true; + client.protect( + ARTICLE, + [ { type: 'edit', level: 'sysop' } ], + this.callback + ); + }, + 'Command not available in dry-run mode': function ( e, res ) { + assert.isTrue( res !== null ); + } + }, + 'When no action type is provided, no error is thrown': { + topic: function () { + client.protect( + ARTICLE, + [ { level: 'all' } ], + this.callback + ); + }, + 'Missing action throws an Error': function ( e, res ) { + assert.isTrue( res !== null ); + } + } } } ).export( module );