Jump to content

User:NguoiDungKhongDinhDanh/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.
// For attribution: [[User:Firefly/more-block-info.js]]

function mbi_logTimestampToWikiTimestamp(logTimestamp) {
	const logDatetime = new Date(logTimestamp);
	return (
		logDatetime.toLocaleString('en-GB', {
			timeZone: 'UTC',
			hour: '2-digit',
			minute: '2-digit',
		}) +
		', ' +
		logDatetime.toLocaleString('en-GB', {
			timeZone: 'UTC',
			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) {
	var block =
	`<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> 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
				.replace(/\[\[(:?(?:[^|]+?))\|([^|]+?)\]\]/g, '<a href="/wiki/$1">$2</a>')
				.replace(/\[\[(:?([^|]+?))\]\]/g, '<a href="/wiki/$1">$2</a>') +
		`)</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>`;
	return block;
}

function mbi_getWarningboxLocked(username, logid, timestamp, lockActor, lockComment) {
	var lock =
	`<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
						.replace(/\[\[(:?(?:[^|]+?))\|([^|]+?)\]\]/g, '<a href="//meta.wikimedia.org/wiki/$1">$2</a>')
						.replace(/\[\[(:?([^|]+?))\]\]/g, '<a href="//meta.wikimedia.org/wiki/$1">$2</a>') +
				`)</span>` +
				`</li>` +
			`</ul> ` +
			`<a href="https://meta.wikimedia.org/wiki/Special:CentralAuth/${username}">View full log</a>` +
		`</div>`;
	return lock;
}

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);
		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
		);
	});
	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_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;
		const retVal = {
			logid: logEvent.logid,
			lockActor: logEvent.user,
			lockComment: rawComment,
			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:Special:CentralAuth/"]').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('wgCanonicalSpecialPageName') === 'Contributions') {
		mw.util.addCSS(`
			div.more-block-info-box {
				margin-bottom: 16px;
				border: 1px solid #7196bc;
				background-color: #dbedff;
				padding: 12px 24px;
			}
		`);
		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);
		}
	}
});