Module:InfoboxCharacterCategories: Difference between revisions

From Candypedia
Making the module
 
Tweaking how it outputs links in the infobox
 
Line 2: Line 2:
-- Purpose:
-- Purpose:
--  1) Auto-categorize character pages based on *infobox parameters* so editors
--  1) Auto-categorize character pages based on *infobox parameters* so editors
--      don't have to remember (or duplicate) manual [[Category:...]] tags.
--      don't have to duplicate manual [[Category:...]] tags at the bottom.
--  2) Provide a small formatter to auto-link certain occupation values when
--  2) Format selected infobox fields so the *displayed* values link to the
--      the editor writes them as plain text (e.g. "Student" -> [[Student]]).
--      relevant category pages (not to nonexistent article pages).
--
--
-- Rules (as requested):
-- Rules (per your requests):
--  - residence: ignored (no categories)
--  - residence: ignored (no categories, no special formatting)
--  - affiliation: ONLY linked items produce categories. Plain text is ignored.
--  - affiliation: ONLY linked items become categories. Plain text is ignored.
--      affiliation = [[Cheer squad]], [[Senior friend group]]
--      affiliation = [[Cheer squad]], [[Senior friend group]]
--        -> [[Category:Cheer squad]][[Category:Senior friend group]]
--        -> page categories: [[Category:Cheer squad]][[Category:Senior friend group]]
--  - occupation: hardcoded mapping from occupation -> category (pluralization etc.)
--        -> infobox display: links point to [[:Category:Cheer squad]] etc.
--      Student -> Students
--  - occupation: hardcoded mapping from occupation -> category (for pluralization).
--      Burger-Tron employee -> Burger-Tron employees
--      Student -> Category:Students
--      Roseville cinema employee -> Roseville cinema employees
--      Burger-Tron employee -> Category:Burger-Tron employees
--      School faculty -> School faculty
--      Roseville cinema employee -> Category:Roseville cinema employees
--      School faculty -> Category:School faculty
--    Works whether the editor wrote Student bare or as [[Student]].
--
--
-- Optional:
-- Optional:
Line 27: Line 29:
if s == "" then return nil end
if s == "" then return nil end
return s
return s
end
-- Build a link to a category page without categorizing the current page.
-- Example: [[:Category:Students|Student]]
local function catlink(cat, label)
cat = trim(cat)
label = trim(label) or cat
if not cat then return "" end
return ("[[:Category:%s|%s]]"):format(cat, label)
end
end


Line 44: Line 55:
end
end


-- Split a plain-text list like "Student, Burger-Tron employee"
-- Split a list like "Student, Burger-Tron employee"
-- Also handles someone typing a link; we strip links to their target for matching.
-- Also handles someone typing a link; we strip links to their target for matching.
local function split_list(wikitext)
local function split_list(wikitext)
Line 74: Line 85:


local function lookup_occupation_category(occupation)
local function lookup_occupation_category(occupation)
occupation = trim(occupation)
if not occupation then return nil end
if not occupation then return nil end
-- Exact match first
-- Exact match first
if OCCUPATION_TO_CATEGORY[occupation] then
if OCCUPATION_TO_CATEGORY[occupation] then
return OCCUPATION_TO_CATEGORY[occupation]
return OCCUPATION_TO_CATEGORY[occupation]
end
end
-- Case-insensitive match
-- Case-insensitive match
local lower = mw.ustring.lower(occupation)
local lower = mw.ustring.lower(occupation)
Line 89: Line 103:
end
end


-- Formatter used by the infobox display:
-- Formatter used by the infobox display for occupation.
-- If the editor wrote "Student" (bare) and it's one of our hardcoded occupations,
-- Always links recognized occupations to their CATEGORY pages.
-- return [[Student]]. If they already wrote [[Student]], leave it alone.
-- Examples:
-- Supports comma-separated lists: "Student, Burger-Tron employee"
--   "Student" -> [[:Category:Students|Student]]
--   "[[Student]]" -> [[:Category:Students|Student]]
--   "Student, Burger-Tron employee" -> both linked to their categories
function p.formatOccupation(frame)
function p.formatOccupation(frame)
local raw = frame.args[1] or frame.args.value
local raw = frame.args[1] or frame.args.value
raw = trim(raw)
raw = trim(raw)
if not raw then return "" end
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)
local parts = split_list(raw)
if #parts == 0 then return raw end
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
for i, part in ipairs(parts) do
if lookup_occupation_category(part) then
local cat = lookup_occupation_category(part)
parts[i] = ("[[%s]]"):format(part)
if cat then
-- Display singular label, link to plural category.
parts[i] = catlink(cat, part)
else
parts[i] = part
end
end
end
end
Line 118: Line 130:
end
end


-- Category emission used at the bottom of the template.
-- Formatter used by the infobox display for affiliation.
-- Converts any wikilinks into links to the corresponding CATEGORY page:
--  [[Cheer squad]] -> [[:Category:Cheer squad|Cheer squad]]
--  [[Cheer squad|Squad]] -> [[:Category:Cheer squad|Squad]]
-- Plain text remains unchanged.
function p.formatAffiliation(frame)
local raw = frame.args[1] or frame.args.value
raw = trim(raw)
if not raw then return "" end
 
-- Replace [[Target|Label]] first
raw = raw:gsub("%[%[([^%]|#]+)%|([^%]]+)%]%]", function(target, label)
target = trim(target)
label = trim(label)
if not target or not label then return "" end
return catlink(target, label)
end)
 
-- Replace [[Target]]
raw = raw:gsub("%[%[([^%]|#]+)%]%]", function(target)
target = trim(target)
if not target then return "" end
return catlink(target, target)
end)
 
return raw
end
 
-- Category emission used at the bottom of the infobox template.
function p.categories(frame)
function p.categories(frame)
local args = frame:getParent().args
local args = frame:getParent().args

Latest revision as of 02:25, 5 January 2026

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 duplicate manual [[Category:...]] tags at the bottom.
--   2) Format selected infobox fields so the *displayed* values link to the
--      relevant category pages (not to nonexistent article pages).
--
-- Rules (per your requests):
--   - residence: ignored (no categories, no special formatting)
--   - affiliation: ONLY linked items become categories. Plain text is ignored.
--       affiliation = [[Cheer squad]], [[Senior friend group]]
--         -> page categories: [[Category:Cheer squad]][[Category:Senior friend group]]
--         -> infobox display: links point to [[:Category:Cheer squad]] etc.
--   - occupation: hardcoded mapping from occupation -> category (for pluralization).
--       Student -> Category:Students
--       Burger-Tron employee -> Category:Burger-Tron employees
--       Roseville cinema employee -> Category:Roseville cinema employees
--       School faculty -> Category:School faculty
--     Works whether the editor wrote Student bare or as [[Student]].
--
-- 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

-- Build a link to a category page without categorizing the current page.
-- Example: [[:Category:Students|Student]]
local function catlink(cat, label)
	cat = trim(cat)
	label = trim(label) or cat
	if not cat then return "" end
	return ("[[:Category:%s|%s]]"):format(cat, label)
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 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)
	occupation = trim(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 for occupation.
-- Always links recognized occupations to their CATEGORY pages.
-- Examples:
--   "Student" -> [[:Category:Students|Student]]
--   "[[Student]]" -> [[:Category:Students|Student]]
--   "Student, Burger-Tron employee" -> both linked to their categories
function p.formatOccupation(frame)
	local raw = frame.args[1] or frame.args.value
	raw = trim(raw)
	if not raw then return "" end

	local parts = split_list(raw)
	if #parts == 0 then return raw end

	for i, part in ipairs(parts) do
		local cat = lookup_occupation_category(part)
		if cat then
			-- Display singular label, link to plural category.
			parts[i] = catlink(cat, part)
		else
			parts[i] = part
		end
	end

	return table.concat(parts, ", ")
end

-- Formatter used by the infobox display for affiliation.
-- Converts any wikilinks into links to the corresponding CATEGORY page:
--   [[Cheer squad]] -> [[:Category:Cheer squad|Cheer squad]]
--   [[Cheer squad|Squad]] -> [[:Category:Cheer squad|Squad]]
-- Plain text remains unchanged.
function p.formatAffiliation(frame)
	local raw = frame.args[1] or frame.args.value
	raw = trim(raw)
	if not raw then return "" end

	-- Replace [[Target|Label]] first
	raw = raw:gsub("%[%[([^%]|#]+)%|([^%]]+)%]%]", function(target, label)
		target = trim(target)
		label = trim(label)
		if not target or not label then return "" end
		return catlink(target, label)
	end)

	-- Replace [[Target]]
	raw = raw:gsub("%[%[([^%]|#]+)%]%]", function(target)
		target = trim(target)
		if not target then return "" end
		return catlink(target, target)
	end)

	return raw
end

-- Category emission used at the bottom of the infobox 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