Jump to content

Module:LuaTemplateTLD

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

-- Module:LuaTemplateTLD
-- Renders TLD/ccTLD article templates with normalization and dynamic content. Features:
-- * Processes type/subtype values with canonical mapping
-- * Auto-detects IDN status and converts to punycode when needed
-- * Normalizes dates, countries, and website links
-- * Assigns categories based on country, PIC, and domain type
-- * Integrates social media and NTLDStats components

local p = {}

-- Dependencies
local CanonicalForms        = require('Module:CanonicalForms')
local CountryData           = require('Module:CountryData')
local dateNormalization     = require('Module:NormalizationDate')
local linkParser            = require('Module:LinkParser')
local punycode              = require('Module:Punycode')
local socialFooter          = require('Module:SocialMedia')
local TemplateStructure     = require('Module:TemplateStructure')
local TemplateHelpers       = require('Module:TemplateHelpers')
local SemanticAnnotations   = require('Module:SemanticAnnotations')
local ConfigRepository      = require('Module:ConfigRepository')
local SemanticCategoryHelpers = require('Module:SemanticCategoryHelpers')

--------------------------------------------------------------------------------
-- Configuration and Constants
--------------------------------------------------------------------------------
local Config = ConfigRepository.getStandardConfig('TLD')

--------------------------------------------------------------------------------
-- Helper Functions
--------------------------------------------------------------------------------
local IDNHelpers = {
    processIDN = function(args, rawType)
        if args["idn"] then
            local idnVal = string.lower(args["idn"])
            if idnVal == "yes" or idnVal == "true" then return true end
        end
        return string.find(string.lower(rawType), "idn") and true or false
    end,
    getASCII = function(pageName)
        return "xn--" .. punycode.encode(pageName:gsub("^%.", ""))
    end
}

-- Use TemplateHelpers for common functions
local getFieldValue = TemplateHelpers.getFieldValue
local splitMultiValueString = SemanticCategoryHelpers.splitMultiValueString
local normalizeWebsites = TemplateHelpers.normalizeWebsites
local normalizeDates = TemplateHelpers.normalizeDates

-- Wrapper for country normalization
local normalizeCountries = TemplateHelpers.normalizeCountries

--------------------------------------------------------------------------------
-- Block Rendering Functions
--------------------------------------------------------------------------------
local function renderTitleBlock(args)
    local titleText = (args._type == "ccTLD") and "ccTLD" or "gTLD"
    return TemplateHelpers.renderTitleBlock(args, "template-title template-title-tld", titleText, {
        achievementSupport = false -- No achievement support for TLD template
    })
end

local function renderLogoBlock(args)
    return TemplateHelpers.renderLogoBlock(args, {
        cssClass = "tld-logo"
    })
end

local function renderFlairBlock(args)
    local classes = {"tld-flair"}
    if args._idnFlag then
        table.insert(classes, args._type == "ccTLD" and "tld-template-idn-cctld" or "tld-template-idn-gtld")
    elseif args["subtype"] and args["subtype"] ~= "" then
        local _, css = CanonicalForms.normalize(args["subtype"], Config.mappings.subtype)
        if css then table.insert(classes, css) end
    end
    return string.format('|-\n| colspan="2" class="%s" |', table.concat(classes, " "))
end

local function renderFieldsBlock(args)
    -- Define field processors
    local processors = {
        type = function(value)
            return select(1, CanonicalForms.normalize(value, Config.mappings.type)) or value
        end,
        subtype = function(value)
            return select(1, CanonicalForms.normalize(value, Config.mappings.subtype)) or value
        end,
        website = normalizeWebsites,
        date = function(value)
            return TemplateHelpers.formatDateRange(value, nil, {
                outputMode = "text"
            })
        end,
        implemented = function(value)
            return TemplateHelpers.formatDateRange(value, nil, {
                outputMode = "text"
            })
        end,
        introduced = function(value)
            return TemplateHelpers.formatDateRange(value, nil, {
                outputMode = "text"
            })
        end,
        country = normalizeCountries,
        territory = normalizeCountries,
        RVC = function(value)
            if value:match("^%d+$") then
                return string.format("[https://gtldresult.icann.org/applicationstatus/applicationdetails/%s Here]", value)
            end
            return value
        end,
        PIC = function(value)
            if value:match("^%d+$") then
                return string.format("[https://gtldresult.icann.org/applicationstatus/applicationdetails/%s Here]", value)
            end
            return value
        end
    }
    
    return TemplateHelpers.renderFieldsBlock(args, Config.fields, processors)
end

local function renderNTLDStatsBlock(args)
    if args._type == "ccTLD" then return "" end
    
    -- Get the current page name
    local titleObj = mw.title.getCurrentTitle()
    local pageName = titleObj and titleObj.text or ""
    
    local tldName = pageName:gsub("^%.", "")
    local tldExtension = tldName:match(Config.patterns.tldExtension)
    if Config.constants.classicTLDs[mw.ustring.lower(tldName)] or (tldExtension and Config.constants.classicTLDs[mw.ustring.lower(tldExtension)]) then
        return ""
    end
    
    local divider = TemplateHelpers.renderDividerBlock("Find out more:")
    local stats = string.format('|-\n| colspan="2" | <div class="ntldstats icannwiki-centered">[[File:NTLDStatsLogo.png|100px|alt=NTLDStats|link=https://ntldstats.com/tld/%s]]</div>', tldName)
    
    return divider .. "\n" .. stats
end

