Last active
October 16, 2025 21:39
-
-
Save ternera/659afdb63a705d77450e28cbccf8e47a to your computer and use it in GitHub Desktop.
Quickly vote on requests for deletion on Wikidata. Install by adding importScript("User:Ternera/RFDVoteHelper.js"); to your common.js file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (function() { | |
| 'use strict'; | |
| if (window.RFDVoteHelper) { | |
| return; | |
| } | |
| if (mw.config.get('wgPageName') !== 'Wikidata:Requests_for_deletions') { | |
| return; | |
| } | |
| const VoteDialog = function(config) { | |
| VoteDialog.super.call(this, config); | |
| this.sectionNumber = config.sectionNumber; | |
| }; | |
| OO.inheritClass(VoteDialog, OO.ui.ProcessDialog); | |
| VoteDialog.static.name = 'WikidataRFDVote'; | |
| VoteDialog.static.title = $('<span>').html('RfD Quick Vote <span style="font-size:80%; color:#666;">v1.0</span>'); | |
| VoteDialog.static.size = 'medium'; | |
| VoteDialog.static.actions = [ | |
| { action: 'cancel', icon: 'close', label: 'Cancel', flags: 'safe' }, | |
| { action: 'submit', icon: 'check', label: 'Submit Vote', flags: ['primary', 'progressive'] } | |
| ]; | |
| VoteDialog.prototype.initialize = function() { | |
| VoteDialog.super.prototype.initialize.apply(this, arguments); | |
| this.panel = new OO.ui.PanelLayout({ padded: true, expanded: false }); | |
| this.voteTypeInput = new OO.ui.DropdownInputWidget({ | |
| options: [ | |
| { data: 'delete', label: 'Delete' }, | |
| { data: 'keep', label: 'Keep' }, | |
| { data: 'comment', label: 'Comment' } | |
| ] | |
| }); | |
| this.reasonInput = new OO.ui.MultilineTextInputWidget({ | |
| placeholder: 'Optional reason or comment...', | |
| rows: 4, | |
| autosize: true | |
| }); | |
| const voteTypeField = new OO.ui.FieldLayout(this.voteTypeInput, { | |
| label: 'Vote type', | |
| align: 'top' | |
| }); | |
| const reasonField = new OO.ui.FieldLayout(this.reasonInput, { | |
| label: 'Reason/Comment', | |
| align: 'top' | |
| }); | |
| this.panel.$element.append( | |
| voteTypeField.$element, | |
| reasonField.$element | |
| ); | |
| this.$body.append(this.panel.$element); | |
| }; | |
| VoteDialog.prototype.getBodyHeight = function() { | |
| return this.panel.$element.outerHeight(true) + 20; | |
| }; | |
| VoteDialog.prototype.getActionProcess = function(action) { | |
| if (action === 'submit') { | |
| return new OO.ui.Process(async () => { | |
| const submitButton = this.actions.get({ actions: 'submit' })[0]; | |
| submitButton.setDisabled(true); | |
| try { | |
| await this.submitVote(); | |
| } catch (error) { | |
| submitButton.setDisabled(false); | |
| const errorMsg = error.error ? error.error.info : (error.message || 'An unknown error occurred.'); | |
| mw.notify('Error: ' + errorMsg, { type: 'error' }); | |
| } | |
| }); | |
| } | |
| if (action === 'cancel') { | |
| return new OO.ui.Process(() => { this.close(); }); | |
| } | |
| return VoteDialog.super.prototype.getActionProcess.call(this, action); | |
| }; | |
| VoteDialog.prototype.submitVote = async function() { | |
| const voteType = this.voteTypeInput.getValue(); | |
| const reason = this.reasonInput.getValue().trim(); | |
| let voteText = ':'; | |
| if (voteType === 'delete') { | |
| voteText += '{{Vote delete}}'; | |
| } else if (voteType === 'keep') { | |
| voteText += '{{Keep}}'; | |
| } else if (voteType === 'comment') { | |
| voteText += '{{comment}}'; | |
| } | |
| if (reason) { | |
| voteText += ' ' + reason; | |
| } | |
| voteText += ' ~~' + '~~'; | |
| const api = new mw.Api(); | |
| const data = await api.get({ | |
| action: 'parse', | |
| page: 'Wikidata:Requests_for_deletions', | |
| prop: 'wikitext', | |
| section: this.sectionNumber | |
| }); | |
| if (!data || !data.parse || !data.parse.wikitext) { | |
| throw new Error('Could not fetch section content'); | |
| } | |
| const currentText = data.parse.wikitext['*']; | |
| const newText = currentText.trimEnd() + '\n' + voteText; | |
| await api.postWithEditToken({ | |
| action: 'edit', | |
| title: 'Wikidata:Requests_for_deletions', | |
| section: this.sectionNumber, | |
| text: newText, | |
| summary: 'Voting on RfD: ' + voteType + ' (via [[User:Ternera/RFDVoteHelper.js|RFDVoteHelper]])' | |
| }); | |
| mw.notify('Vote submitted successfully!', { type: 'success' }); | |
| this.close().then(() => location.reload()); | |
| }; | |
| const RFDVoteHelper = { | |
| windowManager: null, | |
| init: function() { | |
| $('.mw-editsection').each(function() { | |
| const $editSection = $(this); | |
| const $editLink = $editSection.find('a[href*="&action=edit"]').first(); | |
| if (!$editLink.length) return; | |
| const sectionMatch = $editLink.attr('href').match(/[?&]section=([^&]+)/); | |
| if (!sectionMatch) return; | |
| const section = sectionMatch[1]; | |
| if (section === 'T' || section === '0') return; | |
| const $voteLink = $('<a>') | |
| .attr('href', '#') | |
| .text('vote') | |
| .css('margin-left', '0.3em') | |
| .click(function(e) { | |
| e.preventDefault(); | |
| RFDVoteHelper.showVoteDialog(section); | |
| }); | |
| $editSection.append( | |
| $('<span>').addClass('mw-editsection-divider').text(' | '), | |
| $('<span>').addClass('mw-editsection-bracket').text('['), | |
| $voteLink, | |
| $('<span>').addClass('mw-editsection-bracket').text(']') | |
| ); | |
| }); | |
| }, | |
| showVoteDialog: function(sectionNumber) { | |
| if (!this.windowManager) { | |
| this.windowManager = new OO.ui.WindowManager(); | |
| document.body.appendChild(this.windowManager.$element[0]); | |
| } | |
| const dialog = new VoteDialog({ sectionNumber: sectionNumber }); | |
| this.windowManager.addWindows([dialog]); | |
| this.windowManager.openWindow(dialog); | |
| } | |
| }; | |
| window.RFDVoteHelper = RFDVoteHelper; | |
| $(function() { | |
| mw.loader.using(['oojs-ui-core', 'oojs-ui-windows', 'oojs-ui-widgets', 'mediawiki.api'], function() { | |
| RFDVoteHelper.init(); | |
| }); | |
| }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment