Module:ConfigRepository

Revision as of 01:51, 24 April 2025 by MarkWD (talk | contribs) (HARD FIX)

Documentation for this module may be created at Module:ConfigRepository/doc

--[=[<%-- [PAGE_INFO]
    pageTitle = #Module:ConfigRepository#
[END_PAGE_INFO] --%>]=]

-- Module:ConfigRepository
-- Single source of truth for all template configurations across the ICANNWiki ecosystem
-- Implements stop-gaps:
-- ▸ Hoist singletons (NormalizationDate & CanonicalForms)
-- ▸ Load big constant tables via mw.loadData
--
-- Works with the Template Blueprint Framework to power rendering & semantic processing

local p = {}

-- Stop-gap A: hoist singletons
local DateFmt        = require('Module:NormalizationDate')
local CanonicalForms = require('Module:CanonicalForms')

-- Stop-gap B: load large tables externally
local ClassicTLDs    = mw.loadData('Module:ConfigRepository/Data/ClassicTLDs')

------------------------------------------------------------------------------
-- Global Constants and Properties
------------------------------------------------------------------------------

p.fieldLabels = {
    region  = "Region",
    country = "Country",
    date    = "Date",
    website = "Website",
}

p.dateFormatting = {
    useShortMonthNames = true
}

p.semanticProperties = {
    region   = "Has ICANN region",
    country  = "Has country",
    language = "Knows language",
    person   = "Has person",
}

------------------------------------------------------------------------------
-- TEMPLATE-SPECIFIC CONFIGURATIONS
------------------------------------------------------------------------------
p.templates = {}

------------------------------------------------------------------------------
-- ANCHOR: EVENT TEMPLATE
------------------------------------------------------------------------------

p.templates.Event = {
    meta = { description = "Module for rendering Event templates, including next/previous navigation" },

    categories = { base = {"Events"} },

    patterns = {
        seriesNumber = "^([%w%s]+)%s+(%d+)$",     -- e.g., "ICANN 76"
        seriesYear   = "^([%w%s]+)%s+(%d%d%d%d)$" -- e.g., "IGF 2023"
    },

    fields = {
        {key="logo"    , label="Logo"},
        {key="process" , label="Process"},
        {key="start"   , label="Start Date"},
        {key="end"     , label="End Date"},
        {key="region"  , label=p.fieldLabels.region},
        {keys={"country","territory"}, label=p.fieldLabels.country},
        {key="city"    , label="City"},
        {key="venue"   , label="Venue"},
        {key="organizer", label="Organizer"},
        {keys={"website","url"}, label=p.fieldLabels.website},
        {keys={"subject","category"}, label="Subject"}
    },

    semantics = {
        properties = {
            ["Has start date"]    = "start",
            ["Has end date"]      = "end",
            ["Part of process"]   = "process",
            ["Has city"]          = "city",
            ["Has venue"]         = "venue",
            ["Has event organizer"] = "organizer",
        },

        additionalProperties = {
            ["Has country"] = {"country","territory"},
        },

        transforms = {
            ["Has start date"] = function(value)
                return tostring(DateFmt.formatDate(value))
            end,
            ["Has end date"] = function(value)
                return tostring(DateFmt.formatDate(value))
            end,
        },

        skipProperties = {
            ["Has country"]      = true,
            ["Has ICANN region"] = true,
            ["Has event subject"] = true,
        }
    }
}

------------------------------------------------------------------------------
-- ANCHOR: PERSON TEMPLATE
------------------------------------------------------------------------------

