User:GeneralNotability/sharedhelpermethods.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump.
This code will be executed when previewing this page.
This code will be executed when previewing this page.
Documentation for this user script can be added at User:GeneralNotability/sharedhelpermethods.
// <nowiki>
// @ts-check
// "Building-block" functions to wrap basic API calls
// These are shared by spihelper and arbhelper
// Intentionally non-const - set this when you import to get an ad for whatever script you're using
let helperCommon_ADVERT = '';
/**
* Get the text of a page. Not that complicated.
*
* @param {string} title Title of the page to get the contents of
* @param {boolean} show Whether to show page fetch progress on-screen
* @param {?number} [sectionId=null] Section to retrieve, setting this to null will
* retrieve the entire page
*
* @return {Promise<string>} The text of the page, '' if the page does not exist.
*/
async function helperCommon_getPageText(title, show, sectionId = null) {
const $statusLine = $('<li>');
if (show) {
// Actually display the statusLine
$('#progress', document).append($statusLine);
}
// Build the link element (use JQuery so we get escapes and such)
const $link = $('<a>').attr('href', mw.util.getUrl(title)).attr('title', title).text(title);
$statusLine.html('Getting page ' + $link.prop('outerHTML'));
const finalTitle = helperCommon_stripXWikiPrefix(title);
const request = {
action: 'query',
prop: 'revisions',
rvprop: 'content',
rvslots: 'main',
indexpageids: true,
titles: finalTitle
};
if (sectionId) {
request.rvsection = sectionId;
}
try {
const response = await helperCommon_getAPI(title).get(request);
const pageid = response.query.pageids[0];
if (pageid === '-1') {
$statusLine.html('Page ' + $link.html() + ' does not exist');
return '';
}
$statusLine.html('Got ' + $link.html());
return response.query.pages[pageid].revisions[0].slots.main['*'];
} catch (error) {
$statusLine.addClass('helperCommon-errortext').html('<b>Failed to get ' + $link.html() + '</b>: ' + error);
return '';
}
}
/**
*
* @param {string} title Title of the page to edit
* @param {string} newtext New content of the page
* @param {string} summary Edit summary to use for the edit
* @param {boolean} createonly Only try to create the page - if false,
* will fail if the page already exists
* @param {string} watch What watchlist setting to use when editing - decides
* whether the edited page will be watched
* @param {?number} baseRevId Base revision ID, used to detect edit conflicts. If 0,
* we'll grab the current page ID.
* @param {?number} [sectionId=null] Section to edit - if null, edits the whole page
*/
async function helperCommon_editPage(title, newtext, summary, createonly, watch, baseRevId = null, sectionId = null) {
const $statusLine = $('<li>').appendTo($('#progress', document));
const $link = $('<a>').attr('href', mw.util.getUrl(title)).attr('title', title).text(title);
$statusLine.html('Editing ' + $link.prop('outerHTML'));
if (!baseRevId) {
baseRevId = await helperCommon_getPageRev(title);
}
const api = helperCommon_getAPI(title);
const finalTitle = helperCommon_stripXWikiPrefix(title);
const request = {
action: 'edit',
watchlist: watch,
summary: summary + helperCommon_ADVERT,
text: newtext,
title: finalTitle,
createonly: createonly,
baserevid: baseRevId
};
if (sectionId) {
request.section = sectionId;
}
try {
await api.postWithToken('csrf', request);
$statusLine.html('Saved ' + $link.prop('outerHTML'));
} catch (error) {
$statusLine.addClass('spiHelper-errortext').html('<b>Edit failed on ' + $link.html() + '</b>: ' + error);
}
}
/**
* Moves a page. Exactly what it sounds like.
*
* @param {string} sourcePage Title of the source page (page we're moving)
* @param {string} destPage Title of the destination page (page we're moving to)
* @param {string} summary Edit summary to use for the move
* @param {boolean} ignoreWarnings Whether to ignore warnings on move (used to force-move one page over another)
*/
async function helperCommon_movePage(sourcePage, destPage, summary, ignoreWarnings) {
// Move a page from sourcePage to destPage. Not that complicated.
'use strict';
const api = helperCommon_getAPI(sourcePage);
const $statusLine = $('<li>').appendTo($('#progress', document));
const $sourceLink = $('<a>').attr('href', mw.util.getUrl(sourcePage)).attr('title', sourcePage).text(sourcePage);
const $destLink = $('<a>').attr('href', mw.util.getUrl(destPage)).attr('title', destPage).text(destPage);
$statusLine.html('Moving ' + $sourceLink.prop('outerHTML') + ' to ' + $destLink.prop('outerHTML'));
try {
await api.postWithToken('csrf', {
action: 'move',
from: sourcePage,
to: destPage,
reason: summary + helperCommon_ADVERT,
noredirect: true,
movesubpages: true,
ignoreWarnings: ignoreWarnings
});
$statusLine.html('Moved ' + $sourceLink.prop('outerHTML') + ' to ' + $destLink.prop('outerHTML'));
} catch (error) {
$statusLine.addClass('spihelper-errortext').html('<b>Failed to move ' + $sourceLink.prop('outerHTML') + ' to ' + $destLink.prop('outerHTML') + '</b>: ' + error);
}
}
/**
* Purges a page's cache
*
*
* @param {string} title Title of the page to purge
*/
async function helperCommon_purgePage(title) {
// Forces a cache purge on the selected page
'use strict';
const $statusLine = $('<li>').appendTo($('#progress', document));
const $link = $('<a>').attr('href', mw.util.getUrl(title)).attr('title', title).text(title);
$statusLine.html('Purging ' + $link.prop('outerHTML'));
const strippedTitle = helperCommon_stripXWikiPrefix(title);
const api = helperCommon_getAPI(title);
try {
await api.postWithToken('csrf', {
action: 'purge',
titles: strippedTitle
});
$statusLine.html('Purged ' + $link.prop('outerHTML'));
} catch (error) {
$statusLine.addClass('spihelper-errortext').html('<b>Failed to purge ' + $link.prop('outerHTML') + '</b>: ' + error);
}
}
/**
* Blocks a user.
*
* @param {string} user Username to block
* @param {string} duration Duration of the block
* @param {string} reason Reason to log for the block
* @param {boolean} reblock Whether to reblock - if false, nothing will happen if the
* target user is already blocked
* @param {boolean} anononly For IPs, whether this is an anonymous-only block (alternative is
* that logged-in users with the IP are also blocked)
* @param {boolean} accountcreation Whether to permit the user to create new accounts
* @param {boolean} autoblock Whether to apply an autoblock to the user's IP
* @param {boolean} talkpage Whether to revoke talkpage access
* @param {boolean} email Whether to block email
* @param {string} watchBlockedUser Watchlist setting for whether to watch the newly-blocked user
*/
async function helperCommon_blockUser(user, duration, reason, reblock, anononly, accountcreation, autoblock, talkpage, email, watchBlockedUser) {
'use strict';
const userPage = 'User:' + user;
const $statusLine = $('<li>').appendTo($('#progress', document));
const $link = $('<a>').attr('href', mw.util.getUrl(userPage)).attr('title', userPage).text(user);
$statusLine.html('Blocking ' + $link.prop('outerHTML'));
const api = helperCommon_getAPI(user);
try {
await api.postWithToken('csrf', {
action: 'block',
expiry: duration,
reason: reason,
reblock: reblock,
anononly: anononly,
nocreate: accountcreation,
autoblock: autoblock,
allowusertalk: !talkpage,
noemail: email,
watchuser: watchBlockedUser,
user: user
});
$statusLine.html('Blocked ' + $link.prop('outerHTML'));
} catch (error) {
$statusLine.addClass('spihelper-errortext').html('<b>Failed to block ' + $link.prop('outerHTML') + '</b>: ' + error);
}
}
/**
* Get whether a user is currently blocked
*
* @param {string} user Username
* @return {Promise<string>} Block reason, empty string if not blocked
*/
async function helperCommon_getUserBlockReason(user) {
'use strict';
// This is not something which should ever be cross-wiki
const api = helperCommon_getAPI(user);
try {
const response = await api.get({
action: 'query',
list: 'blocks',
bklimit: '1',
bkusers: user,
bkprop: 'user|reason'
});
if (response.query.blocks.length === 0) {
// If the length is 0, then the user isn't blocked
return '';
}
return response.query.blocks[0].reason;
} catch (error) {
return '';
}
}
/**
* Get a page's latest revision ID - useful for preventing edit conflicts
*
* @param {string} title Title of the page
* @return {Promise<number>} Latest revision of a page, 0 if it doesn't exist
*/
async function helperCommon_getPageRev(title) {
'use strict';
const finalTitle = helperCommon_stripXWikiPrefix(title);
const request = {
action: 'query',
prop: 'revisions',
rvslots: 'main',
indexpageids: true,
titles: finalTitle
};
try {
const response = await helperCommon_getAPI(title).get(request);
const pageid = response.query.pageids[0];
if (pageid === '-1') {
return 0;
}
return response.query.pages[pageid].revisions[0].revid;
} catch (error) {
return 0;
}
}
/**
* Delete a page. Admin-only function.
*
* @param {string} title Title of the page to delete
* @param {string} reason Reason to log for the page deletion
*/
async function helperCommon_deletePage(title, reason) {
'use strict';
const $statusLine = $('<li>').appendTo($('#progress', document));
const $link = $('<a>').attr('href', mw.util.getUrl(title)).attr('title', title).text(title);
$statusLine.html('Deleting ' + $link.prop('outerHTML'));
const api = helperCommon_getAPI(title);
try {
await api.postWithToken('csrf', {
action: 'delete',
title: title,
reason: reason
});
$statusLine.html('Deleted ' + $link.prop('outerHTML'));
} catch (error) {
$statusLine.addClass('spihelper-errortext').html('<b>Failed to delete ' + $link.prop('outerHTML') + '</b>: ' + error);
}
}
/**
* Undelete a page (or, if the page exists, undelete deleted revisions). Admin-only function
*
* @param {string} title Title of the pgae to undelete
* @param {string} reason Reason to log for the page undeletion
*/
async function helperCommon_undeletePage(title, reason) {
'use strict';
const $statusLine = $('<li>').appendTo($('#progress', document));
const $link = $('<a>').attr('href', mw.util.getUrl(title)).attr('title', title).text(title);
$statusLine.html('Undeleting ' + $link.prop('outerHTML'));
const api = helperCommon_getAPI(title);
try {
await api.postWithToken('csrf', {
action: 'undelete',
title: title,
reason: reason
});
$statusLine.html('Undeleted ' + $link.prop('outerHTML'));
} catch (error) {
$statusLine.addClass('spihelper-errortext').html('<b>Failed to undelete ' + $link.prop('outerHTML') + '</b>: ' + error);
}
}
/**
* Render a snippet of wikitext
*
* @param {string} title Page title
* @param {string} text Text to render
* @return {Promise<string>} Rendered version of the text
*/
async function helperCommon_renderText(title, text) {
'use strict';
const request = {
action: 'parse',
prop: 'text',
pst: 'true',
text: text,
title: title
};
try {
const response = await helperCommon_getAPI(title).get(request);
return response.parse.text['*'];
} catch (error) {
console.error('Error rendering text: ' + error);
return '';
}
}
/**
* Given a page title, get an API to operate on that page
*
* @param {string} title Title of the page we want the API for
* @return {Object} MediaWiki Api/ForeignAPI for the target page's wiki
*/
function helperCommon_getAPI(title) {
'use strict';
if (title.startsWith('m:') || title.startsWith('meta:')) {
return new mw.ForeignApi('https://meta.wikimedia.org/w/api.php');
} else {
return new mw.Api();
}
}
/**
* Removes the interwiki prefix from a page title
*
* @param {*} title Page name including interwiki prefix
* @return {string} Just the page name
*/
function helperCommon_stripXWikiPrefix(title) {
// TODO: This only works with single-colon names, make it more robust
'use strict';
if (title.startsWith('m:') || title.startsWith('meta:')) {
return title.slice(title.indexOf(':') + 1);
} else {
return title;
}
}
// </nowiki>