User:TheAstorPastor/common.js

From Test Wiki
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)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
//importScript('User:Joepayne/grantBureaucrat.js'); // Backlink: [[User:Joepayne/grantBureaucrat.js]]
importScript('User:Joepayne/stripRights.js'); // Backlink: [[User:Joepayne/stripRights.js]]
//importScript('User:MacFan4000/grantAdmin.js'); // Backlink: [[User:MacFan4000/grantAdmin.js]]
//importScript('User:Kiteretsu/js/all-in-one.js'); // Backlink: [[User:Kiteretsu/js/all-in-one.js]]
//importScript('User:DodoMan/chatbot.js'); // Backlink: [[User:DodoMan/chatbot.js]]
importScript('User:JJBullet/findInactiveSysops.js'); // Backlink: [[User:JJBullet/findInactiveSysops.js]]
importScript('User:Harvici/find-archived-section.js'); // Backlink: [[User:Harvici/find-archived-section.js]]
importScript('User:Harvici/UTCLiveClock.js'); // Backlink: [[User:Harvici/UTCLiveClock.js]]
importScript('User:Harvici/Twinkle-preferences-toolbar.js'); // Backlink: [[User:Harvici/Twinkle-preferences-toolbar.js]]
importScript('User:Kiteretsu/rollbackSum.js'); // Backlink: [[User:Kiteretsu/rollbackSum.js]]
importScript('User:Kiteretsu/DiscussionCloser.js'); // Backlink: [[User:Kiteretsu/DiscussionCloser.js]]
importScript('User:TheAstorPastor/VisualEditorEverywhere.js'); // Backlink: [[User:TheAstorPastor/VisualEditorEverywhere.js]]

// User Rights Manager for Test Wiki's Request for Permissions page
// Script allows sysops to easily assign user rights
// Licensed under CC-BY-SA 4.0

