User:SD0001/userRightsManager.js

// forked from user:MusikAnimal/userRightsManager.js for adding decline options (per User_talk:MusikAnimal)

/* To install, add: importScript('User:SD0001/userRightsManager.js'); // User:SD0001/userRightsManager.js to your common.js page

// jshint maxerr: 999

// // Some UI code adapted from User:Mr. Stradivarius/gadgets/Draftify.js (function { if (!/Wikipedia:Requests for permissions\//.test(document.title)) {	return; }

var permissionNames = { 'Account creator': 'accountcreator', 'Autopatrolled': 'autoreviewer', 'Confirmed': 'confirmed', 'Event coordinator': 'eventcoordinator', 'Extended confirmed': 'extendedconfirmed', 'File mover': 'filemover', 'Mass message sender': 'massmessage-sender', 'New page reviewer': 'patroller', 'Page mover': 'extendedmover', 'Pending changes reviewer': 'reviewer', 'Rollback': 'rollbacker', 'Template editor': 'templateeditor' };

var templates = { 'Account creator': 'Account creator granted', 'Autopatrolled': 'Autopatrolledgiven', 'AutoWikiBrowser': '', 'Confirmed': '', 'Event coordinator': 'Event coordinator granted', 'Extended confirmed': '', 'File mover': 'Filemovergiven', 'Mass message sender': 'Mass message sender granted', 'New page reviewer': 'New Page Reviewer granted', 'Page mover': 'Page mover granted', 'Pending changes reviewer': 'Pending changes reviewer granted', 'Rollback': 'Rollback granted 3', 'Template editor': 'Template editor granted' };

var decline_templates = { 'Account creator': 'permission declined notification|Account creator|Wikipedia Accountcreators.svg', 'Autopatrolled': 'permission declined notification|Autopatrolled|Wikipedia Autopatrolled.svg', 'AutoWikiBrowser': '', 'Confirmed': '', 'Event coordinator': 'permission declined notification|Event coordinator|Wikipedia Event coordinator.svg', 'Extended confirmed': '', 'File mover': 'permission declined notification|File mover|Wikipedia File mover.svg', 'Mass message sender': 'permission declined notification|Mass message sender|Wikipedia mass messenger.svg', 'New page reviewer': 'permission declined notification|New page reviewer|Wikipedia New page reviewer.svg', 'Page mover': 'permission declined notification|Page mover|Wikipedia page mover.svg', 'Pending changes reviewer': 'permission declined notification|Pending changes reviewer|Wikipedia Reviewer.svg', 'Rollback': 'permission declined notification|Rollback|Wikipedia Rollbacker.svg', 'Template editor': 'permission declined notification|Template editor|Wikipedia Template editor icon (1).svg' };

var api, permission = mw.config.get('wgTitle').split('/').slice(-1)[0], revisionId = mw.config.get('wgRevisionId'), tagLine = ' (using userRightsManager)', permaLink, userName, dialog;

mw.loader.using(['oojs-ui', 'mediawiki.api', 'mediawiki.widgets.DateInputWidget'], function {	api = new mw.Api;	$('.perm-assign-permissions a').on('click', function(e) { if (permission === 'AutoWikiBrowser') {return true;} e.preventDefault; userName = $(this).parents('.plainlinks').find('a').eq(0).text; showDialog; });	$('.perm-assign-permissions').after( ' (',		$('').html(' decline ').attr('href', '#')			.click(function(e) { e.preventDefault; userName = $(this).parents('.plainlinks').find('a').eq(0).text; showDeclineDialog; }),		')'	); });

function showDialog { var Dialog = function(config) { Dialog.super.call(this, config); };	OO.inheritClass(Dialog, OO.ui.ProcessDialog); Dialog.static.name = 'user-rights-manager'; Dialog.static.title = 'Grant ' + permission + ' to ' + userName; Dialog.static.actions = [ { action: 'submit', label: 'Submit', flags: ['primary', 'constructive'] }, { label: 'Cancel', flags: 'safe' } ];	Dialog.prototype.getApiManager = function { return this.apiManager; };	Dialog.prototype.getBodyHeight = function { return 208; };	Dialog.prototype.initialize = function { Dialog.super.prototype.initialize.call(this); this.editFieldset = new OO.ui.FieldsetLayout({			classes: ['container']		}); this.editPanel = new OO.ui.PanelLayout({			expanded: false		}); this.editPanel.$element.append(this.editFieldset.$element); this.rightsChangeSummaryInput = new OO.ui.TextInputWidget({			value: 'Requested at WP:PERM'		}); this.expiryInput = new mw.widgets.DateInputWidget({			$overlay: $('.oo-ui-window')		}); this.closingRemarksInput = new OO.ui.TextInputWidget({			value: '✅ ~'		}); this.watchTalkPageCheckbox = new OO.ui.CheckboxInputWidget({			selected: false		}); var formElements = [ new OO.ui.FieldLayout(this.rightsChangeSummaryInput, {				label: 'Summary'			}), new OO.ui.FieldLayout(this.expiryInput, {				label: 'Expiry (optional)'			}), new OO.ui.FieldLayout(this.closingRemarksInput, {				label: 'Closing remarks'			}) ];		if (templates[permission]) { formElements.push(				new OO.ui.FieldLayout(this.watchTalkPageCheckbox, { label: 'Watch user talk page' })			);		}		this.editFieldset.addItems(formElements); 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.changeRightsProgressLabel = new OO.ui.LabelWidget; this.changeRightsProgressField = new OO.ui.FieldLayout(this.changeRightsProgressLabel); this.markAsDoneProgressLabel = new OO.ui.LabelWidget; this.markAsDoneProgressField = new OO.ui.FieldLayout(this.markAsDoneProgressLabel); this.issueTemplateProgressLabel = new OO.ui.LabelWidget; this.issueTemplateProgressField = new OO.ui.FieldLayout(this.issueTemplateProgressLabel); this.stackLayout = new OO.ui.StackLayout({			items: [this.editPanel, this.submitPanel],			padded: true		}); this.$body.append(this.stackLayout.$element); $('.mw-widget-dateInputWidget').css('width', '100%'); };

Dialog.prototype.onSubmit = function { var self = this, promiseCount = templates[permission] ? 3 : 2;

self.actions.setAbilities({ submit: false });

addPromise = function(field, promise) { self.pushPending; promise.done(function {				field.$field.append($(' ') .text('Complete!') .prop('style', 'position:relative; top:0.5em; color: #009000; font-weight: bold') );			}).fail(function(obj) {				if (obj && obj.error && obj.error.info) {					field.$field.append($(' ') .text('Error: ' + obj.error.info) .prop('style', 'position:relative; top:0.5em; color: #cc0000; font-weight: bold') );				} else {					field.$field.append($(' ') .text('An unknown error occurred.') .prop('style', 'position:relative; top:0.5em; color: #cc0000; font-weight: bold') );				}			}).always(function {				promiseCount--; // FIXME: maybe we could use a self.isPending or something				self.popPending;

if (promiseCount === 0) { setTimeout(function {						location.reload(true);					}, 1000); }			});

return promise; };

self.markAsDoneProgressField.setLabel('Marking request as done...'); self.submitFieldset.addItems([self.markAsDoneProgressField]); self.changeRightsProgressField.setLabel('Assigning rights...'); self.submitFieldset.addItems([self.changeRightsProgressField]);

if (templates[permission]) { self.issueTemplateProgressField.setLabel('Issuing template...'); self.submitFieldset.addItems([self.issueTemplateProgressField]); }

addPromise(			self.markAsDoneProgressField,			editPERMpage('done', '\n::' + this.closingRemarksInput.getValue)		).then(function(data) {			addPromise( self.changeRightsProgressField, assignPermission(					this.rightsChangeSummaryInput.getValue,					data.edit.newrevid,					this.expiryInput.getValue				) ).then(function { // silently add user to MMS list if (permission === 'New page reviewer') {addToMMSList;}

if (templates[permission]) { addPromise(						self.issueTemplateProgressField,						issueTemplate(this.watchTalkPageCheckbox.isSelected, this.expiryInput.getValue)					); }			}.bind(this));		}.bind(this));

self.stackLayout.setItem(self.submitPanel); };

Dialog.prototype.getActionProcess = function(action) { return Dialog.super.prototype.getActionProcess.call(this, action).next(function {				if ( action === 'submit' ) {					return this.onSubmit;				}					return Dialog.super.prototype.getActionProcess.call( this, action );

}, this);	};

dialog = new Dialog({		size: 'medium'	});

var windowManager = new OO.ui.WindowManager; $('body').append(windowManager.$element); windowManager.addWindows([dialog]); windowManager.openWindow(dialog); }

function showDeclineDialog { var Dialog = function(config) { Dialog.super.call(this, config); };	OO.inheritClass(Dialog, OO.ui.ProcessDialog); Dialog.static.name = 'user-rights-manager'; Dialog.static.title = 'Decline ' + permission + ' to ' + userName; Dialog.static.actions = [ { action: 'submit', label: 'Submit', flags: ['primary', 'constructive'] }, { label: 'Cancel', flags: 'safe' } ];	Dialog.prototype.getApiManager = function { return this.apiManager; };	Dialog.prototype.getBodyHeight = function { return 208; };	Dialog.prototype.initialize = function { Dialog.super.prototype.initialize.call(this); this.editFieldset = new OO.ui.FieldsetLayout({			classes: ['container']		}); this.editPanel = new OO.ui.PanelLayout({			expanded: false		}); this.editPanel.$element.append(this.editFieldset.$element);

this.closingRemarksInput = new OO.ui.TextInputWidget({			value: '❌ ~'		}); this.notifyUserCheckbox = new OO.ui.CheckboxInputWidget({			selected: true		}); this.watchTalkPageCheckbox = new OO.ui.CheckboxInputWidget({			selected: false		}); var formElements = [ new OO.ui.FieldLayout(this.closingRemarksInput, {				label: 'Closing remarks'			}) ];		if (decline_templates[permission]) { formElements.push(				new OO.ui.FieldLayout(this.notifyUserCheckbox, { label: 'Notify user on talk page' })			);			formElements.push(				new OO.ui.FieldLayout(this.watchTalkPageCheckbox, { label: 'Watch user talk page' })			);		}		this.editFieldset.addItems(formElements); 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.markAsDoneProgressLabel = new OO.ui.LabelWidget; this.markAsDoneProgressField = new OO.ui.FieldLayout(this.markAsDoneProgressLabel); this.issueTemplateProgressLabel = new OO.ui.LabelWidget; this.issueTemplateProgressField = new OO.ui.FieldLayout(this.issueTemplateProgressLabel); this.stackLayout = new OO.ui.StackLayout({			items: [this.editPanel, this.submitPanel],			padded: true		}); this.$body.append(this.stackLayout.$element); };

Dialog.prototype.onSubmit = function { var self = this, promiseCount = decline_templates[permission] ? 2 : 1;

self.actions.setAbilities({ submit: false });

addPromise = function(field, promise) { self.pushPending; promise.done(function {				field.$field.append($(' ') .text('Complete!') .prop('style', 'position:relative; top:0.5em; color: #009000; font-weight: bold') );			}).fail(function(obj) {				if (obj && obj.error && obj.error.info) {					field.$field.append($(' ') .text('Error: ' + obj.error.info) .prop('style', 'position:relative; top:0.5em; color: #cc0000; font-weight: bold') );				} else {					field.$field.append($(' ') .text('An unknown error occurred.') .prop('style', 'position:relative; top:0.5em; color: #cc0000; font-weight: bold') );				}			}).always(function {				promiseCount--; // FIXME: maybe we could use a self.isPending or something				self.popPending;

if (promiseCount === 0) { setTimeout(function {						location.reload(true);					}, 1000); }			});

return promise; };

self.markAsDoneProgressField.setLabel('Marking request as not done...'); self.submitFieldset.addItems([self.markAsDoneProgressField]); // self.changeRightsProgressField.setLabel( 'Assigning rights...' ); // self.submitFieldset.addItems( [self.changeRightsProgressField] );

if (!!decline_templates[permission] && this.notifyUserCheckbox.getValue) { self.issueTemplateProgressField.setLabel('Issuing template...'); self.submitFieldset.addItems([self.issueTemplateProgressField]); }

addPromise(			self.markAsDoneProgressField,			editPERMpage('not done', '\n::' + this.closingRemarksInput.getValue)		).then(function(data) {			if (!!decline_templates[permission] && this.notifyUserCheckbox.isSelected) {				addPromise( self.issueTemplateProgressField, issueDeclineTemplate(this.watchTalkPageCheckbox.isSelected, data.edit.newrevid) );			}		}.bind(this));

self.stackLayout.setItem(self.submitPanel); };

Dialog.prototype.getActionProcess = function(action) { return Dialog.super.prototype.getActionProcess.call(this, action).next(function {				if ( action === 'submit' ) {					return this.onSubmit;				}					return Dialog.super.prototype.getActionProcess.call( this, action );

}, this);	};

dialog = new Dialog({		size: 'medium'	});

var windowManager = new OO.ui.WindowManager; $('body').append(windowManager.$element); windowManager.addWindows([dialog]); windowManager.openWindow(dialog); }

function assignPermission(summary, revId, expiry) { permaLink = 'permalink'; return api.postWithToken('userrights', {		action: 'userrights',		format: 'json',		user: userName.replace(/ /g, '_'),		add: permissionNames[permission],		reason: '+' + permissionNames[permission] + '; ' + summary + '; ' + permaLink + tagLine,		expiry: expiry === '' ? 'infinity' : expiry	}); }

// `status` is either "done" or "not done" function editPERMpage(status, closingRemarks) { var sectionNode = document.getElementById('User:' + userName.replace(/"/g, '.22').replace(/ /g, '_')),		sectionNumber = $(sectionNode).siblings('.mw-editsection').find("a:not('.mw-editsection-visualeditor')").prop('href').match(/section=(\d+)/)[1];	return api.postWithToken('csrf', {		format: 'json',		action: 'edit',		title: mw.config.get('wgPageName'),		section: sectionNumber,		summary: '/* User:' + userName + ' */ ' + status + tagLine,		appendtext: closingRemarks	}); }

function issueDeclineTemplate(watch, revId) { permaLink = 'permalink'; var talkPage = 'User talk:' + userName.replace(/ /g, '_'); return api.postWithToken('csrf', {		format: 'json',		action: 'edit',		title: talkPage,		section: 'new',		summary: 'Your request for ' + permission + ' was declined per ' + permaLink + tagLine,		text: '',		sectiontitle: 'Your request for ' + permission[0].toLowerCase + permission.slice(1) + ' right',		watchlist: watch ? 'watch' : 'unwatch'	}); }

function issueTemplate(watch, expiry) { var talkPage = 'User talk:' + userName.replace(/ /g, '_'); return api.postWithToken('csrf', {		format: 'json',		action: 'edit',		title: talkPage,		section: 'new',		summary: permission + ' granted per ' + permaLink + tagLine,		text: '',		sectiontitle: permission + ' granted',		watchlist: watch ? 'watch' : 'unwatch'	}); }

function addToMMSList { api.postWithToken('csrf', {		format: 'json',		action: 'editmassmessagelist',		spamlist: 'Wikipedia:New pages patrol/Reviewers/Newsletter list',		add: 'User talk:' + userName	}); } }); //