--------------------------------------------------------------------------------
-- Semantic Properties
--------------------------------------------------------------------------------
-- Generate semantic properties for the TLD
local function generateSemanticProperties(args)
    -- Set basic properties using the transformation functions from Config
    local semanticOutput = SemanticAnnotations.setSemanticProperties(
        args, 
        Config.semantics.properties, 
        {transform = Config.semantics.transforms}
    )
    
    -- For non-SMW case, collect property HTML fragments in a table for efficient concatenation
    local propertyHtml = {}
    
    -- Handle special case for IDN flag
    if args._idnFlag then
        if mw.smw then
            mw.smw.set({["Is IDN"] = "true"})
        else
            -- Collect HTML fragments instead of concatenating strings
            table.insert(propertyHtml, '<div style="display:none;">')
            table.insert(propertyHtml, '  {{#set: Is IDN=true }}')
            table.insert(propertyHtml, '</div>')
        end
    end
    
    -- Handle special case for multiple countries using CountryData
    local countryValue = args["country"] or args["territory"]
    semanticOutput = CountryData.addCountrySemanticProperties(countryValue, semanticOutput)
    
-- Process additional properties with multi-value support
-- Skip properties that are handled separately above
    semanticOutput = SemanticCategoryHelpers.processAdditionalProperties(
        args, 
        Config.semantics, 
        semanticOutput, 
        Config.semantics.skipProperties
    )
    
    -- For non-SMW case, concatenate all property HTML fragments at once
    if not mw.smw and #propertyHtml > 0 then
        semanticOutput = semanticOutput .. "\n" .. table.concat(propertyHtml, "\n")
    end
    
    return semanticOutput
end

--------------------------------------------------------------------------------
-- Category Assignment
--------------------------------------------------------------------------------
local function computeCategories(args)
    local cats = {}
    
    -- Process country categories from either country or territory key using CountryData
    local countryValue = args["country"] or args["territory"]
    if countryValue and countryValue ~= "" then
        cats = SemanticCategoryHelpers.addMultiValueCategories(
            countryValue,
            function(value)
                -- Use the new CountryData module directly
                return CountryData.normalizeCountryName(value)
            end,
            cats
        )
    end
    
    -- Process region categories using the generic function
    if args["region"] and args["region"] ~= "" then
        cats = SemanticCategoryHelpers.addMultiValueCategories(
            args["region"],
            function(region) return region:match("^%s*(.-)%s*$") end, -- Trim whitespace
            cats
        )
    end
    
    -- Add conditional categories from config
    if Config.categories and Config.categories.conditional then
        -- Check for RVC/PIC
        if Config.categories.conditional.rvc and ((args["RVC"] and args["RVC"] ~= "") or (args["PIC"] and args["PIC"] ~= "")) then
            table.insert(cats, Config.categories.conditional.rvc)
        end
        
        -- Check for IDN
        if args._idnFlag then
            if Config.categories.conditional.idn then
                table.insert(cats, Config.categories.conditional.idn)
            end
            
            -- Special case for IDN ccTLDs
            if args._type == "ccTLD" and Config.categories.conditional.idn_cctld then
                table.insert(cats, Config.categories.conditional.idn_cctld)
            end
        end
    end
    
    -- Add type categories from mapping
    local typeCategories = SemanticCategoryHelpers.addMappingCategories(args._type, Config.mappings.type)
    for _, cat in ipairs(typeCategories) do
        table.insert(cats, cat)
    end
    
    -- Add subtype categories
    if args["subtype"] and args["subtype"] ~= "" then
        local canonicalSubtype, _, subtypeCategory = CanonicalForms.normalize(args["subtype"], Config.mappings.subtype)
        if subtypeCategory and subtypeCategory ~= "" then
            table.insert(cats, subtypeCategory)
        elseif canonicalSubtype and canonicalSubtype ~= "" then
            table.insert(cats, canonicalSubtype)
        end
    end
    
    return SemanticCategoryHelpers.buildCategories(cats)
end

--------------------------------------------------------------------------------
-- Main Render Function
--------------------------------------------------------------------------------
function p.render(frame)
    local args = frame:getParent().args or {}
    
    -- Normalize arguments for case-insensitivity
    args = TemplateHelpers.normalizeArgumentCase(args)
    
    local titleObj = mw.title.getCurrentTitle()
    local pageName = titleObj and titleObj.text or ""
    
    -- Process type and IDN status
    local rawType = args["type"] or ""
    args._type = select(1, CanonicalForms.normalize(rawType, Config.mappings.type))
    args._idnFlag = IDNHelpers.processIDN(args, rawType)
    
    -- Set ASCII value for IDN
    if args._idnFlag and titleObj then
        args["ascii"] = IDNHelpers.getASCII(pageName)
    end
    
    -- Configure block rendering
    local config = {
        blocks = {
            function(a) return renderTitleBlock(a) end,
            function(a) return renderLogoBlock(a) end,
            function(a) return renderFlairBlock(a) end,
            function(a) return renderFieldsBlock(a) end,
            function(a) return socialFooter.render(a) or "" end,
            function(a) return renderNTLDStatsBlock(a) end
        }
    }
    
    -- Generate template output
    local output = TemplateStructure.render(args, config)
    
    -- Add categories
    local categories = computeCategories(args)
    if categories and categories ~= "" then
        output = output .. "\n" .. categories
    end
    
    -- Add semantic properties
    local semantics = generateSemanticProperties(args)
    if semantics and semantics ~= "" then
        output = output .. "\n" .. semantics
    end
    
    return output
end

return p