跳转到内容

User:Hamish/userRightsManager.js

维基百科,自由的百科全书
注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google ChromeFirefoxMicrosoft EdgeSafari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。
/* global Morebits */
/* eslint-disable no-extra-boolean-cast */
// <nowiki>
// Some UI code adapted from [[User:Mr. Stradivarius/gadgets/Draftify.js]]
// Adapted from https://en.wikipedia.org/wiki/User:MusikAnimal/userRightsManager.js 
//				https://zh.wikipedia.org/wiki/User:Xiplus/js/userRightsManager.js

(function() {
	var pagePermissions = {
		'Wikipedia:權限申請/申請IP封禁豁免權': 'ipblock-exempt',
		'Wikipedia:權限申請/申請巡查權': 'patroller',
		'Wikipedia:權限申請/申請回退權': 'rollbacker',
		'Wikipedia:權限申請/申請巡查豁免權': 'autoreviewer',
		'Wikipedia:權限申請/申請確認用戶權': 'confirmed',
		'Wikipedia:權限申請/申請大量訊息發送權': 'massmessage-sender',
		'Wikipedia:權限申請/申請大量帳號建立權': 'accountcreator',
		'Wikipedia:權限申請/申請檔案移動權': 'filemover',
		'Wikipedia:權限申請/申請跨維基導入權': 'transwiki',
		'Wikipedia:權限申請/申請模板編輯權': 'templateeditor',
        'Wikipedia:權限申請/申請過濾器助理權限': 'abusefilter-helper',
        'Wikipedia:權限申請/申請活動組織權': 'event-organizer',
        'Wikipedia:權限申請/申請IP封鎖豁免授予權': 'ipblock-exempt-grantor'

	}

	var pageName = mw.config.get('wgPageName');
	var permission = pagePermissions[pageName];

	if (!permission) {
		return;
	}
	if (permission === 'accountcreator' && mw.config.get('wgUserGroups').indexOf('bureaucrat') === -1) {
		return;
	}

	var templates = {
		'ipblock-exempt': 'Ipexemptgranted',
		'patroller': 'Patrolgranted',
		'rollbacker': 'Rollbackgranted',
		'autoreviewer': 'Autopatrolgranted',
		'massmessage-sender': 'MMSgranted',
		'templateeditor': 'Template editor granted',
        'transwiki': 'Importer granted',
        'filemover': 'Filemovergranted',
        'abusefilter-helper': 'Edit filter helper granted',
	};

	var api,
		tagLine = '(使用[[User:Hamish/userRightsManager.js|userRightsManager]])',
		permaLink, userName, dialog, permissionNames;

	mw.loader.using(['oojs-ui', 'mediawiki.api', 'mediawiki.widgets.SelectWithInputWidget', 'mediawiki.widgets.expiry', 'ext.gadget.morebits', 'ext.gadget.HanAssist'], function() {
        var conv = require('ext.gadget.HanAssist').conv;
        
		permissionNames = {
            'ipblock-exempt': conv({ hans: 'IP封禁豁免', hant: 'IP封鎖例外' }),
            'patroller': conv({ hans: '巡查员', hant: '巡查員' }),
            'rollbacker': conv({ hans: '回退员', hant: '回退員' }),
            'confirmed': conv({ hans: '已确认的用户', hant: '已確認的使用者' }),
            'massmessage-sender': conv({ hans: '大量消息发送者', hant: '大量訊息傳送者' }),
            'accountcreator': conv({ hans: '大量账户创建者', hant: '大量帳號建立者' }),
            'filemover': conv({ hans: '文件移动员', hant: '檔案移動員' }),
            'transwiki': conv({ hans: '跨维基导入者', hant: '跨維基匯入者' }),
            'templateeditor': conv({ hans: '模板编辑员', hant: '模板編輯員' }),
			'autoreviewer': '巡查豁免者',
            'abusefilter-helper': conv({ hans: '过滤器助理', hant: '過濾器助理' }),
            'event-organizer': conv({ hans: '活动组织者', hant: '活動組織者' }),
            'ipblock-exempt-grantor': conv({ hans: 'IP封禁豁免授予者', hant: 'IP封鎖例外授予者' })
		};

		api = new mw.Api();
		$('.perm-assign-permissions a').on('click', function(e) {
			if (permission === 'AutoWikiBrowser') return true;
			e.preventDefault();
			userName = mw.util.getParamValue('user', $(this).attr('href'));
			showDialog();
		});
	});

	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 = '授予' + permissionNames[permission] + conv({ hans: '给', hant: '給' }) + userName;
		Dialog.static.actions = [
			{ action: 'submit', label: conv({ hans: '授权', hant: '授權' }), flags: ['primary', 'progressive'] },
			{ label: '取消', flags: 'safe' },
		];
		Dialog.prototype.getApiManager = function() {
			return this.apiManager;
		};
		Dialog.prototype.getBodyHeight = function() {
			return 255;
		};
		Dialog.prototype.initialize = function() {
			Dialog.super.prototype.initialize.call(this);
			this.editPanel = new OO.ui.PanelLayout({
				expanded: false,
			});

			var rightLogWapper = $('<span>');
			var url = mw.util.getUrl('Special:Log/rights', { type: 'rights', page: 'User:' + userName });
			$('<a>').text(conv({ hans: '最近权限日志', hant: '最近權限日誌' })).attr({ 'href': url, 'target': '_blank' }).appendTo(rightLogWapper);
			rightLogWapper.append(':');
			var rightLogText = $('<span>').text(conv({ hans: '获取中', hant: '取得中' })).appendTo(rightLogWapper);
			this.rightLog = new OO.ui.LabelWidget({
				label: rightLogWapper,
			});
			this.editPanel.$element.append(this.rightLog.$element);

			api.get({
				action: 'query',
				format: 'json',
				list: 'logevents',
				leaction: 'rights/rights',
				letitle: 'User:' + userName,
				lelimit: '1',
			}).done(function(data) {
				var logs = data.query.logevents;
				if (logs.length === 0) {
					rightLogText.text(conv({ hans: '没有任何日志', hant: '沒有任何日誌' }));
				} else {
					var timestamp = new Morebits.date(logs[0].timestamp).calendar();
					var rights = logs[0].params.newgroups.join('、') || conv({ hans: '(无)', hant: '(無)' });
					rightLogText.text(timestamp + ' ' + logs[0].user + conv({ hans: '将用户组改为', hant: '將使用者群組改為' }) + rights);
				}
			});

			this.editFieldset = new OO.ui.FieldsetLayout({
				classes: ['container'],
			});
			this.editPanel.$element.append(this.editFieldset.$element);

			this.rightsChangeSummaryInput = new OO.ui.TextInputWidget({
				value: '',
				placeholder: '可留空',
			});
			this.expiryInput = new mw.widgets.ExpiryWidget({
				$overlay: $('.oo-ui-window'),
				RelativeInputClass: mw.widgets.SelectWithInputWidget,
				relativeInput: {
					or: true,
					dropdowninput: {
						options: [
							{ data: '1 day', label: '1天' },
							{ data: '1 week', label: conv({ hans: '1周', hant: '1週' })},
							{ data: '1 month', label: conv({ hans: '1个月', hant: '1個月' })},
							{ data: '3 months', label: conv({ hans: '3个月', hant: '3個月' })},
							{ data: '6 months', label: conv({ hans: '6个月', hant: '6個月' })},
							{ data: '1 year', label: '1年' },
							{ data: 'infinite', label: conv({ hans: '没有期限', hant: '沒有期限' })},
							{ data: 'other', label: conv({ hans: '其他时间', hant: '其他時間' })},
						],
						value: 'infinite',
					},
					textinput: {
						required: true,
					},
				},
			});
			this.closingRemarksInput = new OO.ui.TextInputWidget({
				value: '{{done}}--~~~~',
			});
			this.watchTalkPageCheckbox = new OO.ui.CheckboxInputWidget({
				selected: false,
			});
			var formElements = [
				new OO.ui.FieldLayout(this.rightsChangeSummaryInput, {
					label: conv({ hans: '授权原因', hant: '授權原因' }),
				}),
				new OO.ui.FieldLayout(this.expiryInput, {
					label: conv({ hans: '结束时间', hant: '結束時間' }),
				}),
				new OO.ui.FieldLayout(this.closingRemarksInput, {
					label: conv({ hans: '关闭请求留言', hant: '關閉請求留言' }),
				}),
			];
			if (!!templates[permission]) {
				formElements.push(
					new OO.ui.FieldLayout(this.watchTalkPageCheckbox, {
						label: conv({ hans: '监视用户讨论页', hant: '監視使用者討論頁' }),
					})
				);
			}
			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);
		};

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

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

			var addPromise = function(field, promise) {
				self.pushPending();
				promise.done(function() {
					field.$field.append($('<span>')
						.text('完成!')
						.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(conv({ hans: '错误:', hant: '錯誤:' }) + obj.error.info)
							.prop('style', 'position:relative; top:0.5em; color: #cc0000; font-weight: bold')
						);
					} else {
						field.$field.append($('<span>')
							.text(conv({ hans: '发生未知错误。', hant: '發生未知錯誤。' }))
							.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(conv({ hans: '标记请求为已完成...', hant: '標記請求為已完成...' }));
			self.submitFieldset.addItems([self.markAsDoneProgressField]);
			self.changeRightsProgressField.setLabel(conv({ hans: '授予权限...', hant: '授予權限...' }));
			self.submitFieldset.addItems([self.changeRightsProgressField]);

			if (!!templates[permission]) {
				self.issueTemplateProgressField.setLabel(conv({ hans: '发送通知...', hant: '發送通知...' }));
				self.submitFieldset.addItems([self.issueTemplateProgressField]);
			}

			addPromise(
				self.markAsDoneProgressField,
				markAsDone('\n:' + this.closingRemarksInput.getValue())
			).then(function(data) {
				addPromise(
					self.changeRightsProgressField,
					assignPermission(
						this.rightsChangeSummaryInput.getValue(),
						data.edit.newrevid,
						this.expiryInput.getValue()
					)
				).then(function() {
					if (!!templates[permission]) {
						addPromise(
							self.issueTemplateProgressField,
							issueTemplate(this.watchTalkPageCheckbox.isSelected())
						);
					}
				}.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();
				} 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(summary, revId, expiry) {
		if (expiry === '') {
			expiry = 'infinite';
		}
		var removePermission = '';
		if (permission === 'patroller' && expiry === 'infinite') {
			removePermission = 'autoreviewer';
		}
		permaLink = '[[Special:PermaLink/' + revId + '#User:' + userName + '|' + conv({ hans: '权限申请', hant: '權限申請' }) + ']]';
		var fullSummary = '+' + permissionNames[permission] + ';' + permaLink;
		if (summary !== '') {
			fullSummary += ';' + summary;
		}
		fullSummary += tagLine;

		return api.postWithToken('userrights', {
			action: 'userrights',
			format: 'json',
			user: userName.replace(/ /g, '_'),
			add: permission,
			remove: removePermission,
			reason: fullSummary,
			expiry: expiry,
		});
	}

	function markAsDone(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];

		var basetimestamp, curtimestamp, page, revision, content;

		return api.get({
			action: 'query',
			prop: 'revisions',
			rvprop: ['content', 'timestamp'],
			titles: [pageName],
			rvsection: sectionNumber,
			formatversion: '2',
			curtimestamp: true,
		})
			.then(function(data) {
				if (!data.query || !data.query.pages) {
					return $.Deferred().reject('unknown');
				}
				page = data.query.pages[0];
				if (!page || page.invalid) {
					return $.Deferred().reject('invalidtitle');
				}
				if (page.missing) {
					return $.Deferred().reject('nocreate-missing');
				}
				revision = page.revisions[0];
				basetimestamp = revision.timestamp;
				curtimestamp = data.curtimestamp;
				content = revision.content;
			})
			.then(function() {
				content = content.trim();
				content = content.replace(/(:\s*{{Status)(\|.*?)?}}/i, '$1|+}}');
				content += closingRemarks;

				return api.postWithEditToken({
					action: 'edit',
					title: pageName,
					section: sectionNumber,
					text: content,
					summary: '/* User:' + userName + ' */ 完成' + tagLine,
					formatversion: '2',

					assert: mw.config.get('wgUserName') ? 'user' : undefined,
					basetimestamp: basetimestamp,
					starttimestamp: curtimestamp,
					nocreate: true,
				});
			});
	}

	function issueTemplate(watch) {
		var talkPage = 'User talk:' + userName.replace(/ /g, '_');
		var message = '{{subst:' + templates[permission] + '}}';

		return api.get({
			action: 'query',
			prop: 'info',
			titles: talkPage,
		}).then(function(data) {
			var page = Object.values(data.query.pages)[0];
			if (page.missing !== undefined) {
				return api.create(
					talkPage,
					{
						summary: conv({ hans: '根据', hant: '根據' }) + permaLink + '授予' + permissionNames[permission] + tagLine,
						watchlist: watch ? 'watch' : 'unwatch',
					},
					message
				);
			} else if (page.contentmodel == 'flow-board') {
				return api.postWithEditToken({
					action: 'flow',
					page: talkPage,
					submodule: 'new-topic',
					nttopic: conv({ hans: '根据', hant: '根據' }) + permaLink + '授予' + permissionNames[permission],
					ntcontent: message,
					ntformat: 'wikitext',
				});
			} else {
				return api.edit(talkPage, function(revision) {
					return {
						text: (revision.content + '\n\n' + message).trim(),
						summary: conv({ hans: '根据', hant: '根據' }) + permaLink + '授予' + permissionNames[permission] + tagLine,
						watchlist: watch ? 'watch' : 'unwatch',
					};
				});
			}
		});
	}
})();
// </nowiki>