MediaWiki:Gadget-staff.js
MediaWiki interface page
More actions
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
(function () {
var api;
var HIDDEN_TAG_GROUPS = {
'*': true,
user: true,
autoconfirmed: true,
emailconfirmed: true,
bot: true,
};
var joinedDateFormatter = new Intl.DateTimeFormat('en', {
month: 'short',
year: 'numeric',
});
function getApi() {
if (!api) {
api = new mw.Api();
}
return api;
}
function normalizeUserName(name) {
return String(name || '')
.replace(/_/g, ' ')
.replace(/^\s+|\s+$/g, '');
}
function getField(card, fieldName) {
return card.querySelector('[data-staff-field="' + fieldName + '"]');
}
function formatRegistrationDate(registration) {
var date;
if (!registration) {
return 'Joined —';
}
date = new Date(registration);
if (isNaN(date.getTime())) {
return 'Joined —';
}
return 'Joined ' + joinedDateFormatter.format(date);
}
function getGroupMessageKey(group) {
return 'group-' + group + '-member';
}
function formatGroupFallback(group) {
return String(group || '')
.replace(/[-_]+/g, ' ')
.replace(/\b\w/g, function (char) {
return char.toUpperCase();
});
}
function getGroupLabel(group) {
var key = getGroupMessageKey(group);
if (mw.message(key).exists()) {
return mw.message(key).text();
}
return formatGroupFallback(group);
}
function getVisibleGroups(groups) {
var visible = [];
var i;
var group;
groups = Array.isArray(groups) ? groups : [];
for (i = 0; i < groups.length; i++) {
group = groups[i];
if (!HIDDEN_TAG_GROUPS[group]) {
visible.push(group);
}
}
return visible;
}
function mergeUserGroups(userData) {
var merged = [];
var seen = {};
var groups = (userData && userData.groups) || [];
var memberships = (userData && userData.groupmemberships) || [];
var i;
var name;
for (i = 0; i < groups.length; i++) {
name = groups[i];
if (!seen[name]) {
seen[name] = true;
merged.push(name);
}
}
for (i = 0; i < memberships.length; i++) {
name = memberships[i] && memberships[i].group;
if (name && !seen[name]) {
seen[name] = true;
merged.push(name);
}
}
return merged;
}
function loadMissingGroupLabels(groups) {
var messageKeys = [];
var seen = {};
var i;
var key;
for (i = 0; i < groups.length; i++) {
key = getGroupMessageKey(groups[i]);
if (seen[key] || mw.message(key).exists()) continue;
seen[key] = true;
messageKeys.push(key);
}
if (!messageKeys.length) return $.Deferred().resolve().promise();
return getApi()
.get({
action: 'query',
meta: 'allmessages',
format: 'json',
amincludelocal: 1,
amlang: mw.config.get('wgUserLanguage') || 'en',
ammessages: messageKeys.join('|'),
})
.then(function (data) {
var messages = (data && data.query && data.query.allmessages) || [];
var loadedMessages = {};
var i;
var content;
for (i = 0; i < messages.length; i++) {
content = messages[i].content;
if (typeof content !== 'string') {
content = messages[i]['*'];
}
if (typeof content === 'string' && content !== '') {
loadedMessages[messages[i].name] = content;
}
}
if (Object.keys(loadedMessages).length) {
mw.messages.set(loadedMessages);
}
});
}
function fetchUserData(userName) {
return getApi()
.get({
action: 'query',
list: 'users',
format: 'json',
formatversion: 2,
ususers: userName,
usprop: 'registration|groups|groupmemberships',
})
.then(function (data) {
var users = (data && data.query && data.query.users) || [];
return users[0] || null;
});
}
function fetchUserActivity(userName) {
return getApi()
.get({
action: 'query',
list: 'recentchanges',
format: 'json',
formatversion: 2,
rcprop: 'user|timestamp',
rctype: 'edit|new|log',
rcuser: userName,
rclimit: 1,
})
.then(function (data) {
var changes = (data && data.query && data.query.recentchanges) || [];
return changes.length > 0;
});
}
function renderGroups(card, groups) {
var container = getField(card, 'tags');
var visibleGroups = getVisibleGroups(groups);
var i;
var chip;
if (!container) return;
container.innerHTML = '';
if (!visibleGroups.length) {
chip = document.createElement('span');
chip.className = 'staff-role-tag staff-role-tag--placeholder';
chip.appendChild(document.createTextNode('No tags'));
container.appendChild(chip);
return;
}
for (i = 0; i < visibleGroups.length; i++) {
chip = document.createElement('span');
chip.className = 'staff-role-tag';
chip.appendChild(document.createTextNode(getGroupLabel(visibleGroups[i])));
container.appendChild(chip);
}
}
function renderJoined(card, registration) {
var joined = getField(card, 'joined');
if (joined) {
joined.textContent = formatRegistrationDate(registration);
}
}
function renderStatus(card, isActive) {
var status = getField(card, 'status');
if (!status) return;
status.className = 'staff-status ' + (isActive ? 'active' : 'inactive');
status.textContent = isActive ? 'Active' : 'Inactive';
}
function hydrateCard(card) {
var userName = normalizeUserName(card.getAttribute('data-staff-user'));
if (!userName || card.getAttribute('data-staff-ready') === '1') return;
$.when(fetchUserData(userName), fetchUserActivity(userName)).then(
function (userData, isActive) {
var groups = mergeUserGroups(userData);
var registration = userData && userData.registration ? userData.registration : null;
loadMissingGroupLabels(getVisibleGroups(groups)).then(function () {
renderGroups(card, groups);
renderJoined(card, registration);
renderStatus(card, !!isActive);
card.setAttribute('data-staff-ready', '1');
});
},
function (error) {
console.error('staff card hydration failed for', userName, error);
}
);
}
function init() {
var cards = document.querySelectorAll('.staff-card[data-staff-user]');
var i;
if (!cards.length) return;
for (i = 0; i < cards.length; i++) {
hydrateCard(cards[i]);
}
}
mw.loader.using(['mediawiki.api']).then(init);
})();