MediaWiki:Gadget-UserRightsManager.js

From Test Wiki
Revision as of 22:08, 19 February 2022 by Chrs (talk | contribs) (update the gadget to allow selecting which rights to grant + changing the target user)
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
// <nowiki>
// Adapted from [[mhtest:MediaWiki:Gadget-userRightsManagerTW.js]], itself adapted from [[w:User:MusikAnimal/userRightsManager.js]]
(function() {
	if (mw.config.get('wgPageName') != 'Test_Wiki:Request_permissions' && !mw.config.get('wgPageName').includes('URMTW-TESTPAGE-RFP')) {
		return;
	}
	
	var names = {
		'sysop': 'Administrator',
		'bureaucrat': 'Bureaucrat',
		'interface-admin': 'Interface Administrator',
	}
	
	var cannedResponses = {
		'sysop': '{{done}}. ~~~~',
		'bureaucrat': '{{done}}. ~~~~',
		'interface-admin': '{{done}}. ~~~~',
	};
	
	var listKeys = {
		'sysop': 'A',
		'bureaucrat': 'B',
		'interface-admin': 'F'
	}

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

	mw.loader.using(['oojs-ui', 'mediawiki.api'], function() {
		api = new mw.Api();
		$('.assign-permissions-link').on('click', function(e) {
			e.preventDefault();
			permissionText = $(this).parent().parent().next().text().split(':')[1].trim().toLowerCase();
			permission = permissionText.includes('interface') ? 'interface-admin' : (permissionText.includes('crat') ? 'bureaucrat' : 'sysop');
			userName = $(this).siblings().eq(0).text();
			sectionId = $(this).parent().parent().prev().find(".mw-editsection a:not('.mw-editsection-visualeditor')").prop('href').match(/section=(\d+)/)[1];
			showDialog();
		});
	});

	function showDialog() {
		Dialog = function(config) {
			Dialog.super.call(this, config);
		};
		OO.inheritClass(Dialog, OO.ui.ProcessDialog);
		Dialog.static.name = 'user-rights-manager';
		Dialog.static.title = 'Granting rights';
		Dialog.static.actions = [
			{ action: 'submit', label: 'Grant', flags: ['primary', 'progressive'] },
			{ label: 'Cancel', flags: 'safe' }
		];
		Dialog.prototype.getApiManager = function() {
			return this.apiManager;
		};
		Dialog.prototype.getBodyHeight = function() {
			return 393;
		};
		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.groupsSysopInput = new OO.ui.CheckboxInputWidget({
				selected: permissionText.includes('sysop') || (permissionText.includes('admin') && (!permissionText.includes('interface') || permissionText.split('admin').length > 2)) || (!permissionText.includes('interface') && !permissionText.includes('crat')),
			});
			this.groupsBureaucratInput = new OO.ui.CheckboxInputWidget({
				selected: permissionText.includes('crat')
			});
			this.groupsInterfaceAdminInput = new OO.ui.CheckboxInputWidget({
				selected: permissionText.includes('interface') && mw.config.get('wgUserGroups').includes('steward'),
				disabled: !mw.config.get('wgUserGroups').includes('steward')
			});
			this.userNameInput = new OO.ui.TextInputWidget({
				value: userName.replace(/_/g, ' ')
			});
			this.closingRemarksInput = new OO.ui.MultilineTextInputWidget({
				value: cannedResponses[permission],
				rows: 9
			});
			var formElements = [
				new OO.ui.FieldLayout(new OO.ui.Widget({
					content: [
						new OO.ui.FieldsetLayout({
							content: [
								new OO.ui.FieldLayout(this.groupsSysopInput, {label: 'Administrator', align: 'inline'}),
								new OO.ui.FieldLayout(this.groupsBureaucratInput, {label: 'Bureaucrat', align: 'inline'}),
								new OO.ui.FieldLayout(this.groupsInterfaceAdminInput, {label: 'Interface administrator', align: 'inline'}),
							]
						})
					]
				}), {
					label: 'Rights',
				}),
				new OO.ui.FieldLayout(this.closingRemarksInput, {
					label: 'Closing remarks',
				})
			];
			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.updateJsonProgressLabel = new OO.ui.LabelWidget();
			this.updateJsonProgressField = new OO.ui.FieldLayout( this.updateJsonProgressLabel );
			this.markAsDoneProgressLabel = new OO.ui.LabelWidget();
			this.markAsDoneProgressField = new OO.ui.FieldLayout( this.markAsDoneProgressLabel );
			this.stackLayout = new OO.ui.StackLayout( {
				items: [this.editPanel, this.submitPanel],
				padded: true
			} );
			this.$body.append( this.stackLayout.$element );
		};

		Dialog.prototype.onSubmit = function() {
			userName = this.userNameInput.getValue();
			perms = [];
			if(this.groupsSysopInput.isSelected()) perms.push('sysop');
			if(this.groupsBureaucratInput.isSelected()) perms.push('bureaucrat');
			if(this.groupsInterfaceAdminInput.isSelected()) perms.push('interface-admin');
			
			var self = this, promiseCount = 3;

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

			addPromise = function( field, promise ) {
				self.pushPending();
				promise.done(function() {
					field.$field.append( $( '<span>' )
						.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( $( '<span>' )
							.text('Error: ' + obj.error.info)
							.prop('style', 'position:relative; top:0.5em; color: #cc0000; font-weight: bold')
						);
					} else {
						field.$field.append( $( '<span>' )
							.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.updateJsonProgressField.setLabel( 'Updating markadmins.json' );
			self.submitFieldset.addItems( [self.updateJsonProgressField] );
			self.changeRightsProgressField.setLabel( 'Assigning rights...' );
			self.submitFieldset.addItems( [self.changeRightsProgressField] );

			addPromise(
				self.markAsDoneProgressField,
				markAsDone('\n:' + this.closingRemarksInput.getValue())
			).then(function(data) {
				addPromise(
					self.updateJsonProgressField,
					updateJson(data.edit.newrevid)
				);
				addPromise(
					self.changeRightsProgressField,
					assignPermission(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();
				} else {
					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(revId) {
		permaLink = '[[Special:Diff/' + revId + '|Requested]]';
		return api.postWithToken( 'userrights', {
			action: 'userrights',
			format: 'json',
			user: userName.replace(/ /g, '_'),
			add: perms.join('|'),
			reason: '+' + perms.join(', +') + '; ' + permaLink + ' at [[TW:RFP]]',
			expiry: 'infinity',
			tags: 'userRightsManagerTW'
		});
	}
	
	function updateJson(revId) {
		return api.get({
			'action': 'query',
			'titles': 'MediaWiki:Gadget-markadmins.json',
			'prop': 'revisions',
			'rvslots': '*',
			'rvlimit': 1,
			'rvprop': 'content'
		}).then(function(data){
			var newContent = JSON.parse(data['query']['pages'][Object.keys(data['query']['pages'])[0]]['revisions'][0]['slots']['main']['*']);
			for(perm in perms){
				if(!newContent['userSet'][listKeys[perm]].includes(userName.replace(/_/g, ' '))){
					newContent['userSet'][listKeys[perm]].push(userName.replace(/_/g, ' '))
				}
				newContent['userSet'][listKeys[perm]].sort()
			}
			newContent = JSON.stringify(newContent, null, 4)
			return api.postWithToken( 'edit', {
				'action': 'edit',
				'title': 'MediaWiki:Gadget-markadmins.json',
				'text': newContent,
				'tags': 'userRightsManagerTW',
				summary: 'Adding [[User:' + userName + '|' + userName.replace(/_/g, ' ') + ']] to ' + names[permission] + 's ' + '([[Special:Diff/' + revId + '|per request]])'
			})
		});
	}

	function markAsDone(closingRemarks) {
		return api.get({
			'action': 'query', 
			'pageids': mw.config.get('wgArticleId'), 
			'prop': 'revisions', 
			'rvslots': '*', 
			'rvlimit': 1, 
			'rvprop': 'content', 
			'rvsection': sectionId
		}).then(function(data){
			var newContent = data['query']['pages'][mw.config.get('wgArticleId')]['revisions'][0]['slots']['main']['*'] + closingRemarks
			return api.postWithToken( 'edit', {
				'action': 'edit',
				'pageid': mw.config.get('wgArticleId'),
				'section': sectionId,
				'text': newContent,
				'tags': 'userRightsManagerTW',
				summary: '/* User:' + userName + ' */ done'
			})
		});
	}
})();
// </nowiki>