p.templates.Person = {
    meta = { description = "Renders profiles of people with a carousel for multiple images, supporting various normalizations" },

    categories = { base = {"Person"} },

    mappings = {
        community = {
            {canonical="ICANN Community", synonyms={"icann","community"}, category="ICANN Community"},
            {canonical="ICANN Staff"    , synonyms={"staff","icann org"},   category="ICANN Staff"},
            {canonical="Root Server Operator Community", synonyms={"root server operator","rso"}, category="Root Server Operator Community"},
            {canonical="RIR Community"   , synonyms={"rir"},             category="RIR Community"},
            {canonical="Universal Acceptance Community", synonyms={"universal acceptance","ua","ua member","idn","idn community"}, category="Universal Acceptance Community"},
            {canonical="ISOC Community"  , synonyms={"isoc","internet society","internet society community","isoc member"}, category="ISOC Community"},
            {canonical="IETF Community"  , synonyms={"ietf","ietf member"}, category="IETF Community"},
            {canonical="W3C Community"   , synonyms={"w3c","w3c member"}, category="W3C Community"},
            {canonical="IGF Community"   , synonyms={"igf","nri","youth igf"}, category="IGF Community"},
            {canonical="Governmental"    , synonyms={"government"},         category="Governmental"},
            {canonical="Intergovernmental", synonyms={"igo"},              category="Intergovernmental"},
        }
    },

    fields = {
        {key="community"  , label="Community"},
        {key="affiliation", label="ICANN group"},
        {key="organization", label="Organization"},
        {key="region"     , label=p.fieldLabels.region},
        {key="country"    , label=p.fieldLabels.country},
        {key="languages"  , label="Languages"},
        {key="website"    , label=p.fieldLabels.website},
        {key="soi"        , label="SOI"},
        {key="userbox"    , label="Achievements"},
    },

    patterns = {
        itemDelimiter  = ";%s*",
        websitePattern = "^https?://[^%s]+",
    },

    semantics = {
        properties = {
            ["Has governance community"] = "community",
            ["Has ICANN affiliation"]    = "affiliation",
            ["Has organization"]         = "organization",
        },

        additionalProperties = {
            ["Has country"]      = {"country"},
            ["Has ICANN region"] = {"region"},
        },

        transforms = {
            ["Has governance community"] = function(value)
                return select(1, CanonicalForms.normalize(value, p.templates.Person.mappings.community)) or value
            end,
            ["Knows language"] = function(value)
                return value
            end,
        },

        skipProperties = {
            ["Has country"]      = true,
            ["Has ICANN region"] = true,
        }
    }
}

------------------------------------------------------------------------------
-- ANCHOR: TLD TEMPLATE
------------------------------------------------------------------------------

p.templates.TLD = {
    meta = { description = "Versatile module for rendering TLD/ccTLD article templates with extensive normalization and dynamic content" },

    categories = {
        base = {},
        conditional = { rvc="TLDs with RVCs", idn="IDN", idn_cctld="IDN ccTLD" },
    },

    mappings = {
        type = {
            {canonical="gTLD", synonyms={"generic","tld","generic top level domain","generic top-level domain","generic tld"}, category="gTLD"},
            {canonical="ccTLD", synonyms={"country","cc","country code top level domain","country code top-level domain","country tld"}, category="ccTLD"},
        },
        subtype = {
            {canonical="geoTLD", synonyms={"geo tld","geo","geographic","geographical","geographic top level domain","geographic top-level domain","geographic tld"}, css="tld-template-subtype-geotld", category="geoTLD"},
            {canonical="dotBrand", synonyms={"brand","brandtld","brand tld","brand top level domain","brand top-level domain"}, css="tld-template-subtype-brandtld", category="dotBrand"},
            {canonical="Sponsored TLD", synonyms={"sponsored","sponsored top level domain","sponsored top-level domain"}, css="tld-template-subtype-sponsored", category="Sponsored TLD"},
            {canonical="Legacy TLD", synonyms={"legacy","legacy top level domain","legacy top-level domain"}, css="tld-template-subtype-legacytld", category="Legacy TLD"},
            {canonical="2012 gTLD Round", synonyms={"gtld round 2012","2012 ngtld round","2012 ngtld","ngtld 2012","ngtld","2012"}, css="tld-template-subtype-ngtld-round-2012", category="2012 gTLD Round"},
        }
    },

    constants = {
        classicTLDs = ClassicTLDs,
    },

    patterns = {
        tldExtension     = "%.([^%.]+)$",
        countryDelimiter = "([^;]+)",
    },

    fields = {
        {key="type", label="Type"},
        {key="subtype", label="Subtype"},
        {key="status", label="Status"},
        {keys={"country","territory"}, label=p.fieldLabels.country},
        {key="introduced", label="Introduced"},
        {keys={"date","implemented"}, label="Implemented"},
        {keys={"script","language"}, label="Script"},
        {key="translation", label="English version"},
        {key="ascii", label="Punycode"},
        {keys={"registry","registryprovider"}, label="Registry"},
        {key="website", label=p.fieldLabels.website},
        {keys={"RVC","PIC"}, label="PIC/RVC"},
    },

    semantics = {
        properties = {
            ["Has TLD type"]        = "type",
            ["Has TLD subtype"]     = "subtype",
            ["Has TLD status"]      = "status",
            ["Date introduced"]     = "introduced",
            ["Date implemented"]    = "date",
            ["Uses writing script"] = "script",
            ["Has registry operator"] = "registry",
            ["Has PIC or RVC"]      = "RVC",
        },

        additionalProperties = {
            ["Has country"]         = {"country","territory"},
            ["Date implemented"]    = {"date","implemented"},
            ["Uses writing script"] = {"script","language"},
            ["Has registry operator"] = {"registry","registryprovider"},
            ["Has PIC or RVC"]      = {"RVC","PIC"},
        },

        transforms = {
            ["Has TLD type"]     = function(value)
                return select(1, CanonicalForms.normalize(value, p.templates.TLD.mappings.type)) or value
            end,
            ["Has TLD subtype"]  = function(value)
                return select(1, CanonicalForms.normalize(value, p.templates.TLD.mappings.subtype)) or value
            end,
            ["Date introduced"]  = function(value)
                return tostring(DateFmt.formatDate(value))
            end,
            ["Date implemented"] = function(value)
                return tostring(DateFmt.formatDate(value))
            end,
            ["Has PIC or RVC"]   = function(value)
                if value and value ~= "" then return "true" end
            end
        },

        skipProperties = {
            ["Has country"] = true
        }
    }
}

