Module:T-Person
Documentation for this module may be created at Module:T-Person/doc
-- Module:T-Person
-- Blueprint migration of Person template (Phase 1)
local p = {}
-- ==================== Required modules ====================
local Blueprint = require('Module:LuaTemplateBlueprint')
local TemplateHelpers = require('Module:TemplateHelpers')
local ErrorHandling = require('Module:ErrorHandling')
local ConfigRepository = require('Module:ConfigRepository')
local LinkParser = require('Module:LinkParser')
-- Blueprint default: Module-level cache for lazy-loaded modules
local moduleCache = {}
-- Blueprint default: Lazy module loader
local function lazyRequire(moduleName)
return function()
if not moduleCache[moduleName] then
moduleCache[moduleName] = require(moduleName)
end
return moduleCache[moduleName]
end
end
-- Blueprint default: Modules to lazy load
local CanonicalForms = lazyRequire('Module:CanonicalForms')
local CountryData = lazyRequire('Module:CountryData')
local SemanticCategoryHelpers = lazyRequire('Module:SemanticCategoryHelpers')
local LanguageNormalization = lazyRequire('Module:NormalizationLanguage')
local socialFooter = lazyRequire('Module:SocialMedia')
local Achievements = lazyRequire('Module:AchievementSystem')
-- ==================== Helper Functions ====================
local errorContext = ErrorHandling.createContext('T-Person')
local function extractSemanticValue(fieldValue, fieldName)
return TemplateHelpers.extractSemanticValue(fieldValue, fieldName, errorContext)
end
-- ================================================================================
-- IMPORTANT! TEMPLATE BLUEPRINT FRAMEWORK INSTRUCTIONS
local template = Blueprint.registerTemplate('Person', {
features = {
title = true,
logo = false,
fields = true,
socialMedia = true,
semanticProperties = true,
categories = true,
errorReporting = true,
}
})
-- Blueprint default: Initialize standard configuration
Blueprint.initializeConfig(template)
-- CONTROL THE VISUAL ORDER OF BLOCKS
template.config.blockSequence = {
'title',
'achievementHeader',
'portraitCarousel',
'fields',
'achievementBadges',
'socialMedia'
}
-- ================================================================================
-- Block: Title
local function renderTitleBlock(args, frame)
local pageId = mw.title.getCurrentTitle().id
local cls, name = Achievements().getTitleClass(pageId, frame)
local aid = ''
if cls and cls ~= '' then
aid = cls:gsub('^achievement%-', '')
end
return TemplateHelpers.renderTitleBlock(
args,
'template-title template-title-person person-template',
'Person',
{
achievementSupport = true,
achievementClass = cls or '',
achievementId = aid,
achievementName = name or ''
}
)
end
-- Block: Achievement header
local function renderAchievementHeaderBlock(args, frame)
local pageId = mw.title.getCurrentTitle().id
local css, display, aid = Achievements().getTitleAchievement(pageId, frame)
if css ~= '' and display ~= '' and aid ~= '' then
return string.format(
'|-\n! colspan="2" class="achievement-header %s" data-achievement-id="%s" data-achievement-name="%s" | %s',
aid, aid, display, display
)
end
return ''
end
-- Block: Portrait carousel
local function renderPortraitCarousel(args)
if not args.portrait or args.portrait == '' then
return ''
end
local images = SemanticCategoryHelpers().splitMultiValueString(
args.portrait,
SemanticCategoryHelpers().SEMICOLON_PATTERN
)
if #images == 0 then
return ''
elseif #images == 1 then
return string.format(
'|-\n| colspan="2" class="person-portrait" | [[Image:%s|220px|center]]',
images[1]
)
end
local out = '|-\n| colspan="2" class="person-portrait-carousel" |'
out = out .. '<div class="carousel-container">'
out = out .. '<div class="carousel-nav carousel-prev">◀</div>'
out = out .. '<div class="carousel-images">'
for i, img in ipairs(images) do
local vis = (i == 1) and 'carousel-visible' or 'carousel-hidden'
local pos = ''
if #images == 2 then
pos = (i == 1) and 'carousel-orbital-1' or 'carousel-orbital-2'
else
if i == 2 then pos = 'carousel-right' end
if i == #images then pos = 'carousel-left' end
end
out = out .. string.format(
'<div class="carousel-item %s %s" data-index="%d">[[Image:%s|220px|center]]</div>',
vis, pos, i, img
)
end
out = out .. '</div>'
out = out .. '<div class="carousel-nav carousel-next">▶</div>'
out = out .. '</div>'
return out
end
-- Block: Fields
local function renderFieldsBlock(args, frame)
local processors = {
community = function(val)
return select(1, CanonicalForms().normalize(val, template.config.mappings.community)) or val
end,
languages = function(val)
return LanguageNormalization().formatLanguages(val)
end,
country = TemplateHelpers.normalizeCountries,
website = TemplateHelpers.normalizeWebsites,
soi = function(val)
local formatted = string.format('[%s Here]', val)
return {
isCompleteHtml = true,
html = string.format(TemplateHelpers.FIELD_FORMAT, 'SOI', formatted)
}
end,
userbox = function(val)
local pid = mw.title.getCurrentTitle().id
local ok, box = pcall(function()
return Achievements().renderAchievementBox(pid, frame)
end)
if ok and box and box ~= '' then
return box
end
return val
end
}
return TemplateHelpers.renderFieldsBlock(args, template.config.fields, processors)
end
-- Block: Achievement badges
local function renderAchievementBadgesBlock(args, frame)
local pid = mw.title.getCurrentTitle().id
local ok, achmod = pcall(Achievements)
local parts = { '|-\n| colspan="2" |' }
if not ok then
table.insert(parts, '<div class="achievement-badges"></div>')
return table.concat(parts)
end
local types = achmod.loadTypes(frame)
local defs = {}
for _, t in ipairs(types) do defs[t.id] = t end
local badges = {}
for _, a in ipairs(achmod.getUserAchievements(pid)) do
local td = defs[a.type]
if td and td.type == 'badge' then
table.insert(badges, { id = a.type, name = td.name or a.type })
end
end
if #badges > 0 then
table.insert(parts, '<div class="achievement-badges">')
for _, b in ipairs(badges) do
table.insert(parts, string.format(
'<div class="achievement-badge %s" data-achievement-id="%s" data-achievement-name="%s" title="%s"></div>',
b.id, b.id, b.name, b.name
))
end
table.insert(parts, '</div>')
else
table.insert(parts, '<div class="achievement-badges"></div>')
end
return table.concat(parts)
end
-- Register blocks with Blueprint
Blueprint.addBlock(template, 'title', renderTitleBlock)
Blueprint.addBlock(template, 'achievementHeader', renderAchievementHeaderBlock)
Blueprint.addBlock(template, 'portraitCarousel', renderPortraitCarousel)
Blueprint.addBlock(template, 'fields', renderFieldsBlock)
Blueprint.addBlock(template, 'achievementBadges', renderAchievementBadgesBlock)
Blueprint.addBlock(template, 'socialMedia', socialFooter().render)
-- ==================== Preprocessors ====================
Blueprint.addPreprocessor(template, 'setPageIdField')
-- ==================== Main Render Function ====================
function p.render(frame)
return ErrorHandling.protect(
errorContext,
'render',
function()
return template.render(frame)
end,
ErrorHandling.getMessage('TEMPLATE_RENDER_ERROR'),
frame
)
end
return p