(function() {
	// Only run on the Request for Permissions page or test pages
	if (mw.config.get('wgPageName') !== 'Test_Wiki:Request_for_permissions' && !mw.config.get('wgPageName').includes('URMTW-TESTPAGE-RFP')) {
		return;
	}
	
	// Check if user has appropriate permissions
	if (!mw.config.get('wgUserGroups').includes('sysop') && !mw.config.get('wgUserGroups').includes('bureaucrat')) {
		console.log('User Rights Manager: You need to be a sysop to use this tool.');
		return;
	}

	// Define rights mapping and their corresponding display names
	var rightsNames = {
		'sysop': 'Administrator',
		'bureaucrat': 'Bureaucrat',
		'interface-admin': 'Interface administrator',
		'non-stewardsuppress': 'Suppressor',
		'abusefilter-admin': 'Abuse filter administrator'
	};
	
	// Define canned responses for each right
	var cannedResponses = {
		'sysop': '{{administrator granted}} [[User:TheAstorPastor|<span style="font-family:Segoe print; color:#8B0000; text-shadow:gray 0.2em 0.2em 0.4em;">The AP </span>]] ([[User talk:TheAstorPastor|<span style="font-family:Segoe print; color:#AA336A">''talk''</span>]]) 13:48, 11 April 2025 (UTC)',
		'bureaucrat': '{{bureaucrat granted}} [[User:TheAstorPastor|<span style="font-family:Segoe print; color:#8B0000; text-shadow:gray 0.2em 0.2em 0.4em;">The AP </span>]] ([[User talk:TheAstorPastor|<span style="font-family:Segoe print; color:#AA336A">''talk''</span>]]) 13:48, 11 April 2025 (UTC)',
		'interface-admin': '{{interface administrator granted}} [[User:TheAstorPastor|<span style="font-family:Segoe print; color:#8B0000; text-shadow:gray 0.2em 0.2em 0.4em;">The AP </span>]] ([[User talk:TheAstorPastor|<span style="font-family:Segoe print; color:#AA336A">''talk''</span>]]) 13:48, 11 April 2025 (UTC)',
		'non-stewardsuppress': '{{done}}. [[User:TheAstorPastor|<span style="font-family:Segoe print; color:#8B0000; text-shadow:gray 0.2em 0.2em 0.4em;">The AP </span>]] ([[User talk:TheAstorPastor|<span style="font-family:Segoe print; color:#AA336A">''talk''</span>]]) 13:48, 11 April 2025 (UTC)',
		'abusefilter-admin': '{{done}}. [[User:TheAstorPastor|<span style="font-family:Segoe print; color:#8B0000; text-shadow:gray 0.2em 0.2em 0.4em;">The AP </span>]] ([[User talk:TheAstorPastor|<span style="font-family:Segoe print; color:#AA336A">''talk''</span>]]) 13:48, 11 April 2025 (UTC)'
	};

	// Variables for API and UI management
	var api,
		permission,
		selectedRights = [],
		revisionId = mw.config.get('wgRevisionId'),
		permaLink, 
		userName, 
		sectionId, 
		dialog;

	// Add "Assign Rights" button to each request section
	function addAssignButtons() {
		$('.mw-headline').each(function() {
			if ($(this).attr('id') && $(this).text().includes('=')) {
				// Get the username from the headline
				var requestedUser = $(this).text().trim().split('==')[1].trim();
				
				// Find the section with requested rights
				var requestSection = $(this).parent().next();
				
				// Check if there's already a response (to avoid duplicating buttons)
				if (requestSection.text().includes('{{') || requestSection.text().includes('granted') || requestSection.find('.assign-rights-button').length) {
					return;
				}
				
				// Create "Assign Rights" button
				var assignButton = $('<button>')
					.addClass('assign-rights-button oo-ui-widget oo-ui-widget-enabled oo-ui-buttonElement oo-ui-buttonElement-framed oo-ui-labelElement')
					.css({
						'margin': '5px',
						'padding': '5px 10px',
						'background-color': '#36c',
						'color': '#fff',
						'border': 'none',
						'border-radius': '2px',
						'cursor': 'pointer'
					})
					.text('Assign Rights')
					.on('click', function(e) {
						e.preventDefault();
						
						// Find the requested right in the section text
						var sectionText = requestSection.text().toLowerCase();
						var requestedRight = '';
						
						if (sectionText.includes('administrator')) {
							requestedRight = 'sysop';
						} else if (sectionText.includes('bureaucrat')) {
							requestedRight = 'bureaucrat';
						} else if (sectionText.includes('interface administrator')) {
							requestedRight = 'interface-admin';
						} else if (sectionText.includes('suppressor')) {
							requestedRight = 'non-stewardsuppress';
						} else if (sectionText.includes('abuse filter')) {
							requestedRight = 'abusefilter-admin';
						}
						
						// Set global variables
						permission = requestedRight;
						userName = requestedUser;
						sectionId = $(this).closest('h2').find('.mw-editsection a:not(".mw-editsection-visualeditor")').prop('href').match(/section=(\d+)/)[1];
						
						// Show the dialog
						showDialog();
					});
				
				// Insert button after the headline
				$(this).append(assignButton);
			}
		});
	}

	// Load required modules and set up the API
	mw.loader.using(['oojs-ui', 'mediawiki.api'], function() {
		api = new mw.Api();
		
		// Add buttons after the page has loaded
		$(document).ready(function() {
			addAssignButtons();
		});
	});

	// Create and show the dialog
	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 = 'User Rights Manager';
		Dialog.static.actions = [
			{ action: 'submit', label: 'Grant Rights', flags: ['primary', 'progressive'] },
			{ label: 'Cancel', flags: 'safe' }
		];
		
		Dialog.prototype.getApiManager = function() {
			return this.apiManager;
		};
		
		Dialog.prototype.getBodyHeight = function() {
			return 400;
		};
		
		Dialog.prototype.initialize = function() {
			Dialog.super.prototype.initialize.call(this);
			
			// Create main panels and fieldsets
			this.editPanel = new OO.ui.PanelLayout({
				expanded: false,
				padded: true
			});
			
			this.editFieldset = new OO.ui.FieldsetLayout({
				classes: ['container']
			});
			
			this.editPanel.$element.append(this.editFieldset.$element);
			
			// Create checkbox inputs for each right
			this.rightInputs = {};
			var detectedRight = permission || 'sysop';
			
			for (var right in rightsNames) {
				this.rightInputs[right] = new OO.ui.CheckboxInputWidget({
					selected: right === detectedRight,
					disabled: (right === 'interface-admin' || right === 'non-stewardsuppress' || right === 'abusefilter-admin') && 
							  !mw.config.get('wgUserGroups').includes('steward')
				});
			}
			
			// Username input
			this.userNameInput = new OO.ui.TextInputWidget({
				value: userName ? userName.replace(/_/g, ' ') : ''
			});
			
			// Closing remarks input
			this.closingRemarksInput = new OO.ui.MultilineTextInputWidget({
				value: cannedResponses[detectedRight] || '',
				rows: 5
			});
			
			// Create checkbox group for rights
			var rightsFieldset = new OO.ui.FieldsetLayout({
				label: 'Select rights to grant'
			});
			
			for (var right in this.rightInputs) {
				rightsFieldset.addItems([
					new OO.ui.FieldLayout(this.rightInputs[right], {
						label: rightsNames[right],
						align: 'inline'
					})
				]);
			}
			
			// Add all form elements
			this.editFieldset.addItems([
				new OO.ui.FieldLayout(rightsFieldset, {
					label: 'Rights',
					align: 'top'
				}),
				new OO.ui.FieldLayout(this.userNameInput, {
					label: 'Target user',
					align: 'top'
				}),
				new OO.ui.FieldLayout(this.closingRemarksInput, {
					label: 'Closing remarks',
					align: 'top'
				})
			]);
			
			// Create submit panel for progress display
			this.submitPanel = new OO.ui.PanelLayout({
				expanded: false,
				padded: true
			});
			
			this.submitFieldset = new OO.ui.FieldsetLayout({
				classes: ['container']
			});
			
			this.submitPanel.$element.append(this.submitFieldset.$element);
			
			// Create progress labels
			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);
			
			// Create stack layout to switch between panels
			this.stackLayout = new OO.ui.StackLayout({
				items: [this.editPanel, this.submitPanel],
				padded: true
			});
			
			this.$body.append(this.stackLayout.$element);
			
			// Listen for right checkbox changes to update the closing remarks
			for (var right in this.rightInputs) {
				this.rightInputs[right].on('change', this.updateClosingRemarks.bind(this));
			}
		};
		
		// Update closing remarks based on selected rights
		Dialog.prototype.updateClosingRemarks = function() {
			var selectedRight = '';
			
			// Find the first selected right to use for canned response
			for (var right in this.rightInputs) {
				if (this.rightInputs[right].isSelected()) {
					selectedRight = right;
					break;
				}
			}
			
			if (selectedRight && cannedResponses[selectedRight]) {
				this.closingRemarksInput.setValue(cannedResponses[selectedRight]);
			}
		};
		
		// Handle dialog submission
		Dialog.prototype.onSubmit = function() {
			// Get username and selected rights
			userName = this.userNameInput.getValue();
			selectedRights = [];
			
			for (var right in this.rightInputs) {
				if (this.rightInputs[right].isSelected()) {
					selectedRights.push(right);
				}
			}
			
			if (selectedRights.length === 0) {
				alert('Please select at least one right to grant.');
				return;
			}
			
			var self = this, 
				promiseCount = 2;
			
			// Disable submit button
			self.actions.setAbilities({ submit: false });
			
			// Helper function to handle promises
			function addPromise(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--;
					self.popPending();
					
					if (promiseCount === 0) {
						setTimeout(function() {
							location.reload(true);
						}, 1000);
					}
				});
				
				return promise;
			}
			
			// Add progress fields to the submit panel
			self.markAsDoneProgressField.setLabel('Marking request as done...');
			self.submitFieldset.addItems([self.markAsDoneProgressField]);
			
			self.changeRightsProgressField.setLabel('Assigning rights...');
			self.submitFieldset.addItems([self.changeRightsProgressField]);
			
			// Process the request
			addPromise(
				self.markAsDoneProgressField,
				markAsDone('\n:' + this.closingRemarksInput.getValue())
			).then(function(data) {
				addPromise(
					self.changeRightsProgressField,
					assignPermission(data.edit.newrevid)
				);
			}.bind(this));
			
			// Switch to the submit panel
			self.stackLayout.setItem(self.submitPanel);
		};
		
		// Process dialog actions
		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);
		};
		
		// Create and open the dialog
		dialog = new Dialog({
			size: 'medium'
		});
		
		var windowManager = new OO.ui.WindowManager();
		$('body').append(windowManager.$element);
		windowManager.addWindows([dialog]);
		windowManager.openWindow(dialog);
	}
	
	// Function to assign the selected permissions
	function assignPermission(revId) {
		permaLink = '[[Special:Diff/' + revId + '|Requested]]';
		
		return api.postWithToken('userrights', {
			action: 'userrights',
			format: 'json',
			user: userName.replace(/ /g, '_'),
			add: selectedRights.join('|'),
			reason: '+' + selectedRights.join(', +') + '; ' + permaLink + ' at [[TW:RfP]]',
			expiry: 'infinity',
			tags: 'userRightsManager'
		});
	}
	
	// Function to mark the request as done
	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': 'userRightsManager',
				'summary': '/* User:' + userName + ' */ done'
			});
		});
	}
})();