------------------------------------------------------------------------------
-- ANCHOR: LIBRARY INTERVIEW TEMPLATE
------------------------------------------------------------------------------

p.templates.LibraryInterview = {
    meta = { description = "Module for rendering the Library Interview template with semantics" },

    categories = {
        base = { "Internet & Digital Governance Library", "ICANNWiki Interviews" }
    },

    constants = {
        title      = "Internet & Digital Governance Library",
        tableClass = "library-box"
    },

    fields = {
        { key="Title"      , label="Title" },
        { key="Format"     , label="Format" },
        { key="Date"       , label=p.fieldLabels.date },
        { key="Interviewer", label="Interviewer", autoWikiLink=true },
        { key="Interviewee", label="Interviewee", autoWikiLink=true },
        { key="ID"         , label="Permanent ID" },
    },

    semantics = {
        properties = {
            ["Has interview format"] = "Format",
            ["Has date"]             = "Date",
            ["Has interviewer"]      = "Interviewer",
            ["Has interviewee"]      = "Interviewee",
            ["Permanent ID"]         = "ID",
        },

        additionalProperties = {
            ["Has person"] = {"Interviewer","Interviewee"},
        },

        transforms = {
            ["Has date"]     = function(value) return tostring(DateFmt.formatDate(value)) end,
            ["Permanent ID"] = function(value) return tostring(value or "") end,
        },
    }
}

------------------------------------------------------------------------------
-- CONFIGURATION ACCESS FUNCTIONS
------------------------------------------------------------------------------

function p.getConfig(templateType)
    return p.templates[templateType] or {}
end

function p.createStandardConfig(config)
    config = config or {}
    return {
        meta       = config.meta       or { description = "Template module configuration" },
        mappings   = config.mappings   or {},
        fields     = config.fields     or {},
        semantics  = config.semantics  or { properties={}, transforms={}, additionalProperties={} },
        constants  = config.constants  or {},
        patterns   = config.patterns   or {},
        categories = config.categories or {},
    }
end

function p.getStandardConfig(templateType, customOverrides)
    return p.createStandardConfig(p.getConfig(templateType), customOverrides)
end

------------------------------------------------------------------------------
mw.log('ConfigRepository loaded; heap '..collectgarbage('count')..' kB')
return p