Module:TemplateStarter: Difference between revisions

// via Wikitext Extension for VSCode
Tag: Reverted
Maintenance update // via Wikitext Extension for VSCode
 
(23 intermediate revisions by the same user not shown)
Line 1: Line 1:
-- Module:TemplateStarter
--[[
-- Generates empty template structures for new pages using ConfigRepository
* Name: TemplateStarter
* Author: Mark W. Datysgeld
* Description: Generates configurable template structures for new pages using ConfigRepository with support for variants and placeholder replacement
* Notes: Template generation from ConfigRepository configs; support for campaign variants; placeholder replacement in boilerplate text; creator field management; caching of template lists; includes error handling and validation
]]


local p = {}
local p = {}
Line 8: Line 12:
local ErrorHandling = require('Module:ErrorHandling')
local ErrorHandling = require('Module:ErrorHandling')


-- Cache for template lists to improve performance
-- Cache for template lists
local templateListCache = nil
local templateListCache = nil
local templateListCacheTime = 0
local templateListCacheTime = 0
local CACHE_DURATION = 300 -- 5 minutes in seconds
local CACHE_DURATION = 300 -- 5 minutes


-- Generate empty template wikitext from template type
-- Generate empty template wikitext from template type
Line 20: Line 24:
     end
     end
      
      
     -- Sanitize input - remove any potentially harmful characters
     -- Sanitize input: remove any potentially harmful characters
     templateType = mw.text.trim(tostring(templateType))
     templateType = mw.text.trim(tostring(templateType))
      
      
Line 27: Line 31:
     end
     end
      
      
     -- Create error context for better error handling
     -- Create error context
     local errorContext = ErrorHandling.createContext("TemplateStarter")
     local errorContext = ErrorHandling.createContext("TemplateStarter")
      
      
Line 53: Line 57:
     -- Pre-allocate lines table for better performance
     -- Pre-allocate lines table for better performance
     local lines = {}
     local lines = {}
     lines[1] = "{{" .. templateType
     local lineCount = 0
     local lineCount = 1
   
    -- Check for boilerplate configuration
    local hasBoilerplate = config.boilerplate and (config.boilerplate.intro or config.boilerplate.outro)
   
    -- Add debug comment
    if hasBoilerplate then
        lineCount = lineCount + 1
        lines[lineCount] = "<!-- Boilerplate available for " .. templateType .. " template -->"
    else
        lineCount = lineCount + 1
        lines[lineCount] = "<!-- No boilerplate for this template -->"
    end
   
    -- Start the template
     lineCount = lineCount + 1
    lines[lineCount] = "{{" .. templateType
      
      
     -- Process each field with error protection
     -- Process each field with error protection
Line 80: Line 99:
     lines[lineCount] = "}}"
     lines[lineCount] = "}}"
      
      
     -- Join with newlines (more efficient than multiple concatenations)
    -- Add intro boilerplate after template (if available)
    if config.boilerplate and config.boilerplate.intro then
        lineCount = lineCount + 1
        lines[lineCount] = ""  -- First empty line for separation
        lineCount = lineCount + 1
        lines[lineCount] = ""  -- Second empty line for distance
        lineCount = lineCount + 1
        lines[lineCount] = config.boilerplate.intro
    end
 
     -- Join with newlines
     return table.concat(lines, "\n")
     return table.concat(lines, "\n")
end
end
Line 113: Line 142:
end
end


-- Generate a dynamic preload template (main function used by JavaScript)
-- Generate a dynamic preload template (main function used by JS)
function p.preload(frame)
function p.preload(frame)
     local args = frame.args
     local args = frame.args
Line 122: Line 151:
     end
     end
      
      
     return p.generateTemplate(mw.text.trim(tostring(templateType)))
     templateType = mw.text.trim(tostring(templateType))
   
    -- Handle "None" special case
    if templateType == "None" then
        return ""
    end
   
    -- Parse template to check if it's a variant
    local parsedTemplate = p.parseVariantTemplate(templateType)
   
    if parsedTemplate and parsedTemplate.variant then
        -- Generate combined template for variant
        return p.generateCombinedTemplate(parsedTemplate.baseTemplate, parsedTemplate.variant)
    else
        -- Generate regular template
        return p.generateTemplate(templateType)
    end
end
end


