Jump to content

Module:ConfigRepository: Difference between revisions

HARD FIX
Tag: Reverted
No edit summary
Tag: Reverted
Line 1: Line 1:
--[=[<%-- [PAGE_INFO]
--[=[<%-- [PAGE_INFO]
     pageTitle = #Module:ConfigRepository#
     pageTitle = #Module:ConfigRepository#
[END_PAGE_INFO] --%>]=]
[END_PAGE_INFO] --%>--]=]


-- Module:ConfigRepository
-- Module:ConfigRepository
-- Single source of truth for all template configurations across the ICANNWiki ecosystem
-- 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
-- This module centralizes configuration data to ensure consistency, eliminate duplication, and provide a standardized structure for all templates. It works in conjunction with the Template Blueprint Framework to power the rendering and semantic processing
--
-- This module consists of three main parts:
-- 1. Global constants and properties shared across templates
-- 2. Template-specific configurations with standardized structure
-- 3. Configuration access functions for retrieving and standardizing configurations
--
-- Each template configuration provides a standardized structure with the following components:
--
-- meta:        Template metadata including description and rendering options
-- categories:  Base and conditional categories for the template
-- patterns:    Regular expression patterns used by the template for validation and parsing
-- fields:      Maps template field names in article source to display labels
-- mappings:    Canonical mappings for normalizing user input values
-- constants:  Template-specific constant values
-- semantics:  Semantic MediaWiki integration with the following sub-components:
--  properties:          Maps semantic properties to template fields (1:1 mapping)
--  additionalProperties: Maps semantic properties to multiple possible template fields
--  transforms:          Functions to transform field values before storing as semantic properties
--  skipProperties:      Properties to exclude from automatic processing


local p = {}
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')


------------------------------------------------------------------------------
------------------------------------------------------------------------------
Line 24: Line 33:
------------------------------------------------------------------------------
------------------------------------------------------------------------------


-- Global field labels used across multiple templates
-- These provide consistent labeling for common fields across all templates
-- and ensure that changes to label text only need to be made in one place
p.fieldLabels = {
p.fieldLabels = {
     region = "Region",
    -- Geographic information
     region = "Region",
     country = "Country",
     country = "Country",
     date   = "Date",
   
     website = "Website",
    -- Temporal information
     date = "Date",
   
    -- Contact information
     website = "Website"
    -- Add other cross-template field labels as needed
}
}


-- Global date formatting configuration
-- Controls how dates are displayed throughout the wiki's templates
p.dateFormatting = {
p.dateFormatting = {
     useShortMonthNames = true
     useShortMonthNames = true -- When true, use "Apr. 21, 2025" format; when false, use "April 21, 2025"
}
}


-- Global semantic property names used across multiple templates
-- These properties connect data across different template types
-- and enable cross-template semantic queries
p.semanticProperties = {
p.semanticProperties = {
     region   = "Has ICANN region",
    -- Geographic properties
     country = "Has country",
     region = "Has ICANN region",
     country = "Has country",
   
    -- Person-related properties
     language = "Knows language",
     language = "Knows language",
     person   = "Has person",
     person = "Has person"
    -- Add other cross-template properties as needed
}
}


