User:Firefly/more-block-info.js

From Wikipedia, the free encyclopedia
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&amp;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}&amp;action=edit&amp;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>&nbsp;<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);
        }
    }
});