-
Notifications
You must be signed in to change notification settings - Fork 183
speedy: add admin-only "Delete" and "Decline" buttons for quick parsing #2248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,7 +24,10 @@ Twinkle.speedy = function twinklespeedy() { | |
| return; | ||
| } | ||
|
|
||
| mw.loader.load('codex-styles'); | ||
|
|
||
| Twinkle.addPortletLink(Twinkle.speedy.callback, 'CSD', 'tw-csd', Morebits.userIsSysop ? 'Delete page according to WP:CSD' : 'Request speedy deletion according to WP:CSD'); | ||
| Twinkle.speedy.addButton(); | ||
| }; | ||
|
|
||
| Twinkle.speedy.data = [ | ||
|
|
@@ -1010,6 +1013,156 @@ Twinkle.speedy.initDialog = function twinklespeedyInitDialog(callbackfunc) { | |
| Twinkle.speedy.callback.priorDeletionCount(); | ||
| }; | ||
|
|
||
| // Fast-track deletion without form | ||
| Twinkle.speedy.instantDelete = function twinklespeedyInstantDelete() { | ||
| // check if CSD is already on the page and fill in custom rationale | ||
| if (!Twinkle.speedy.hasCSD) { | ||
| return; | ||
| } | ||
|
|
||
| const csdReason = decodeURIComponent($('#delete-reason').text()).replace(/\+/g, ' '); | ||
|
|
||
| // These three consts are literally copying what is returned by the default params | ||
| // This is not optimal at all, and if we could scan the CSD code instead of making it an ugly custom thing | ||
| // I'd be all for it | ||
| const values = ['reason']; | ||
|
|
||
| const templateParams = [[]]; | ||
| templateParams[0][1] = csdReason; | ||
|
|
||
| const normalizeds = values.map((value) => Twinkle.speedy.normalizeHash[value]); | ||
|
|
||
| // analyse each criterion to determine whether to watch the page, prompt for summary, or notify the creator | ||
| let watchPage, promptForSummary; | ||
| normalizeds.forEach((norm) => { | ||
| if (Twinkle.getPref('watchSpeedyPages').includes(norm)) { | ||
| watchPage = Twinkle.getPref('watchSpeedyExpiry'); | ||
| } | ||
| if (Twinkle.getPref('promptForSpeedyDeletionSummary').includes(norm)) { | ||
| promptForSummary = true; | ||
| } | ||
| }); | ||
|
|
||
| const warnusertalk = !Twinkle.speedy.hasCSD && normalizeds.some((norm, index) => Twinkle.getPref('warnUserOnSpeedyDelete').includes(norm) && | ||
| !(norm === 'g6' && values[index] !== 'copypaste') && !(norm === 'g5' && values[index] !== 'gs')); | ||
|
|
||
| const welcomeuser = warnusertalk && normalizeds.some((norm) => Twinkle.getPref('welcomeUserOnSpeedyDeletionNotification').includes(norm)); | ||
|
|
||
| const params = { | ||
| values: values, | ||
| normalizeds: normalizeds, | ||
| watch: watchPage, | ||
| deleteTalkPage: Twinkle.getPref('deleteTalkPageOnDelete'), | ||
| deleteRedirects: Twinkle.getPref('deleteRedirectsOnDelete'), | ||
| warnUser: warnusertalk, | ||
| welcomeuser: welcomeuser, | ||
| promptForSummary: promptForSummary, | ||
| templateParams: templateParams | ||
| }; | ||
|
|
||
| Twinkle.speedy.callbacks.sysop.main(params); | ||
| }; | ||
|
|
||
| Twinkle.speedy.instantDecline = function twinklespeedyInstantDecline() { | ||
| const wikipediaPage = new Morebits.wiki.Page(mw.config.get('wgPageName'), 'Tagging page'); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Tagging page" is not what this does |
||
| wikipediaPage.load(Twinkle.speedy.instantDecline.main); | ||
| }; | ||
|
|
||
| Twinkle.speedy.instantDecline.main = function twinklespeedyInstantDeclineMain(pageObj) { | ||
| // finds the db template | ||
| const text = pageObj.getPageText(); | ||
| const tag = /(?:\{\{\s*(db|delete|db-.*?|speedy deletion-.*?)(?:\s*\||\s*\}\}))/.exec(text)[1]; | ||
| let wikitextObj = new Morebits.wikitext.page(text); | ||
|
|
||
| // and then removes it | ||
| wikitextObj.removeTemplate(tag); | ||
|
|
||
| if (wikitextObj.getText()[0] === '\n') { | ||
| wikitextObj = new Morebits.wikitext.page(wikitextObj.getText().slice(1)); | ||
| } | ||
|
|
||
| pageObj.setPageText(wikitextObj.getText()); | ||
|
|
||
| const splitTag = tag.split(/-(.*)/).length > 1 ? tag.split(/-(.*)/)[1] : tag; | ||
|
|
||
| if (splitTag in Twinkle.speedy.normalizeHash) { | ||
| if (Twinkle.speedy.normalizeHash[splitTag] === 'db') { | ||
| pageObj.setEditSummary('Declining speedy deletion (no code provided).'); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "(no code provided)" is unnecessary |
||
| } else { | ||
| pageObj.setEditSummary('Declining speedy deletion ([[WP:CSD#' + Twinkle.speedy.normalizeHash[splitTag].toUpperCase() + '|CSD ' + Twinkle.speedy.normalizeHash[splitTag].toUpperCase() + ']]).'); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Edit summary needs to include reason for declining. In other places we've done this using |
||
| } | ||
| } else { | ||
| pageObj.setEditSummary('Declining speedy deletion (multiple criteria).'); | ||
| } | ||
| pageObj.setChangeTags(Twinkle.changeTags); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't handle watchlisting, which should be based on |
||
| pageObj.save(window.location.reload.bind(window.location)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We normally show a completion success message and then reload after a couple of seconds. Also, let's not use |
||
| }; | ||
|
|
||
| Twinkle.speedy.createCodexButton = function twinkleSpeedyCreateCodexButton({ label, action = 'neutral', weight = 'normal', size = 'medium', onClick }) { | ||
| const button = document.createElement('button'); | ||
| button.classList.add('cdx-button'); | ||
|
|
||
| // Action classes | ||
| if (action !== 'neutral') { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can drop all these |
||
| button.classList.add(`cdx-button--action-${action}`); | ||
| } | ||
|
|
||
| // Weight classes | ||
| if (weight !== 'normal') { | ||
| button.classList.add(`cdx-button--weight-${weight}`); | ||
| } | ||
|
|
||
| // Size classes | ||
| if (size !== 'medium') { | ||
| button.classList.add(`cdx-button--size-${size}`); | ||
| } | ||
|
|
||
| button.textContent = label; | ||
|
|
||
| if (typeof onClick === 'function') { | ||
| button.addEventListener('click', onClick); | ||
| } | ||
|
|
||
| return button; | ||
| }; | ||
|
|
||
| // Adds a quick "Delete" button to a speedy deletion template | ||
| Twinkle.speedy.addButton = function twinklespeedyAddButton() { | ||
| if (!Morebits.userIsSysop) { | ||
| return; | ||
| } | ||
|
|
||
| const buttonNodes = { | ||
| delete: Twinkle.speedy.createCodexButton( { label: 'Delete', action: 'destructive', weight: 'primary', size: 'medium', onClick: Twinkle.speedy.instantDelete } ), | ||
| decline: Twinkle.speedy.createCodexButton( { label: 'Decline', action: 'neutral', weight: 'normal', size: 'medium', onClick: Twinkle.speedy.instantDecline } ) | ||
| }; | ||
|
|
||
| const buttonContainer = document.createElement('div'); | ||
| buttonContainer.style.display = 'flex'; | ||
| buttonContainer.style.gap = '1em'; | ||
| buttonContainer.style.justifyContent = 'center'; | ||
|
|
||
| // Append the buttons to the container | ||
| buttonContainer.appendChild(buttonNodes.delete); | ||
| buttonContainer.appendChild(buttonNodes.decline); | ||
|
|
||
| const contestButton = document.querySelector( | ||
| 'table.ambox-speedy form[name="commentbox"].mw-inputbox-form-inline' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of using such complex and fragile selectors, it's better to add a class or id to the button and target that |
||
| ); | ||
|
|
||
| if (contestButton) { | ||
| // Replace the "Contest this deletion" link if it exists | ||
| contestButton.replaceWith(buttonContainer); | ||
| } else { | ||
| // Otherwise, add it at the end of the template notice | ||
| const notice = document.querySelector('table.ambox-speedy div.mbox-text-span'); | ||
| if (notice) { | ||
| notice.appendChild(document.createTextNode(' ')); // space separator | ||
| notice.appendChild(buttonContainer); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| Twinkle.speedy.callback.modeChanged = function twinklespeedyCallbackModeChanged(form) { | ||
| const namespace = mw.config.get('wgNamespaceNumber'); | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be loaded only if needed (inside createCodexButton).