Content deleted Content added
use uncompressed addresses for links |
add consolidated user talk pages for IP ranges |
||
Line 147:
} else if (match[1] === 'RangeCalculator') {
displayRangeCalculator();
}
} else if (mw.config.get('wgCanonicalNamespace') === 'User_talk') {
const ip = IPAddress.from(mw.config.get('wgTitle'));
if (ip && ip.mask) {
displayRangeTalk(ip);
}
} else if (pageName === 'Special:Log/block') {
Line 165 ⟶ 170:
const userToolsContainer = document.querySelector('.mw-contributions-user-tools .mw-changeslist-links');
if (!userToolsContainer) return;
const existingTalkLink = userToolsContainer.querySelector('.mw-contributions-link-talk');
const rangeTalkLink = document.createElement('a');
rangeTalkLink.className = 'mw-contributions-link-talk-range';
const wrapper = document.createElement('span');
if (existingTalkLink) {
const mask = ip.version === 4 ? 24 : 64;
const range = ip.masked(mask);
rangeTalkLink.href = `/wiki/User_talk:${range}`;
rangeTalkLink.title = `User talk:${range}`;
rangeTalkLink.textContent = `(/${mask})`;
wrapper.appendChild(document.createTextNode(' '));
wrapper.appendChild(rangeTalkLink);
existingTalkLink.parentNode.insertBefore(wrapper, existingTalkLink.nextSibling);
} else {
rangeTalkLink.href = `/wiki/User_talk:${ip}`;
rangeTalkLink.title = `User talk:${ip}`;
rangeTalkLink.textContent = 'talk';
wrapper.appendChild(rangeTalkLink);
userToolsContainer.insertBefore(wrapper, userToolsContainer.firstChild);
}
const blockLogLink = userToolsContainer.querySelector('.mw-contributions-link-block-log');
if (blockLogLink) {
Line 253 ⟶ 278:
} else {
statusMessage.innerHTML = `Range blocks for <a href="/wiki/Special:Contributions/${ip}">${ip}</a>`;
}
mw.hook('wikipage.content').fire($(contentContainer));
}
// display talk pages for IP range
async function displayRangeTalk(ip) {
function timeAgo(timestamp) {
const delta = (Date.now() - new Date(timestamp)) / 1000;
const units = { year: 31536000, month: 2628000, day: 86400, hour: 3600, minute: 60 };
for (const [unit, seconds] of Object.entries(units)) {
let count = delta / seconds;
if (count >= 1) return `${count | 0} ${unit}${count >= 2 ? 's' : ''}`;
}
return 'just now';
}
api = new mw.Api();
const contentContainer = document.querySelector('#mw-content-text');
if (!contentContainer) return;
const elementsToRemove = [
'.noarticletext',
'#mw-content-subtitle .subpages',
'#t-whatlinkshere',
'#t-log'
];
for (const selector of elementsToRemove) {
document.querySelector(selector)?.remove();
}
const contributions = document.querySelector('#t-contributions a');
if (contributions) {
contributions.href = `/wiki/Special:Contributions/${ip}`;
}
const blockUser = document.querySelector('#t-blockip a');
if (blockUser) {
blockUser.href = `/wiki/Special:Block/${ip}`;
}
const response = await api.get({
action: 'query',
list: 'usercontribs',
uciprange: `${ip}`,
uclimit: 100,
format: 'json'
});
const ips = [];
for (const contrib of response.query.usercontribs) {
const contribIP = IPAddress.from(contrib.user);
if (contribIP) {
ipListAdd(ips, contribIP);
}
}
if (!ips.length) {
const noContributionsMessage = document.createElement('p');
noContributionsMessage.innerHTML = '<span style="color:red;">No contributions found for this IP range.</span>';
contentContainer.appendChild(noContributionsMessage);
return;
}
const titles = ips.slice(0, 50).map(ip => `User talk:${ip}`);
const infoResponse = await api.get({
action: 'query',
titles: titles.join('|'),
prop: 'info',
format: 'json',
formatversion: 2
});
const pages = infoResponse.query.pages
.filter(page => !page.missing)
.map(page => ({
title: page.title,
touched: page.touched,
redirect: !!page.redirect
}))
.sort((a, b) => b.touched.localeCompare(a.touched));
if (!pages.length) {
const noTalkPagesMessage = document.createElement('p');
noTalkPagesMessage.innerHTML = '<span style="color:red;">No user talk pages found for this IP range.</span>';
contentContainer.appendChild(noTalkPagesMessage);
return;
}
const parseTasks = [];
for (const page of pages) {
const ip = page.title.replace(/^User talk:/, '');
const relativeTime = `${timeAgo(page.touched)} ago`;
const headerText = `== ${relativeTime}: [[Special:Contributions/${ip}|${ip}]] ([[${page.title}|talk]]) ==`;
const inclusionText = `{{${page.title}}}`;
parseTasks.push({ text: headerText, disableeditsection: true, });
parseTasks.push({ text: inclusionText, disableeditsection: false, });
}
const parsePromises = parseTasks.map(task =>
api.post({
action: 'parse',
format: 'json',
prop: 'text',
contentmodel: 'wikitext',
title: `Special:BlankPage/RangeTalk/${ip}`,
text: task.text,
disableeditsection: task.disableeditsection,
})
);
for (const promise of parsePromises) {
const result = await promise;
const html = result.parse.text['*'];
const fragment = document.createRange().createContextualFragment(html);
contentContainer.appendChild(fragment);
}
mw.hook('wikipage.content').fire($(contentContainer));
|