User:Firefly/more-block-info.js
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
function mbi_logTimestampToWikiTimestamp(logTimestamp) {
const logDatetime = new Date(logTimestamp);
return (
logDatetime.toLocaleString("en-GB", {
hour: "2-digit",
minute: "2-digit",
}) +
", " +
logDatetime.toLocaleString("en-GB", {
day: "numeric",
month: "long",
year: "numeric",
})
);
}
function mbi_getBlockOptionsString(blockEntry) {
let retVal = " (";
if ("nocreate" in blockEntry) {
// ACB
retVal += "account creation blocked, ";
}
if (!("allowusertalk" in blockEntry)) {
// NTP
retVal += "cannot edit own talk page, ";
}
retVal += ")";
retVal = retVal.replace(", )", ")").replace("()", "");
return retVal;
}
function mbi_getBlockDisplayListItem(user, logid, timestamp, blockActor, blockComment, blockExpiry, blockOptions, isPartial) {
return `<li data-mw-logaction="block/block" class="mw-logline-block" > <a href="/w/index.php?title=Special:Log&logid=${logid}" title="Special:Log" >${timestamp}</a > <a href="/wiki/User:${blockActor}" class="mw-userlink userlink" title="User:${blockActor}" ><bdi>${blockActor}</bdi></a > <span class="mw-usertoollinks mw-changeslist-links" ><span ><a href="/wiki/User_talk:${blockActor}" class="mw-usertoollinks-talk userlink" title="User talk:${blockActor}" >talk</a ></span > <span ><a href="/wiki/Special:Contributions/${blockActor}" class="mw-usertoollinks-contribs userlink" title="Special:Contributions/${blockActor}" >contribs</a ></span ></span > ${isPartial && '<strong>partially</strong> '|| ''}blocked <a href="/wiki/Special:Contributions/${user}" class="mw-userlink mw-anonuserlink" title="Special:Contributions/${user}" ><bdi>${user}</bdi></a > <span class="mw-usertoollinks mw-changeslist-links" ><span ><a href="/w/index.php?title=User_talk:${user}&action=edit&redlink=1" class="new mw-usertoollinks-talk" title="User talk:${user}" >talk</a ></span ></span > with an expiration time of <span class="blockExpiry" title="${blockExpiry}">${blockExpiry}</span> ${blockOptions} <span class="comment">(${blockComment})</span> <span class="mw-logevent-actionlink sysop-show">(<a href="/wiki/Special:Unblock/${user}" title="Special:Unblock/${user}">unblock</a> | <a href="/wiki/Special:Block/${user}" title="Special:Block/${user}">change block</a>)</span> </li>`;
}
function mbi_getWarningboxLocked(username, logid, timestamp, lockActor, lockComment) {
return `<div class="warningbox mw-warning-with-logexcerpt mw-content-ltr more-block-info-box more-block-info-locked" dir="ltr" lang="en"> <p> This account is currently globally locked. The latest <span class="plainlinks" ><a class="external text" href="https://meta.wikimedia.org/wiki/Special:CentralAuth/${username}" >global account change log</a ></span > entry is provided below for reference: </p> <ul class="mw-logevent-loglines"> <li> <a href="https://meta.wikimedia.org/w/index.php?title=Special:Log&logid=${logid}" >${timestamp}</a > <a href="https://meta.wikimedia.org/wiki/User:${lockActor}" class="mw-userlink userlink" title="User:${lockActor}" ><bdi>${lockActor}</bdi></a > <span class="mw-usertoollinks mw-changeslist-links" ><span ><a href="https://meta.wikimedia.org/wiki/User_talk:${lockActor}" class="mw-usertoollinks-talk userlink" title="User talk:${lockActor}" >talk</a ></span > <span ><a href="https://meta.wikimedia.org/wiki/Special:Contributions/${lockActor}" class="mw-usertoollinks-contribs userlink" title="Special:Contributions/${lockActor}" >contribs</a ></span ></span > changed status for global account <a href="/wiki/User:${username}" class="mw-userlink userlink user-blocked-indef" ><bdi>${username}</bdi></a > <span class="mw-usertoollinks mw-changeslist-links" ><span ><a href="/wiki/User_talk:${username}" class="mw-usertoollinks-talk userlink user-blocked-indef" >talk</a ></span > <span ><a href="/wiki/Special:Contributions/${username}" class="mw-usertoollinks-contribs userlink user-blocked-indef" >contribs</a ></span ></span > : set locked; unset (none) <span class="comment">(${lockComment})</span> </li> </ul> <a href="https://meta.wikimedia.org/wiki/Special:CentralAuth/${username}">View full log</a> </div>`;
}
function mbi_HtmlEncode(rawString) {
return $("<div>").text(rawString).html();
}
function mbi_insertMbiBox(boxContent) {
let blockedNoticeElemList = $(".mw-contributions-blocked-notice");
// If there's an existing block box, append after it
// If not, just shove our box at the top of the content element
if (blockedNoticeElemList.length > 0) {
$($(".mw-contributions-blocked-notice")[0]).after(boxContent);
} else {
$("#mw-content-text").prepend(boxContent);
}
}
async function mbi_getWarningboxRangeblocks(rangeblocks) {
let liConcat = "";
rangeblocks.forEach(function (blockEntry) {
//const blockEntry = rangeblocks[cidr].blockEntries[0];
const blockOptions = mbi_getBlockOptionsString(blockEntry);
//const parsedComment = await mbi_parseWikitext(blockEntry.comment);
liConcat += mbi_getBlockDisplayListItem(
blockEntry.user.replace("User:", ""),
blockEntry.id,
mbi_logTimestampToWikiTimestamp(blockEntry.timestamp),
blockEntry.by,
mbi_HtmlEncode(blockEntry.reason), // HTML-encode the block summary as they often contain hidden notes
mbi_logTimestampToWikiTimestamp(blockEntry.expiry),
blockOptions,
"partial" in blockEntry
);
});
return `<div class="warningbox mw-warning-with-logexcerpt mw-content-ltr more-block-info-box more-block-info-rangeblocks " dir="ltr" lang="en" > <p> This IP address is currently subject to one or more rangeblocks. A list of these rangeblocks is below: </p> <ul class="mw-logevent-loglines">${liConcat}</ul> </div>`;
}
// Shamelessly nicked from [[User:GeneralNotability/mark-locked.js]]
async function mbi_isLocked(user) {
const api = new mw.Api();
try {
const response = await api.get({
action: "query",
list: "globalallusers",
agulimit: "1",
agufrom: user,
aguto: user,
aguprop: "lockinfo",
});
if (response.query.globalallusers.length === 0) {
// If the length is 0, then we couldn't find the global user
return false;
}
// If the 'locked' field is present, then the user is locked
return "locked" in response.query.globalallusers[0];
} catch (error) {
return false;
}
}
async function mbi_parseWikitext(wikitext, stripParaTag = true) {
const api = new mw.Api();
const apiResponse = await api.post({
action: "parse",
format: "json",
text: wikitext,
prop: "text",
disableeditsection: 1,
formatversion: "2",
});
const parsedText = apiResponse.parse.text;
if (stripParaTag) {
let div = document.createElement("div");
div.innerHTML = parsedText;
const pHtml = div.getElementsByTagName("p")[0].innerHTML;
return pHtml.trim();
} else {
return parsedText.trim();
}
}
async function mbi_getRangeblocks(user) {
const retVal = {
currentlyBlocked: false,
everBlocked: false,
blockEntries: [],
};
const api = new mw.Api();
try {
const response = await api.post({
action: "query",
list: "blocks",
bkip: user,
});
let retVal = [];
response.query.blocks.forEach(function (block) {
if (block.user.includes("/")) {
retVal.push(block);
}
});
return retVal;
} catch (error) {
return undefined;
}
}
/*async function mbi_getBlockEntries(user) {
const now = Date.now();
function blockIsCurrent(blockLogEntry) {
return new Date(blockLogEntry.params.expiry) > now;
}
const retVal = {
currentlyBlocked: false,
everBlocked: false,
blockEntries: [],
};
const api = new mw.Api();
try {
const response = await api.post({
action: "query",
list: "logevents",
letitle: `User:${user}`,
letype: "block",
});
const blockEntries = response.query.logevents;
if (blockEntries.length > 0) {
// user/range has been blocked at least once
retVal.everBlocked = true;
retVal.currentlyBlocked = blockIsCurrent(blockEntries[0]);
retVal.blockEntries = blockEntries;
}
return retVal;
} catch (error) {
return undefined;
}
}*/
async function mbi_getLockLogEntry(user) {
const api = new mw.Api();
try {
const response = await $.post("https://meta.wikimedia.org/w/api.php", {
action: "query",
list: "logevents",
lelimit: "1",
letitle: `User:${user}@global`,
leaction: "globalauth/setstatus",
origin: "*",
format: "json",
});
const logEvent = response.query.logevents[0];
const wikiTimestamp = mbi_logTimestampToWikiTimestamp(logEvent.timestamp);
const rawComment = logEvent.comment.replace("[[", "[[meta:"); // Crude fix for wikilinks in lock comments intending to point to Meta-Wiki pages
const parsedComment = await mbi_parseWikitext(rawComment);
const retVal = {
logid: logEvent.logid,
lockActor: logEvent.user,
lockComment: parsedComment,
timestamp: wikiTimestamp,
};
return retVal;
} catch (error) {
console.log(error);
return undefined;
}
}
async function mbi_handleRegisteredUser(relevantUserName) {
const userLocked = await mbi_isLocked(relevantUserName);
if (userLocked) {
const lockLogEntry = await mbi_getLockLogEntry(relevantUserName);
const lockedBox = mbi_getWarningboxLocked(
relevantUserName,
lockLogEntry.logid,
lockLogEntry.timestamp,
lockLogEntry.lockActor,
lockLogEntry.lockComment
);
$("a[title='m:Global locks']").parent().parent().remove(); // Remove "this account is globally locked" box
mbi_insertMbiBox(lockedBox);
}
}
async function mbi_handleIPOrRange(relevantUserName) {
const ipRangeBlocks = await mbi_getRangeblocks(relevantUserName);
if (ipRangeBlocks.length > 0) {
// If there's only one block returned, and it matches the rangeblock already displayed
// then don't duplicate it
if (ipRangeBlocks.length == 1) {
const existingRange = $(".mw-warning-with-logexcerpt>ul>li>a.mw-anonuserlink");
if (existingRange.length > 0) {
const existingRangeIP = existingRange.attr("href").split("/").slice(3).join("/");
if (existingRangeIP === ipRangeBlocks[0].user.replace("User:", "")) {
return;
}
}
}
const rangeblockBox = await mbi_getWarningboxRangeblocks(ipRangeBlocks);
mbi_insertMbiBox(rangeblockBox);
}
}
$.when($.ready, mw.loader.using("mediawiki.util")).then(async function () {
if (mw.config.get("wgPageName").includes("Special:Contributions")) {
mw.util.addCSS("div.more-block-info-box.mw-warning-with-logexcerpt.mw-warning-with-logexcerpt.mw-warning-with-logexcerpt { " + "border: 1px solid #7196bc; " + "background-color: #dbedff; " + "}");
const relevantUserName = mw.config.get("wgRelevantUserName");
if (relevantUserName !== null) {
// wgRelevantUserName is null for IP ranges (see phab T206954)
const isip = mw.util.isIPAddress(relevantUserName, true);
if (!isip) {
// No point checking for locks if IP, IPs cannot be locked
mbi_handleRegisteredUser(relevantUserName);
} else {
mbi_handleIPOrRange(relevantUserName);
}
} else {
// We're naively assuming an IP range
// Have to extract range manually from URL as wgRelevantUserName is null
const queryParamsRaw = window.location.search;
let ipRange;
if (queryParamsRaw === "") {
ipRange = mw.config.get("wgPageName").split("/").slice(1).join("/");
} else {
const queryParams = new URLSearchParams(queryString);
ipRange = queryParams.get("target");
}
mbi_handleIPOrRange(ipRange);
}
}
});