Line 45: Line 72:
-- TEMPLATE-SPECIFIC CONFIGURATIONS
-- TEMPLATE-SPECIFIC CONFIGURATIONS
------------------------------------------------------------------------------
------------------------------------------------------------------------------
p.templates = {}
p.templates = {


------------------------------------------------------------------------------
------------------------------------------------------------------------------
Line 51: Line 78:
------------------------------------------------------------------------------
------------------------------------------------------------------------------


p.templates.Event = {
    Event = {
    meta = { description = "Module for rendering Event templates, including next/previous navigation" },
        meta = {
 
            description = "Module for rendering Event templates, including next/previous navigation"
    categories = { base = {"Events"} },
        },
 
          
    patterns = {
         categories = { -- Default categories
         seriesNumber = "^([%w%s]+)%s+(%d+)$",    -- e.g., "ICANN 76"
            base = {"Events"}
         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 = {
         patterns = {
             ["Has country"] = {"country","territory"},
             -- Patterns for event navigation detection
            seriesNumber = "^([%w%s]+)%s+(%d+)$",    -- e.g., "ICANN 76"
            seriesYear = "^([%w%s]+)%s+(%d%d%d%d)$" -- e.g., "IGF 2023"
         },
         },
 
       
         transforms = {
         fields = {
             ["Has start date"] = function(value)
            {key="logo", label="Logo"},
                return tostring(DateFmt.formatDate(value))
            {key="process", label="Process"},
             end,
             {key="start", label="Start Date"},
             ["Has end date"] = function(value)
            {key="end", label="End Date"},
                return tostring(DateFmt.formatDate(value))
            {key="region", label=p.fieldLabels.region},
             end,
             {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"}
         },
         },
 
       
         skipProperties = {
         semantics = {
            ["Has country"]     = true,
            properties = {
            ["Has ICANN region"] = true,
                ["Has start date"] = "start",
            ["Has event subject"] = true,
                ["Has end date"] = "end",
                ["Part of process"] = "process",
                -- "Has country" and "Has ICANN region" handled by CountryData.addCountrySemanticProperties
                ["Has city"] = "city",
                ["Has venue"] = "venue",
                ["Has event organizer"] = "organizer"
                -- "Has event subject"
            },
           
            additionalProperties = {
                ["Has country"] = {"country", "territory"}
                -- REVIEW "Has event subject" UNUSED
            },
           
            transforms = {
                ["Has start date"] = function(value)
                    return tostring(require('Module:NormalizationDate').formatDate(value))
                end,
                ["Has end date"] = function(value)
                    return tostring(require('Module:NormalizationDate').formatDate(value))
                end
            },
           
            skipProperties = { -- Handled separately
                ["Has country"] = true,
                ["Has ICANN region"] = true,
                ["Has event subject"] = true -- REVIEW: UNUSED
            }
         }
         }
     }
     },
}
   
 
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- ANCHOR: PERSON TEMPLATE
-- ANCHOR: PERSON TEMPLATE
------------------------------------------------------------------------------
------------------------------------------------------------------------------


p.templates.Person = {
    Person = {
    meta = { description = "Renders profiles of people with a carousel for multiple images, supporting various normalizations" },
        meta = {
 
            description = "Renders profiles of people with a carousel for multiple images, supporting various normalizations"
    categories = { base = {"Person"} },
        },
 
       
    mappings = {
        categories = { -- Default categories
        community = {
            base = {"Person"}
            {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"},
        mappings = {
            {canonical="RIR Community"   , synonyms={"rir"},             category="RIR Community"},
            -- Community mappings for normalizing user input to canonical forms
            {canonical="Universal Acceptance Community", synonyms={"universal acceptance","ua","ua member","idn","idn community"}, category="Universal Acceptance Community"},
            -- Each entry maps various synonyms to a canonical form and associated category
            {canonical="ISOC Community" , synonyms={"isoc","internet society","internet society community","isoc member"}, category="ISOC Community"},
            community = {
            {canonical="IETF Community" , synonyms={"ietf","ietf member"}, category="IETF Community"},
                -- ICANN-related communities
            {canonical="W3C Community"   , synonyms={"w3c","w3c member"}, category="W3C Community"},
                {canonical = "ICANN Community",
            {canonical="IGF Community"   , synonyms={"igf","nri","youth igf"}, category="IGF Community"},
                synonyms = {"icann", "community"},
            {canonical="Governmental"   , synonyms={"government"},         category="Governmental"},
                category = "ICANN Community"},
            {canonical="Intergovernmental", synonyms={"igo"},             category="Intergovernmental"},
                {canonical = "ICANN Staff",
        }
                synonyms = {"staff", "icann org"},
    },
                category = "ICANN Staff"},
 
               
    fields = {
                -- Technical communities
        {key="community"  , label="Community"},
                {canonical = "Root Server Operator Community",
        {key="affiliation", label="ICANN group"},
                synonyms = {"root server operator", "rso"},
        {key="organization", label="Organization"},
                category = "Root Server Operator Community"},
        {key="region"    , label=p.fieldLabels.region},
                {canonical = "RIR Community",
        {key="country"    , label=p.fieldLabels.country},
                synonyms = {"rir"},
        {key="languages"  , label="Languages"},
                category = "RIR Community"},
        {key="website"    , label=p.fieldLabels.website},
                {canonical = "Universal Acceptance Community",
        {key="soi"        , label="SOI"},
                synonyms = {"universal acceptance", "ua", "ua member", "idn", "idn community"},
        {key="userbox"    , label="Achievements"},
                category = "Universal Acceptance Community"},
    },
               
 
                -- Internet governance organizations
    patterns = {
                {canonical = "ISOC Community",
        itemDelimiter  = ";%s*",
                synonyms = {"isoc", "internet society", "internet society community", "isoc member"},
        websitePattern = "^https?://[^%s]+",
                category = "ISOC Community"},
    },
                {canonical = "IETF Community",
 
                synonyms = {"ietf", "ietf member"},
    semantics = {
                category = "IETF Community"},
        properties = {
                {canonical = "W3C Community",
            ["Has governance community"] = "community",
                synonyms = {"w3c", "w3c member"},
            ["Has ICANN affiliation"]    = "affiliation",
                category = "W3C Community"},
            ["Has organization"]        = "organization",
                {canonical = "IGF Community",
                synonyms = {"igf", "nri", "youth igf"},
                category = "IGF Community"},
               
                -- Government-related
                {canonical = "Governmental",
                synonyms = {"government"},
                category = "Governmental"},
                {canonical = "Intergovernmental",
                synonyms = {"igo"},
                category = "Intergovernmental"}
            }
         },
         },
 
       
         additionalProperties = {
         fields = {
             ["Has country"]     = {"country"},
            {key="community",  label="Community"},
             ["Has ICANN region"] = {"region"},
            {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"}
         },
         },
 
       
         transforms = {
         patterns = {
             ["Has governance community"] = function(value)
             itemDelimiter = ";%s*", -- Matches semicolon followed by optional whitespace; used for splitting multi-value fields
                return select(1, CanonicalForms.normalize(value, p.templates.Person.mappings.community)) or value
             websitePattern = "^https?://[^%s]+" -- Matches URLs starting with http:// or https:// followed by non-whitespace chars
             end,
            ["Knows language"] = function(value)
                return value
            end,
         },
         },
 
       
         skipProperties = {
         semantics = {
            ["Has country"]     = true,
            properties = {
            ["Has ICANN region"] = true,
                ["Has governance community"] = "community",
                ["Has ICANN affiliation"] = "affiliation",
                ["Has organization"] = "organization",
                -- "Has ICANN region", "Has country", "Knows language", are all handled separately
            },
           
            additionalProperties = {
                -- Handle multiple countries and regions in the special case handlers
                ["Has country"] = {"country"},
                ["Has ICANN region"] = {"region"}
            },
           
            transforms = {
                ["Has governance community"] = function(value)
                    local CanonicalForms = require('Module:CanonicalForms')
                    return select(1, CanonicalForms.normalize(value, p.templates.Person.mappings.community)) or value
                end,
                ["Knows language"] = function(value)
                    -- Return raw value, the semantic property should store the raw data
                    return value
                end
            },
           
            skipProperties = { -- Handled separately
                ["Has country"] = true,
                ["Has ICANN region"] = true
            }
         }
         }
     }
     },
}
   
 
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- ANCHOR: TLD TEMPLATE
-- ANCHOR: TLD TEMPLATE
------------------------------------------------------------------------------
------------------------------------------------------------------------------


p.templates.TLD = {
    TLD = {
    meta = { description = "Versatile module for rendering TLD/ccTLD article templates with extensive normalization and dynamic content" },
        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" },
        categories = {
    },
            base = {}, -- No default category for TLDs as they use type-based categories
 
            conditional = {
    mappings = {
                rvc = "TLDs with RVCs",
        type = {
                idn = "IDN",
            {canonical="gTLD", synonyms={"generic","tld","generic top level domain","generic top-level domain","generic tld"}, category="gTLD"},
                idn_cctld = "IDN ccTLD"
            {canonical="ccTLD", synonyms={"country","cc","country code top level domain","country code top-level domain","country tld"}, category="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"}
            }
         },
         },
         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"},
        -- This list defines legacy TLDs that are not integrated into the nTLDStats database
            {canonical="dotBrand", synonyms={"brand","brandtld","brand tld","brand top level domain","brand top-level domain"}, css="tld-template-subtype-brandtld", category="dotBrand"},
        constants = {
             {canonical="Sponsored TLD", synonyms={"sponsored","sponsored top level domain","sponsored top-level domain"}, css="tld-template-subtype-sponsored", category="Sponsored TLD"},
             classicTLDs = {
            {canonical="Legacy TLD", synonyms={"legacy","legacy top level domain","legacy top-level domain"}, css="tld-template-subtype-legacytld", category="Legacy TLD"},
                -- Generic TLDs
            {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"},
                com=true, net=true, org=true, info=true,
        }
                -- Restricted TLDs
    },
                edu=true, gov=true, mil=true, int=true,
 
                -- Sponsored TLDs
    constants = {
                aero=true, asia=true, cat=true, coop=true,  
        classicTLDs = ClassicTLDs,
                jobs=true, mobi=true, museum=true, post=true,  
    },
                tel=true, travel=true, xxx=true
 
            }
    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 = {
         patterns = {
             ["Has country"]        = {"country","territory"},
             tldExtension="%.([^%.]+)$", -- Extracts the TLD part after the last dot (e.g., "com" from "example.com")
            ["Date implemented"]   = {"date","implemented"},
             countryDelimiter="([^;]+)"   -- Matches any sequence of characters not containing semicolons; used for country parsing
            ["Uses writing script"] = {"script","language"},
             ["Has registry operator"] = {"registry","registryprovider"},
            ["Has PIC or RVC"]     = {"RVC","PIC"},
         },
         },
 
       
         transforms = {
         fields = {
             ["Has TLD type"]    = function(value)
             {key="type",label="Type"},
                return select(1, CanonicalForms.normalize(value, p.templates.TLD.mappings.type)) or value
            {key="subtype",label="Subtype"},
             end,
             {key="status",label="Status"},
             ["Has TLD subtype"]  = function(value)
             {keys={"country", "territory"},label=p.fieldLabels.country},
                return select(1, CanonicalForms.normalize(value, p.templates.TLD.mappings.subtype)) or value
             {key="introduced",label="Introduced"},
             end,
             {keys={"date", "implemented"},label="Implemented"},
             ["Date introduced"= function(value)
            {keys={"script", "language"},label="Script"},
                return tostring(DateFmt.formatDate(value))
             {key="translation",label="English version"},
             end,
             {key="ascii",label="Punycode"},
             ["Date implemented"] = function(value)
             {keys={"registry", "registryprovider"},label="Registry"},
                return tostring(DateFmt.formatDate(value))
             {key="website",label=p.fieldLabels.website},
             end,
            {keys={"RVC", "PIC"},label="PIC/RVC"}
             ["Has PIC or RVC"= function(value)
                if value and value ~= "" then return "true" end
            end
         },
         },
 
       
         skipProperties = {
         semantics = {
            ["Has country"] = true
            properties = {
                ["Has TLD type"] = "type",
                ["Has TLD subtype"] = "subtype",
                ["Has TLD status"] = "status",
                -- "Has country" is handled separately through addMultiCountrySemanticProperties
                ["Date introduced"] = "introduced",
                ["Date implemented"] = "date", -- Will also handle implemented
                ["Uses writing script"] = "script", -- Will also handle language
                ["Has registry operator"] = "registry", -- Will also handle registryprovider
                ["Has PIC or RVC"] = "RVC" -- Will also handle PIC
                -- "Is IDN" is handled separately as a boolean property
            },
           
            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)
                    local CanonicalForms = require('Module:CanonicalForms')
                    return select(1, CanonicalForms.normalize(value, p.templates.TLD.mappings.type)) or value
                end,
                ["Has TLD subtype"] = function(value)
                    local CanonicalForms = require('Module:CanonicalForms')
                    return select(1, CanonicalForms.normalize(value, p.templates.TLD.mappings.subtype)) or value
                end,
                ["Date introduced"] = function(value)
                    return tostring(require('Module:NormalizationDate').formatDate(value))
                end,
                ["Date implemented"] = function(value)
                    return tostring(require('Module:NormalizationDate').formatDate(value))
                end,
                ["Has PIC or RVC"] = function(value)
                    -- If value exists and is not empty, return "true"
                    if value and value ~= "" then
                        return "true"
                    end
                    return nil -- Return nil for empty values
                end
            },
           
            skipProperties = { -- Handled separately
                ["Has country"] = true
            }
         }
         }
     }
     },
}
   
 
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- ANCHOR: LIBRARY INTERVIEW TEMPLATE
-- ANCHOR: LIBRARY INTERVIEW TEMPLATE
------------------------------------------------------------------------------
------------------------------------------------------------------------------


p.templates.LibraryInterview = {
    LibraryInterview = {
    meta = { description = "Module for rendering the Library Interview template with semantics" },
        meta = {
 
            description = "Module for rendering the Library Interview template with semantics"
    categories = {
        },
        base = { "Internet & Digital Governance Library", "ICANNWiki Interviews" }
       
    },
        categories = { -- Default categories
 
            base = {
    constants = {
                "Internet & Digital Governance Library",
        title      = "Internet & Digital Governance Library",
                "ICANNWiki Interviews"
        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 = {
         constants = {
             ["Has person"] = {"Interviewer","Interviewee"},
             title = "Internet & Digital Governance Library",
            tableClass = "library-box"
         },
         },
 
       
         transforms = {
         fields = {
             ["Has date"]    = function(value) return tostring(DateFmt.formatDate(value)) end,
             {key="Title", label="Title"},
             ["Permanent ID"] = function(value) return tostring(value or "") end,
            {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 = {
            -- Direct property mappings (1:1 relationship between property and field)
            properties = {
                ["Has interview format"] = "Format",
                ["Has date"] = "Date",
                ["Has interviewer"] = "Interviewer",
                ["Has interviewee"] = "Interviewee",
                ["Permanent ID"] = "ID"
            },
           
            -- Properties that map to multiple possible fields
            additionalProperties = {
                ["Has person"] = {"Interviewer", "Interviewee"} -- Links interviews to person pages
            },
           
            -- Transform functions to standardize property values
            transforms = {
                ["Has date"] = function(value)
                    return tostring(require('Module:NormalizationDate').formatDate(value))
                end,
                ["Permanent ID"] = function(value)
                    -- Simply return the value as is, ensuring it's a string
                    return tostring(value or "")
                end
            },
           
            skipProperties = {
                -- No properties to skip
            }
        }
     }
     }
}
}
Line 319: Line 454:
------------------------------------------------------------------------------
------------------------------------------------------------------------------


-- Get configuration for a specific template type
function p.getConfig(templateType)
function p.getConfig(templateType)
     return p.templates[templateType] or {}
     return p.templates[templateType] or {}
end
end


-- Creates a standardized configuration structure for template modules
function p.createStandardConfig(config)
function p.createStandardConfig(config)
     config = config or {}
     config = config or {}
     return {
      
         meta       = config.meta       or { description = "Template module configuration" },
    -- Initialize with defaults
         mappings   = config.mappings   or {},
    local standardConfig = {
         fields     = config.fields     or {},
         meta = config.meta or {
         semantics = config.semantics or { properties={}, transforms={}, additionalProperties={} },
            description = "Template module configuration"
         constants = config.constants or {},
        },
         patterns   = config.patterns   or {},
         mappings = config.mappings or {},
         categories = config.categories or {},
         fields = config.fields or {},
         semantics = config.semantics or {
            properties = {},
            transforms = {},
            additionalProperties = {}
        },
         constants = config.constants or {},
         patterns = config.patterns or {},
         categories = config.categories or {} -- Add categories field to preserve base categories
     }
     }
   
    return standardConfig
end
end


-- Get a standard config object for use with existing template modules
function p.getStandardConfig(templateType, customOverrides)
function p.getStandardConfig(templateType, customOverrides)
     return p.createStandardConfig(p.getConfig(templateType), customOverrides)
     -- Get the base configuration
    local baseConfig = p.getConfig(templateType)
   
    -- Create a standard config
    return p.createStandardConfig(baseConfig, customOverrides)
end
end



Revision as of 02:48, 24 April 2025

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
--
-- This module centralizes configuration data to ensure consistency, eliminate duplication, and provide a standardized structure for all templates. It works in conjunction with the Template Blueprint Framework to power the rendering and semantic processing
--
-- This module consists of three main parts:
-- 1. Global constants and properties shared across templates
-- 2. Template-specific configurations with standardized structure
-- 3. Configuration access functions for retrieving and standardizing configurations
--
-- Each template configuration provides a standardized structure with the following components:
--
-- meta:        Template metadata including description and rendering options
-- categories:  Base and conditional categories for the template
-- patterns:    Regular expression patterns used by the template for validation and parsing
-- fields:      Maps template field names in article source to display labels
-- mappings:    Canonical mappings for normalizing user input values
-- constants:   Template-specific constant values
-- semantics:   Semantic MediaWiki integration with the following sub-components:
--   properties:           Maps semantic properties to template fields (1:1 mapping)
--   additionalProperties: Maps semantic properties to multiple possible template fields
--   transforms:           Functions to transform field values before storing as semantic properties
--   skipProperties:       Properties to exclude from automatic processing

local p = {}

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

-- Global field labels used across multiple templates
-- These provide consistent labeling for common fields across all templates
-- and ensure that changes to label text only need to be made in one place
p.fieldLabels = {
    -- Geographic information
    region = "Region",
    country = "Country",
    
    -- Temporal information
    date = "Date",
    
    -- Contact information
    website = "Website"
    -- Add other cross-template field labels as needed
}

-- Global date formatting configuration
-- Controls how dates are displayed throughout the wiki's templates
p.dateFormatting = {
    useShortMonthNames = true -- When true, use "Apr. 21, 2025" format; when false, use "April 21, 2025"
}

-- Global semantic property names used across multiple templates
-- These properties connect data across different template types
-- and enable cross-template semantic queries
p.semanticProperties = {
    -- Geographic properties
    region = "Has ICANN region",
    country = "Has country",
    
    -- Person-related properties
    language = "Knows language",
    person = "Has person"
    -- Add other cross-template properties as needed
}

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

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

    Event = {
        meta = {
            description = "Module for rendering Event templates, including next/previous navigation"
        },
        
        categories = { -- Default categories
            base = {"Events"}
        },

        patterns = {
            -- Patterns for event navigation detection
            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 country" and "Has ICANN region" handled by CountryData.addCountrySemanticProperties
                ["Has city"] = "city",
                ["Has venue"] = "venue",
                ["Has event organizer"] = "organizer"
                -- "Has event subject"
            },
            
            additionalProperties = {
                ["Has country"] = {"country", "territory"}
                -- REVIEW "Has event subject" UNUSED
            },
            
            transforms = {
                ["Has start date"] = function(value)
                    return tostring(require('Module:NormalizationDate').formatDate(value))
                end,
                ["Has end date"] = function(value)
                    return tostring(require('Module:NormalizationDate').formatDate(value))
                end
            },
            
            skipProperties = { -- Handled separately
                ["Has country"] = true,  
                ["Has ICANN region"] = true,
                ["Has event subject"] = true  -- REVIEW: UNUSED
            }
        }
    },
    
------------------------------------------------------------------------------
-- ANCHOR: PERSON TEMPLATE
------------------------------------------------------------------------------

    Person = {
        meta = {
            description = "Renders profiles of people with a carousel for multiple images, supporting various normalizations"
        },
        
        categories = { -- Default categories
            base = {"Person"}
        },
        
        mappings = {
            -- Community mappings for normalizing user input to canonical forms
            -- Each entry maps various synonyms to a canonical form and associated category
            community = {
                -- ICANN-related communities
                {canonical = "ICANN Community",
                 synonyms = {"icann", "community"},
                 category = "ICANN Community"},
                {canonical = "ICANN Staff",
                 synonyms = {"staff", "icann org"},
                 category = "ICANN Staff"},
                
                -- Technical communities
                {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"},
                
                -- Internet governance organizations
                {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"},
                
                -- Government-related
                {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*",  -- Matches semicolon followed by optional whitespace; used for splitting multi-value fields
            websitePattern = "^https?://[^%s]+"  -- Matches URLs starting with http:// or https:// followed by non-whitespace chars
        },
        
        semantics = {
            properties = {
                ["Has governance community"] = "community",
                ["Has ICANN affiliation"] = "affiliation",
                ["Has organization"] = "organization",
                -- "Has ICANN region", "Has country", "Knows language", are all handled separately
            },
            
            additionalProperties = {
                -- Handle multiple countries and regions in the special case handlers
                ["Has country"] = {"country"},
                ["Has ICANN region"] = {"region"}
            },
            
            transforms = {
                ["Has governance community"] = function(value)
                    local CanonicalForms = require('Module:CanonicalForms')
                    return select(1, CanonicalForms.normalize(value, p.templates.Person.mappings.community)) or value
                end,
                ["Knows language"] = function(value)
                    -- Return raw value, the semantic property should store the raw data
                    return value
                end
            },
            
            skipProperties = { -- Handled separately
                ["Has country"] = true,
                ["Has ICANN region"] = true
            }
        }
    },
    
------------------------------------------------------------------------------
-- ANCHOR: TLD TEMPLATE
------------------------------------------------------------------------------

    TLD = {
        meta = {
            description = "Versatile module for rendering TLD/ccTLD article templates with extensive normalization and dynamic content"
        },
        
        categories = {
            base = {}, -- No default category for TLDs as they use type-based categories
            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"}
            }
        },
        
        -- This list defines legacy TLDs that are not integrated into the nTLDStats database
        constants = {
            classicTLDs = {
                -- Generic TLDs
                com=true, net=true, org=true, info=true,
                -- Restricted TLDs
                edu=true, gov=true, mil=true, int=true,
                -- Sponsored TLDs
                aero=true, asia=true, cat=true, coop=true, 
                jobs=true, mobi=true, museum=true, post=true, 
                tel=true, travel=true, xxx=true
            }
        },

        patterns = {
            tldExtension="%.([^%.]+)$",  -- Extracts the TLD part after the last dot (e.g., "com" from "example.com")
            countryDelimiter="([^;]+)"   -- Matches any sequence of characters not containing semicolons; used for country parsing
        },
        
        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",
                -- "Has country" is handled separately through addMultiCountrySemanticProperties
                ["Date introduced"] = "introduced",
                ["Date implemented"] = "date", -- Will also handle implemented
                ["Uses writing script"] = "script", -- Will also handle language
                ["Has registry operator"] = "registry", -- Will also handle registryprovider
                ["Has PIC or RVC"] = "RVC" -- Will also handle PIC
                -- "Is IDN" is handled separately as a boolean property
            },
            
            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)
                    local CanonicalForms = require('Module:CanonicalForms')
                    return select(1, CanonicalForms.normalize(value, p.templates.TLD.mappings.type)) or value
                end,
                ["Has TLD subtype"] = function(value)
                    local CanonicalForms = require('Module:CanonicalForms')
                    return select(1, CanonicalForms.normalize(value, p.templates.TLD.mappings.subtype)) or value
                end,
                ["Date introduced"] = function(value)
                    return tostring(require('Module:NormalizationDate').formatDate(value))
                end,
                ["Date implemented"] = function(value)
                    return tostring(require('Module:NormalizationDate').formatDate(value))
                end,
                ["Has PIC or RVC"] = function(value)
                    -- If value exists and is not empty, return "true"
                    if value and value ~= "" then
                        return "true"
                    end
                    return nil -- Return nil for empty values
                end
            },
            
            skipProperties = { -- Handled separately
                ["Has country"] = true
            }
        }
    },
    
------------------------------------------------------------------------------
-- ANCHOR: LIBRARY INTERVIEW TEMPLATE
------------------------------------------------------------------------------

    LibraryInterview = {
        meta = {
            description = "Module for rendering the Library Interview template with semantics"
        },
        
        categories = { -- Default 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 = {
            -- Direct property mappings (1:1 relationship between property and field)
            properties = {
                ["Has interview format"] = "Format",
                ["Has date"] = "Date",
                ["Has interviewer"] = "Interviewer", 
                ["Has interviewee"] = "Interviewee",
                ["Permanent ID"] = "ID"
            },
            
            -- Properties that map to multiple possible fields
            additionalProperties = {
                ["Has person"] = {"Interviewer", "Interviewee"} -- Links interviews to person pages
            },
            
            -- Transform functions to standardize property values
            transforms = {
                ["Has date"] = function(value) 
                    return tostring(require('Module:NormalizationDate').formatDate(value)) 
                end,
                ["Permanent ID"] = function(value)
                    -- Simply return the value as is, ensuring it's a string
                    return tostring(value or "")
                end
            },
            
            skipProperties = {
                -- No properties to skip
            }
        }
    }
}

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

-- Get configuration for a specific template type
function p.getConfig(templateType)
    return p.templates[templateType] or {}
end

-- Creates a standardized configuration structure for template modules
function p.createStandardConfig(config)
    config = config or {}
    
    -- Initialize with defaults
    local standardConfig = {
        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 {} -- Add categories field to preserve base categories
    }
    
    return standardConfig
end

-- Get a standard config object for use with existing template modules
function p.getStandardConfig(templateType, customOverrides)
    -- Get the base configuration
    local baseConfig = p.getConfig(templateType)
    
    -- Create a standard config
    return p.createStandardConfig(baseConfig, customOverrides)
end

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