Line 134: Line 179:
     end
     end
      
      
    -- Simple approach without complex error handling
     local templates = {}
     local templates = {}
      
      
    -- Check if ConfigRepository.templates exists
     if ConfigRepository.templates then
     if ConfigRepository.templates then
         local index = 1
         local index = 1
         for templateName, _ in pairs(ConfigRepository.templates) do
         for templateName, config in pairs(ConfigRepository.templates) do
             if templateName and templateName ~= "" then
            -- Include template if page_creator is not explicitly false
             if templateName and templateName ~= "" and (not config.meta or config.meta.page_creator ~= false) then
                 templates[index] = templateName
                 templates[index] = templateName
                 index = index + 1
                 index = index + 1
Line 149: Line 193:
     end
     end
      
      
    -- No fallback - if ConfigRepository fails, return empty list
    -- This ensures we only use data from the single source of truth
   
    -- Update cache
     templateListCache = templates
     templateListCache = templates
     templateListCacheTime = currentTime
     templateListCacheTime = currentTime
Line 159: Line 199:
end
end


-- Test function to list available templates (clean output for JavaScript consumption)
-- Get list of available templates including campaign combinations
function p.getAvailableTemplatesWithVariants()
    -- Get the filtered list of base templates that are allowed for page creation
    local baseTemplates = p.getAvailableTemplates()
    local templatesWithCampaigns = {}
   
    -- Use a map to ensure uniqueness before adding to the final list
    local templateMap = {}
   
    -- Add base templates to the map
    for _, templateName in ipairs(baseTemplates) do
        templateMap[templateName] = true
    end
   
    -- Iterate through campaigns to find applicable templates
    if ConfigRepository.campaigns then
        for campaignId, campaign in pairs(ConfigRepository.campaigns) do
            if campaign.applicable_templates then
                for _, templateName in ipairs(campaign.applicable_templates) do
                    -- Only include if the base template is allowed for page creation
                    if templateMap[templateName] then
                        local campaignTemplateName = templateName .. " (" .. campaign.name .. ")"
                        templateMap[campaignTemplateName] = true
                    end
                end
            end
        end
    end
   
    -- Convert map keys to a list
    local index = 1
    for templateName, _ in pairs(templateMap) do
        templatesWithCampaigns[index] = templateName
        index = index + 1
    end
   
    -- Sort the final combined list
    table.sort(templatesWithCampaigns)
   
    -- Add "None" option at the beginning
    table.insert(templatesWithCampaigns, 1, "None")
   
    return templatesWithCampaigns
end
 
