User:GeneralNotability/mark-locked.js
// <nowiki>
// @ts-check
// Companion to markblocked - asynchronously marks locked users
// Chunks borrowed from [[m:User:Krinkle/Scripts/CVNSimpleOverlay_wiki.js]],
// [[User:GeneralNotability/ip-ext-info.js]], and [[MediaWiki:Gadget-markblocked.js]]
(()=> {
const metaApi = new mw.ForeignApi('https://meta.wikimedia.org/w/api.php', { userAgent: 'Marked-locked-userscript' });
const localApi = new mw.Api({ userAgent: 'Marked-locked-userscript' });
/**
* Get all userlinks on the page
*
* @param {JQuery} $content page contents
* @return {Map} list of unique users on the page and their corresponding links
*/
function lockedUsers_getUsers($content) {
const userLinks = new Map();
// Get all aliases for user: & user_talk: (taken from markblocked)
const userNS = [];
for (const ns in mw.config.get( 'wgNamespaceIds' ) ) {
if (mw.config.get('wgNamespaceIds')[ns] === 2 || mw.config.get('wgNamespaceIds')[ns] === 3) {
userNS.push(mw.util.escapeRegExp(ns.replace(/_/g, ' ')) + ':');
}
}
// RegExp for all titles that are User:| User_talk: | Special:Contributions/ (for userscripts)
const userTitleRX = new RegExp('^(' + userNS.join('|') + '|Special:Contrib(?:ution)?s\\/|Special:CentralAuth\\/)+([^\\/#]+)$', 'i');
const articleRX = new RegExp(mw.config.get('wgArticlePath').replace('$1', '') + '([^#?]+)');
const redlinkRX = new RegExp(mw.config.get('wgScript') + '\\?title=([^#&]+)');
$('a', $content).each(function () {
if (!$(this).attr('href')) {
// Ignore if the <a> doesn't have a href
return;
}
let articleTitleReMatch = articleRX.exec($(this).attr('href').toString());
if (!articleTitleReMatch) {
// Try the redlink check
articleTitleReMatch = redlinkRX.exec($(this).attr('href').toString());
if (!articleTitleReMatch) {
return;
}
}
let pgTitle;
try {
pgTitle = decodeURIComponent(articleTitleReMatch[1]).replace(/_/g, ' ');
} catch (error) {
// Happens sometimes on non-username paths, like if there's a slash in the path
return;
}
const userTitleReMatch = userTitleRX.exec(pgTitle);
if (!userTitleReMatch) {
return;
}
let username = userTitleReMatch[2];
// Ensure consistent case conversions with PHP as per https://phabricator.wikimedia.org/T292824
username = new mw.Title(username).getMainText();
if (!mw.util.isIPAddress(username, true)) {
if (!userLinks.get(username)) {
userLinks.set(username, []);
}
userLinks.get(username).push($(this));
}
});
return userLinks;
}
const fetchedUserInfo = new Map();
const lockLogIdsToUsers = new Map();
/**
* Check whether a user is locked and if they are, return details about it
*
* @param {string[]} users Usernames to check
*
* @return {Promise}
*/
async function lockedUsers_getLockBatch(users) {
for (const user of users) {
fetchedUserInfo.set(user, { locked: false, tooltip: '' });
}
// Pre-check whether they're locked at all - if no, return early
try {
const response = await localApi.get({
action: 'query',
list: 'globalusers',
gususers: users,
gusprop: 'locked'
});
for (const userInfo of response.query.globalusers) {
// If the 'locked' field is present, then the user is locked
if ('locked' in userInfo) {
fetchedUserInfo.set(userInfo.name, { locked: true, tooltip: '' });
if ('locklogid' in userInfo) {
lockLogIdsToUsers.set(userInfo.locklogid, userInfo.name);
}
}
}
} catch (error) {}
}
async function lockedUsers_getLockReasonsBatch(logIds) {
try {
const response = await metaApi.get({
action: 'query',
list: 'logevents',
leprop: 'ids|user|timestamp|comment|details',
leids: logIds,
lelimit: 'max'
});
for (let logEvent of response.query.logevents) {
const timestamp = new Date(logEvent.timestamp);
const prettyTimestamp = lockedUsers_formatTimeSince(timestamp);
const user = lockLogIdsToUsers.get(logEvent.logid);
const tooltip = `Locked by ${logEvent.user}: ${logEvent.comment} (${prettyTimestamp} ago)`;
fetchedUserInfo.set(user, { locked: true, tooltip });
}
} catch (error) {}
}
/**
* Formats time since a date. Taken from mark-blocked.js
*
* @param {targetDate} Date to check the time since for
*
* @return {string} A prettified string regarding time since the lock occured
*/
function lockedUsers_formatTimeSince(targetDate) {
const lockedUsers_padNumber = (number) => number <= 9 ? '0' + number : number;
const msSince = new Date() - targetDate;
let minutes = Math.floor(msSince / 60000);
if (!minutes) {
return Math.floor(msSince / 1000) + 's';
}
let hours = Math.floor(minutes / 60);
minutes %= 60;
let days = Math.floor(hours / 24);
hours %= 24;
if (days) {
return `${days}${(days < 10 ? '.' + lockedUsers_padNumber(hours) : '' )}d`;
}
return `${hours}:${lockedUsers_padNumber(minutes)}`;
}
// On window load, get all the users on the page and check if they're blocked
$.when( $.ready, mw.loader.using( 'mediawiki.util' ) ).then( function () {
mw.hook('wikipage.content').add(async function ($content) {
const usersOnPage = lockedUsers_getUsers($content);
const batchSizeLocal = (await localApi.getUserInfo()).rights.includes('apihighlimits') ? 500 : 50;
const usersOnPageList = [...usersOnPage.keys()];
while (usersOnPageList.length) {
await lockedUsers_getLockBatch(usersOnPageList.splice(0, batchSizeLocal));
}
const batchSizeMeta = (await metaApi.getUserInfo()).rights.includes('apihighlimits') ? 500 : 50;
const logLockIdsList = [...lockLogIdsToUsers.keys()];
while (logLockIdsList.length) {
await lockedUsers_getLockReasonsBatch(logLockIdsList.splice(0, batchSizeMeta));
}
usersOnPage.forEach((val, key, _) => {
const { locked, tooltip } = fetchedUserInfo.get(key.replaceAll('_', ' '));
if (locked) {
val.forEach(($link) => {
$link.css({ opacity: 0.4, 'border-bottom-size': 'thick', 'border-bottom-style': 'dashed', 'border-bottom-color': 'red' });
$link.attr('title', tooltip);
});
}
});
});
});
})();
// </nowiki>
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.