Module:LuaTemplatePerson: Difference between revisions
Appearance
No edit summary |
// via Wikitext Extension for VSCode Tag: Reverted |
||
Line 14: | Line 14: | ||
local TemplateStructure = require('Module:TemplateStructure') | local TemplateStructure = require('Module:TemplateStructure') | ||
local LanguageNormalization = require('Module:LanguageNormalization') | local LanguageNormalization = require('Module:LanguageNormalization') | ||
local Achievements = require('Module:Achievements') | |||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
Line 120: | Line 121: | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
local function renderTitleBlock(args) | local function renderTitleBlock(args) | ||
return string.format('! colspan="2" class=" | -- Get achievement class (if any) for this user | ||
"Person") | local username = args.name or "" | ||
local achievementClass = Achievements.getTitleClass(username) | |||
local classes = "template-title template-title-person person-template" | |||
if achievementClass ~= "" then | |||
classes = classes .. " " .. achievementClass | |||
end | |||
return string.format('! colspan="2" class="%s" | %s', | |||
classes, "Person") | |||
end | end | ||
Line 213: | Line 223: | ||
elseif key == "soi" then | elseif key == "soi" then | ||
value = string.format("[%s Here]", value) | value = string.format("[%s Here]", value) | ||
elseif key == "userbox" then | |||
-- Replace user-provided userbox value with achievement box | |||
local username = args.name or "" | |||
local achievementBox = Achievements.renderAchievementBox(username) | |||
-- If there are achievements for this user, use those instead | |||
if achievementBox ~= "" then | |||
value = achievementBox | |||
end | |||
end | end | ||
Line 292: | Line 311: | ||
} | } | ||
} | } | ||
-- Track this page for achievement display cache purging | |||
local pageName = mw.title.getCurrentTitle().fullText | |||
if args.name and args.name ~= "" then | |||
Achievements.trackPage(pageName) | |||
end | |||
return TemplateStructure.render(args, config) .. "\n" .. computeCategories(args) | return TemplateStructure.render(args, config) .. "\n" .. computeCategories(args) |
Revision as of 23:42, 28 March 2025
Documentation for this module may be created at Module:LuaTemplatePerson/doc
-- Template:Person is a module for rendering person profiles with a carousel for multiple images,
-- supporting various normalizations (countries, links, etc.) and integrating social media.
-- The module dynamically assigns categories based on community and country.
local p = {}
-- Dependencies
local CanonicalForms = require('Module:CanonicalForms')
local countryNormalization = require('Module:CountryNormalization')
local RegionalMappingICANN = require('Module:RegionalMappingICANN')
local linkParser = require('Module:LinkParser')
local MultiCountryDisplay = require('Module:MultiCountryDisplay')
local socialFooter = require('Module:SocialMedia')
local TemplateStructure = require('Module:TemplateStructure')
local LanguageNormalization = require('Module:LanguageNormalization')
local Achievements = require('Module:Achievements')
--------------------------------------------------------------------------------
-- Configuration and Constants
--------------------------------------------------------------------------------
local Config = {
communityMapping = {
{canonical = "ICANN Community",
synonyms = {"icann community", "icann", "community"},
category = "[[Category:ICANN Community]]"},
{canonical = "IG Community",
synonyms = {"ig community", "ig", "internet governance"},
category = "[[Category:IG Community]]"},
{canonical = "ICANN Staff",
synonyms = {"icann staff", "staff"},
category = "[[Category:ICANN Staff]]"},
{canonical = "Former ICANN Staff",
synonyms = {"former icann staff", "former staff", "ex-staff"},
category = "[[Category:Former ICANN Staff]]"}
},
fields = {
{key="community", label="Community"},
{key="affiliation", label="ICANN group"},
{key="organization", label="Organization"},
{key="region", label="Region"},
{key="country", label="Country"},
{key="languages", label="Languages"},
{key="website", label="Website"},
{key="soi", label="SOI"},
{key="userbox", label="Achievements"}
},
patterns = {
itemDelimiter=";%s*",
websitePattern="^https?://[^%s]+"
}
}
--------------------------------------------------------------------------------
-- Helper Functions
--------------------------------------------------------------------------------
-- Split semicolon-separated values into an array
local function splitSemicolonValues(value)
if not value or value == "" then return {} end
local items = {}
for item in string.gmatch(value, "[^;]+") do
local trimmed = item:match("^%s*(.-)%s*$")
if trimmed and trimmed ~= "" then
table.insert(items, trimmed)
end
end
return items
end
-- Gets the first non-empty value from a list of equivalent keys
local function getFieldValue(args, field)
-- For fields with multiple equivalent keys
if field.keys then
for _, key in ipairs(field.keys) do
if args[key] and args[key] ~= "" then
return key, args[key]
end
end
return nil, nil
end
-- For single-key fields
return field.key, (args[field.key] and args[field.key] ~= "") and args[field.key] or nil
end
-- Process multiple language entries
local function normalizeLanguages(value)
if not value or value == "" then return "" end
local languages = splitSemicolonValues(value)
if #languages > 1 then
local listItems = {}
for _, lang in ipairs(languages) do
table.insert(listItems, string.format("<li>%s</li>", lang))
end
return string.format("<ul class=\"template-list template-list-language\" style=\"margin:0; padding-left:1em;\">%s</ul>", table.concat(listItems, ""))
end
return languages[1] or ""
end
-- Process multiple country entries handled by the MultiCountryDisplay module
-- Process multiple website entries
local function normalizeWebsites(value)
if not value or value == "" then return "" end
local websites = splitSemicolonValues(value)
if #websites > 1 then
local listItems = {}
for _, site in ipairs(websites) do
local formattedLink = string.format("[%s %s]", site, linkParser.strip(site))
table.insert(listItems, string.format("<li>%s</li>", formattedLink))
end
return string.format("<ul class=\"template-list template-list-website\" style=\"margin:0; padding-left:1em;\">%s</ul>", table.concat(listItems, ""))
elseif #websites == 1 then
return string.format("[%s %s]", websites[1], linkParser.strip(websites[1]))
end
return ""
end
--------------------------------------------------------------------------------
-- Block Rendering Functions
--------------------------------------------------------------------------------
local function renderTitleBlock(args)
-- Get achievement class (if any) for this user
local username = args.name or ""
local achievementClass = Achievements.getTitleClass(username)
local classes = "template-title template-title-person person-template"
if achievementClass ~= "" then
classes = classes .. " " .. achievementClass
end
return string.format('! colspan="2" class="%s" | %s',
classes, "Person")
end
-- Process portrait field (semicolon-separated images) into carousel
local function renderPortraitCarousel(args)
if not args.portrait or args.portrait == "" then
return ""
end
local imageFiles = splitSemicolonValues(args.portrait)
if #imageFiles == 0 then
return ""
end
-- If only one image, just display it regularly
if #imageFiles == 1 then
return string.format("|-\n| colspan=\"2\" class=\"person-portrait\" | [[Image:%s|220px|center]]", imageFiles[1])
end
-- Start building the carousel for multiple images
local output = "|-\n| colspan=\"2\" class=\"person-portrait-carousel\" |"
-- Add the carousel container
output = output .. '<div class="carousel-container">'
-- Add navigation arrows if more than one image
if #imageFiles > 1 then
output = output .. '<div class="carousel-nav carousel-prev">◀</div>'
end
-- Start images container
output = output .. '<div class="carousel-images">'
-- Add each image as a carousel item
for i, imageFile in ipairs(imageFiles) do
local visibility = i == 1 and "carousel-visible" or "carousel-hidden"
local position = ""
-- Special handling for exactly 2 images (orbital layout)
if #imageFiles == 2 then
if i == 1 then
position = "carousel-orbital-1" -- First image in orbital position
else
position = "carousel-orbital-2" -- Second image in orbital position
end
else
-- Original logic for 3+ images
if i == 2 and #imageFiles > 1 then position = "carousel-right" end
if i == #imageFiles and #imageFiles > 2 then position = "carousel-left" end
end
output = output .. string.format(
'<div class="carousel-item %s %s" data-index="%d">[[Image:%s|220px|center]]</div>',
visibility, position, i, imageFile
)
end
-- Close images container
output = output .. '</div>'
-- Add next button if more than one image
if #imageFiles > 1 then
output = output .. '<div class="carousel-nav carousel-next">▶</div>'
end
-- Close carousel container
output = output .. '</div>'
return output
end
local function renderFieldsBlock(args)
local out = {}
for _, field in ipairs(Config.fields) do
local key, value = getFieldValue(args, field)
if value then
-- Process specific field types
if key == "community" then
value = select(1, CanonicalForms.normalize(value, Config.communityMapping)) or value
elseif key == "languages" then
value = LanguageNormalization.formatLanguages(value)
elseif key == "country" then
-- Each country will get its own globe emoji based on its region
value = MultiCountryDisplay.formatCountries(value)
elseif key == "website" then
value = normalizeWebsites(value)
elseif key == "region" then
-- For now, we just display the region as provided
-- Later we'll auto-derive it from country
value = value
elseif key == "soi" then
value = string.format("[%s Here]", value)
elseif key == "userbox" then
-- Replace user-provided userbox value with achievement box
local username = args.name or ""
local achievementBox = Achievements.renderAchievementBox(username)
-- If there are achievements for this user, use those instead
if achievementBox ~= "" then
value = achievementBox
end
end
table.insert(out, string.format("|-\n| '''%s''':\n| %s", field.label, value))
end
end
return table.concat(out, "\n")
end
--------------------------------------------------------------------------------
-- Category Assignment
--------------------------------------------------------------------------------
local function computeCategories(args)
local cats = {}
-- Community categories
if args.community and args.community ~= "" then
local community = select(1, CanonicalForms.normalize(args.community, Config.communityMapping))
if community then
for _, group in ipairs(Config.communityMapping) do
if group.canonical == community and group.category then
table.insert(cats, group.category)
break
end
end
end
end
-- Country category
if args.country and args.country ~= "" then
local normalizedCountries = MultiCountryDisplay.getCountriesForCategories(args.country)
for _, countryName in ipairs(normalizedCountries) do
table.insert(cats, string.format("[[Category:%s]]", countryName))
end
end
-- Region categories
if args.region and args.region ~= "" then
local regions = splitSemicolonValues(args.region)
for _, region in ipairs(regions) do
local trimmedRegion = region:match("^%s*(.-)%s*$")
if trimmedRegion and trimmedRegion ~= "" then
table.insert(cats, string.format("[[Category:%s]]", trimmedRegion))
end
end
end
return table.concat(cats, "\n")
end
--------------------------------------------------------------------------------
-- Main Render Function
--------------------------------------------------------------------------------
function p.render(frame)
local args = frame:getParent().args or {}
-- Process region information once, early in the template rendering
if args.country and (not args.region or args.region == "") then
-- Auto-derive region from country if not provided
local regions = RegionalMappingICANN.getRegionsForCountries(args.country)
if #regions > 0 then
args.region = table.concat(regions, " and ")
end
elseif args.region and args.region ~= "" then
-- Normalize region names using the dedicated function
local regions = RegionalMappingICANN.normalizeRegions(args.region)
if #regions > 0 then
args.region = table.concat(regions, " and ")
end
end
-- Configure and render template
local config = {
blocks = {
function(a) return renderTitleBlock(a) end,
function(a) return renderPortraitCarousel(a) end,
function(a) return renderFieldsBlock(a) end,
function(a) return socialFooter.render(a) or "" end
}
}
-- Track this page for achievement display cache purging
local pageName = mw.title.getCurrentTitle().fullText
if args.name and args.name ~= "" then
Achievements.trackPage(pageName)
end
return TemplateStructure.render(args, config) .. "\n" .. computeCategories(args)
end
return p