-- Parse campaign template name to get base template and campaign info
function p.parseVariantTemplate(templateName)
    if not templateName or templateName == "" or templateName == "None" then
        return nil
    end
   
    -- Check if this is a campaign combination by looking for the pattern "Template (Campaign Name)"
    if ConfigRepository.campaigns then
        for campaignId, campaign in pairs(ConfigRepository.campaigns) do
            local expectedName = " (" .. campaign.name .. ")"
            if templateName:sub(-#expectedName) == expectedName then
                local baseTemplateName = templateName:sub(1, -#expectedName - 1)
                -- Verify this is a valid combination
                if campaign.applicable_templates then
                    for _, applicableTemplate in ipairs(campaign.applicable_templates) do
                        if applicableTemplate == baseTemplateName then
                            return {
                                baseTemplate = baseTemplateName,
                                variantKey = campaignId,
                                variant = {
                                    name = templateName,
                                    campaign_template = campaign.json_config
                                }
                            }
                        end
                    end
                end
            end
        end
    end
   
    -- Not a campaign combination, return as base template
    return {
        baseTemplate = templateName,
        variantKey = nil,
        variant = nil
    }
end
 
-- Generate combined template for variants
function p.generateCombinedTemplate(baseTemplate, variant)
    if not baseTemplate or not variant then
        return "Error: Invalid template combination"
    end
   
    -- Generate base template
    local baseContent = p.generateTemplate(baseTemplate)
    if not baseContent or baseContent:match("^Error:") then
        return baseContent or "Error: Failed to generate base template"
    end
   
    -- Generate campaign template if specified
    local campaignContent = ""
    if variant.campaign_template then
        campaignContent = string.format("{{#invoke:T-Campaign|render|campaign_name=%s}}", variant.campaign_template)
    end
   
    -- Combine templates with blank line separator
    local combinedLines = {}
    local lineCount = 0
   
    -- Add base template
    lineCount = lineCount + 1
    combinedLines[lineCount] = baseContent
   
    -- Add separator
    lineCount = lineCount + 1
    combinedLines[lineCount] = ""
   
    -- Add campaign template
    if campaignContent ~= "" then
        lineCount = lineCount + 1
        combinedLines[lineCount] = campaignContent
    end
   
    return table.concat(combinedLines, "\n")
end
 
-- Test function to list available templates (clean output for JS consumption)
function p.listTemplates(frame)
function p.listTemplates(frame)
     local templates = p.getAvailableTemplates()
     local templates = p.getAvailableTemplatesWithVariants()
     if #templates == 0 then
     if #templates == 0 then
         return "No templates available"
         return "No templates available"
     end
     end
    return table.concat(templates, ", ")
end
-- Get creator fields for a specific template type
function p.getCreatorFields(templateType)
    if not templateType or templateType == "" or templateType == "None" then
        return {}
    end
   
    -- Parse template to resolve variants to base template
    local parsedTemplate = p.parseVariantTemplate(templateType)
    local baseTemplateName = parsedTemplate and parsedTemplate.baseTemplate or templateType
   
    local config = ConfigRepository.getConfig(baseTemplateName)
    return config.creatorFields or {}
end
-- Get field definitions for creator fields
function p.getCreatorFieldDefinitions(templateType)
    if not templateType or templateType == "" then
        return {}
    end
   
    local creatorFields = p.getCreatorFields(templateType)
    local fieldDefinitions = {}
   
    for _, fieldKey in ipairs(creatorFields) do
        local fieldDef = ConfigRepository.pageCreatorFields[fieldKey]
        if fieldDef then
            fieldDefinitions[fieldKey] = fieldDef
        end
    end
   
    return fieldDefinitions
end
-- Replace placeholders in boilerplate text with user values
function p.replacePlaceholders(text, values)
    if not text or not values then
        return text
    end
   
    local result = text
    for key, value in pairs(values) do
        if value and value ~= "" then
            result = result:gsub("%$" .. key .. "%$", value)
        end
    end
   
    return result
end
-- Remove empty placeholders from text (for cases where no values are provided)
function p.removeEmptyPlaceholders(text)
    if not text then
        return text
    end
   
    -- Remove any remaining $VARIABLE$ placeholders that weren't filled
    local result = text:gsub("%$[A-Z_]+%$", "")
   
    -- Clean up any resulting double spaces or awkward punctuation
    result = result:gsub("%s+", " ")  -- Multiple spaces to single space
    result = result:gsub("^%s+", "")  -- Leading whitespace
    result = result:gsub("%s+$", "")  -- Trailing whitespace
    result = result:gsub("%s+%.", ".")  -- Space before period
    result = result:gsub("is a%s+based", "is based")  -- Fix "is a  based" to "is based"
    result = result:gsub("is a%s+in", "is in")  -- Fix "is a  in" to "is in"
   
    return result
end
-- Generate template with placeholder replacement
function p.generateTemplateWithValues(templateType, values)
    if not templateType or templateType == "" then
        return "Error: Template type is required"
    end
   
    -- Get base template
    local baseTemplate = p.generateTemplate(templateType)
   
    -- Get boilerplate and replace placeholders
    local config = ConfigRepository.getConfig(templateType)
    if config.boilerplate and config.boilerplate.intro then
        local processedIntro = p.replacePlaceholders(config.boilerplate.intro, values)
        -- Replace the boilerplate in the template
        baseTemplate = baseTemplate:gsub(config.boilerplate.intro, processedIntro)
    end
   
    return baseTemplate
end
-- Get creator field definitions as JSON for JavaScript consumption
function p.getCreatorFieldDefinitionsJSON(frame)
    local args = frame.args
    local templateType = args.templateType or args[1]
   
    if not templateType or templateType == "" then
        return "{}"
    end
   
    local fieldDefinitions = p.getCreatorFieldDefinitions(templateType)
      
      
     -- Encode template names to prevent Lingo expansion
     -- Convert to JSON-like string manually (simple approach)
     local encodedTemplates = {}
     local jsonParts = {}
     for i, template in ipairs(templates) do
     for fieldKey, fieldDef in pairs(fieldDefinitions) do
         -- Replace 'TLD' with HTML entity to bypass Lingo
         local fieldJson = string.format(
        local encoded = template:gsub("TLD", "T&#76;D")
            '"%s":{"key":"%s","label":"%s","placeholder":"%s","required":%s}',
         encodedTemplates[i] = encoded
            fieldKey,
            fieldDef.key or "",
            fieldDef.label or "",
            fieldDef.placeholder or "",
            fieldDef.required and "true" or "false"
        )
         table.insert(jsonParts, fieldJson)
     end
     end
      
      
     return table.concat(encodedTemplates, ", ")
     return "{" .. table.concat(jsonParts, ",") .. "}"
end
end


return p
return p