Module:InfoboxCharacterCategories
Documentation for this module may be created at Module:InfoboxCharacterCategories/doc
-- Module:InfoboxCharacterCategories
-- Purpose:
-- 1) Auto-categorize character pages based on *infobox parameters* so editors
-- don't have to remember (or duplicate) manual [[Category:...]] tags.
-- 2) Provide a small formatter to auto-link certain occupation values when
-- the editor writes them as plain text (e.g. "Student" -> [[Student]]).
--
-- Rules (as requested):
-- - residence: ignored (no categories)
-- - affiliation: ONLY linked items produce categories. Plain text is ignored.
-- affiliation = [[Cheer squad]], [[Senior friend group]]
-- -> [[Category:Cheer squad]][[Category:Senior friend group]]
-- - occupation: hardcoded mapping from occupation -> category (pluralization etc.)
-- Student -> Students
-- Burger-Tron employee -> Burger-Tron employees
-- Roseville cinema employee -> Roseville cinema employees
-- School faculty -> School faculty
--
-- Optional:
-- - pass |nocat=yes in the infobox to suppress categorization (useful for sandbox/test pages)
local p = {}
local function trim(s)
if s == nil then return nil end
s = mw.text.trim(s)
if s == "" then return nil end
return s
end
-- Extract link targets like [[Cheer squad]] or [[Cheer squad|Squad]].
-- Returns just the link target ("Cheer squad").
local function extract_link_targets(wikitext)
local targets = {}
if not wikitext or wikitext == "" then return targets end
for target in wikitext:gmatch("%[%[([^%]|#]+)") do
target = trim(target)
if target then
targets[#targets + 1] = target
end
end
return targets
end
-- Split a plain-text list like "Student, Burger-Tron employee"
-- Also handles someone typing a link; we strip links to their target for matching.
local function split_list(wikitext)
local out = {}
if not wikitext or wikitext == "" then return out end
-- Convert any links to their target text for matching.
-- Example: [[Student]] -> Student ; [[Foo|Bar]] -> Foo
local s = wikitext:gsub("%[%[([^%]|#]+)[^%]]*%]%]", "%1")
-- Normalize separators
s = s:gsub("<br%s*/?>", ",")
s = s:gsub("[;\n]", ",")
for part in mw.text.gsplit(s, ",", true) do
part = trim(part)
if part then out[#out + 1] = part end
end
return out
end
-- Hardcoded occupation -> category mapping
local OCCUPATION_TO_CATEGORY = {
["School faculty"] = "School faculty",
["Student"] = "Students",
["Burger-Tron employee"] = "Burger-Tron employees",
["Roseville cinema employee"] = "Roseville cinema employees",
}
local function lookup_occupation_category(occupation)
if not occupation then return nil end
-- Exact match first
if OCCUPATION_TO_CATEGORY[occupation] then
return OCCUPATION_TO_CATEGORY[occupation]
end
-- Case-insensitive match
local lower = mw.ustring.lower(occupation)
for k, v in pairs(OCCUPATION_TO_CATEGORY) do
if mw.ustring.lower(k) == lower then
return v
end
end
return nil
end
-- Formatter used by the infobox display:
-- If the editor wrote "Student" (bare) and it's one of our hardcoded occupations,
-- return [[Student]]. If they already wrote [[Student]], leave it alone.
-- Supports comma-separated lists: "Student, Burger-Tron employee"
function p.formatOccupation(frame)
local raw = frame.args[1] or frame.args.value
raw = trim(raw)
if not raw then return "" end
-- If it already contains a wikilink, don't mess with it.
-- (We still allow bare values mixed with links, but simplest rule wins here.)
if raw:find("%[%[", 1, true) then
return raw
end
local parts = split_list(raw)
if #parts == 0 then return raw end
-- Link only items we recognize in the hardcoded mapping.
-- Unknown occupations remain plain text (avoids accidental redlinks).
for i, part in ipairs(parts) do
if lookup_occupation_category(part) then
parts[i] = ("[[%s]]"):format(part)
end
end
return table.concat(parts, ", ")
end
-- Category emission used at the bottom of the template.
function p.categories(frame)
local args = frame:getParent().args
local nocat = trim(args.nocat)
if nocat and (nocat == "yes" or nocat == "y" or nocat == "true" or nocat == "1") then
return ""
end
local cats = {}
-- Occupation categories (mapped)
local occ = trim(args.occupation)
if occ then
for _, part in ipairs(split_list(occ)) do
local cat = lookup_occupation_category(part)
if cat then
cats[#cats + 1] = ("[[Category:%s]]"):format(cat)
end
end
end
-- Affiliation categories (ONLY linked items)
local aff = trim(args.affiliation)
if aff then
for _, target in ipairs(extract_link_targets(aff)) do
cats[#cats + 1] = ("[[Category:%s]]"):format(target)
end
end
-- De-dupe
local seen, out = {}, {}
for _, c in ipairs(cats) do
if not seen[c] then
seen[c] = true
out[#out + 1] = c
end
end
return table.concat(out)
end
return p