Module:TemplateStarter: Difference between revisions
Appearance
// via Wikitext Extension for VSCode |
Maintenance update // via Wikitext Extension for VSCode |
||
| (33 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
-- | --[[ | ||
* 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 6: | Line 10: | ||
-- Required modules | -- Required modules | ||
local ConfigRepository = require('Module:ConfigRepository') | local ConfigRepository = require('Module:ConfigRepository') | ||
local ErrorHandling = require('Module:ErrorHandling') | |||
-- Cache for template lists | |||
local templateListCache = nil | |||
local templateListCacheTime = 0 | |||
local CACHE_DURATION = 300 -- 5 minutes | |||
-- Generate empty template wikitext from template type | -- Generate empty template wikitext from template type | ||
function p.generateTemplate(templateType) | function p.generateTemplate(templateType) | ||
-- | -- Input validation and sanitization | ||
local config = ConfigRepository.getConfig(templateType) | if not templateType or templateType == "" then | ||
return "Error: Template type is required" | |||
end | |||
-- Sanitize input: remove any potentially harmful characters | |||
templateType = mw.text.trim(tostring(templateType)) | |||
if not templateType:match("^[%w_%-]+$") then | |||
return "Error: Invalid template type format" | |||
end | |||
-- Create error context | |||
local errorContext = ErrorHandling.createContext("TemplateStarter") | |||
-- Protected function to get configuration | |||
local function getTemplateConfig() | |||
local config = ConfigRepository.getConfig(templateType) | |||
if not config then | |||
return nil, string.format("Template type '%s' not found in ConfigRepository", templateType) | |||
end | |||
if not config.fields then | |||
return nil, string.format("Template type '%s' has no fields configuration", templateType) | |||
end | |||
return config | |||
end | |||
-- Get configuration with error protection | |||
local config = ErrorHandling.protect(errorContext, 'getTemplateConfig', getTemplateConfig, nil) | |||
if not config then | |||
return "Error: Failed to load template configuration" | |||
end | |||
-- Pre-allocate lines table for better performance | |||
local lines = {} | |||
local lineCount = 0 | |||
-- Check for boilerplate configuration | |||
local hasBoilerplate = config.boilerplate and (config.boilerplate.intro or config.boilerplate.outro) | |||
-- | -- Add debug comment | ||
if | 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 | end | ||
-- Start | -- Start the template | ||
lineCount = lineCount + 1 | |||
lines[lineCount] = "{{" .. templateType | |||
-- Process each field | -- Process each field with error protection | ||
for _, field in ipairs(config.fields) do | local function processFields() | ||
for _, field in ipairs(config.fields) do | |||
-- Skip hidden fields | |||
if not field.hidden then | |||
-- Handle fields with multiple keys (prefer single key, fallback to first of multiple) | |||
local fieldKey = field.key or (field.keys and field.keys[1]) | |||
if fieldKey and fieldKey ~= "" then | |||
lineCount = lineCount + 1 | |||
lines[lineCount] = string.format("|%s = ", fieldKey) | |||
end | |||
end | end | ||
end | end | ||
return true | |||
end | end | ||
-- Process fields with error protection | |||
ErrorHandling.protect(errorContext, 'processFields', processFields, false) | |||
-- Close the template | -- Close the template | ||
lineCount = lineCount + 1 | |||
lines[lineCount] = "}}" | |||
-- 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 | -- Join with newlines | ||
return table.concat(lines, "\n") | return table.concat(lines, "\n") | ||
end | end | ||
-- Main function to be called from wiki | -- Main function to be called from wiki (for testing/preview) | ||
function p.main(frame) | function p.main(frame) | ||
-- Simple validation without complex error handling for this function | |||
local args = frame.args | local args = frame.args | ||
local parent = frame:getParent() | local parent = frame:getParent() | ||
| Line 51: | Line 125: | ||
-- Validate inputs | -- Validate inputs | ||
if not articleName or articleName == "" then | if not articleName or mw.text.trim(tostring(articleName)) == "" then | ||
return "Error: Article name is required" | return "Error: Article name is required" | ||
end | end | ||
if not templateType or templateType == "" then | if not templateType or mw.text.trim(tostring(templateType)) == "" then | ||
return "Error: Template type is required" | return "Error: Template type is required" | ||
end | end | ||
-- Generate the template content | -- Generate the template content | ||
local content = p.generateTemplate(templateType) | local content = p.generateTemplate(mw.text.trim(tostring(templateType))) | ||
-- For testing, return the generated content in a pre block | -- For testing, return the generated content in a pre block | ||
return string.format('<pre>Page: %s\n\n%s</pre>', articleName, content) | return string.format('<pre>Page: %s\n\n%s</pre>', | ||
mw.text.nowiki(mw.text.trim(tostring(articleName))), | |||
mw.text.nowiki(content)) | |||
end | end | ||
-- Generate a | -- Generate a dynamic preload template (main function used by JS) | ||
function p. | function p.preload(frame) | ||
local args = frame.args | local args = frame.args | ||
local | local templateType = args.templateType or args[1] | ||
local | |||
if not templateType or mw.text.trim(tostring(templateType)) == "" then | |||
return "<!-- No template type specified -->" | |||
end | |||
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 | |||
-- Get list of available templates with caching | |||
function p.getAvailableTemplates() | |||
local currentTime = os.time() | |||
-- Check if cache is valid | |||
if templateListCache and (currentTime - templateListCacheTime) < CACHE_DURATION then | |||
return templateListCache | |||
end | |||
local templates = {} | |||
if ConfigRepository.templates then | |||
local index = 1 | |||
for templateName, config in pairs(ConfigRepository.templates) do | |||
-- 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 | |||
index = index + 1 | |||
end | |||
end | |||
table.sort(templates) | |||
end | |||
templateListCache = templates | |||
templateListCacheTime = currentTime | |||
return templates | |||
end | |||
-- 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") | ||
if not | end | ||
return | |||
-- Test function to list available templates (clean output for JS consumption) | |||
function p.listTemplates(frame) | |||
local templates = p.getAvailableTemplatesWithVariants() | |||
if #templates == 0 then | |||
return "No templates available" | |||
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 | 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 | if not templateType or templateType == "" then | ||
return | return {} | ||
end | end | ||
local creatorFields = p.getCreatorFields(templateType) | |||
local | local fieldDefinitions = {} | ||
for _, fieldKey in ipairs(creatorFields) do | |||
local fieldDef = ConfigRepository.pageCreatorFields[fieldKey] | |||
if fieldDef then | |||
fieldDefinitions[fieldKey] = fieldDef | |||
end | |||
end | |||
-- | return fieldDefinitions | ||
if | end | ||
return | |||
-- Replace placeholders in boilerplate text with user values | |||
function p.replacePlaceholders(text, values) | |||
if not text or not values then | |||
return text | |||
end | 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 | |||
return | |||
end | end | ||
-- | -- Remove empty placeholders from text (for cases where no values are provided) | ||
function p. | function p.removeEmptyPlaceholders(text) | ||
if not text then | |||
local | 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 | return result | ||
end | end | ||
-- | -- Generate template with placeholder replacement | ||
function p. | function p.generateTemplateWithValues(templateType, values) | ||
local | 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 | end | ||
return | return baseTemplate | ||
end | end | ||
-- | -- Get creator field definitions as JSON for JavaScript consumption | ||
function p. | function p.getCreatorFieldDefinitionsJSON(frame) | ||
local | local args = frame.args | ||
return " | local templateType = args.templateType or args[1] | ||
if not templateType or templateType == "" then | |||
return "{}" | |||
end | |||
local fieldDefinitions = p.getCreatorFieldDefinitions(templateType) | |||
-- Convert to JSON-like string manually (simple approach) | |||
local jsonParts = {} | |||
for fieldKey, fieldDef in pairs(fieldDefinitions) do | |||
local fieldJson = string.format( | |||
'"%s":{"key":"%s","label":"%s","placeholder":"%s","required":%s}', | |||
fieldKey, | |||
fieldDef.key or "", | |||
fieldDef.label or "", | |||
fieldDef.placeholder or "", | |||
fieldDef.required and "true" or "false" | |||
) | |||
table.insert(jsonParts, fieldJson) | |||
end | |||
return "{" .. table.concat(jsonParts, ",") .. "}" | |||
end | end | ||
return p | return p | ||
Latest revision as of 20:01, 3 December 2025
Documentation for this module may be created at Module:TemplateStarter/doc
--[[
* 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 = {}
-- Required modules
local ConfigRepository = require('Module:ConfigRepository')
local ErrorHandling = require('Module:ErrorHandling')
-- Cache for template lists
local templateListCache = nil
local templateListCacheTime = 0
local CACHE_DURATION = 300 -- 5 minutes
-- Generate empty template wikitext from template type
function p.generateTemplate(templateType)
-- Input validation and sanitization
if not templateType or templateType == "" then
return "Error: Template type is required"
end
-- Sanitize input: remove any potentially harmful characters
templateType = mw.text.trim(tostring(templateType))
if not templateType:match("^[%w_%-]+$") then
return "Error: Invalid template type format"
end
-- Create error context
local errorContext = ErrorHandling.createContext("TemplateStarter")
-- Protected function to get configuration
local function getTemplateConfig()
local config = ConfigRepository.getConfig(templateType)
if not config then
return nil, string.format("Template type '%s' not found in ConfigRepository", templateType)
end
if not config.fields then
return nil, string.format("Template type '%s' has no fields configuration", templateType)
end
return config
end
-- Get configuration with error protection
local config = ErrorHandling.protect(errorContext, 'getTemplateConfig', getTemplateConfig, nil)
if not config then
return "Error: Failed to load template configuration"
end
-- Pre-allocate lines table for better performance
local lines = {}
local lineCount = 0
-- 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
local function processFields()
for _, field in ipairs(config.fields) do
-- Skip hidden fields
if not field.hidden then
-- Handle fields with multiple keys (prefer single key, fallback to first of multiple)
local fieldKey = field.key or (field.keys and field.keys[1])
if fieldKey and fieldKey ~= "" then
lineCount = lineCount + 1
lines[lineCount] = string.format("|%s = ", fieldKey)
end
end
end
return true
end
-- Process fields with error protection
ErrorHandling.protect(errorContext, 'processFields', processFields, false)
-- Close the template
lineCount = lineCount + 1
lines[lineCount] = "}}"
-- 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")
end
-- Main function to be called from wiki (for testing/preview)
function p.main(frame)
-- Simple validation without complex error handling for this function
local args = frame.args
local parent = frame:getParent()
local pargs = parent and parent.args or {}
-- Get parameters (check both direct and parent args)
local articleName = args.articleName or pargs.articleName or args[1] or pargs[1]
local templateType = args.templateType or pargs.templateType or args[2] or pargs[2]
-- Validate inputs
if not articleName or mw.text.trim(tostring(articleName)) == "" then
return "Error: Article name is required"
end
if not templateType or mw.text.trim(tostring(templateType)) == "" then
return "Error: Template type is required"
end
-- Generate the template content
local content = p.generateTemplate(mw.text.trim(tostring(templateType)))
-- For testing, return the generated content in a pre block
return string.format('<pre>Page: %s\n\n%s</pre>',
mw.text.nowiki(mw.text.trim(tostring(articleName))),
mw.text.nowiki(content))
end
-- Generate a dynamic preload template (main function used by JS)
function p.preload(frame)
local args = frame.args
local templateType = args.templateType or args[1]
if not templateType or mw.text.trim(tostring(templateType)) == "" then
return "<!-- No template type specified -->"
end
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
-- Get list of available templates with caching
function p.getAvailableTemplates()
local currentTime = os.time()
-- Check if cache is valid
if templateListCache and (currentTime - templateListCacheTime) < CACHE_DURATION then
return templateListCache
end
local templates = {}
if ConfigRepository.templates then
local index = 1
for templateName, config in pairs(ConfigRepository.templates) do
-- 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
index = index + 1
end
end
table.sort(templates)
end
templateListCache = templates
templateListCacheTime = currentTime
return templates
end
-- 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)
local templates = p.getAvailableTemplatesWithVariants()
if #templates == 0 then
return "No templates available"
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)
-- Convert to JSON-like string manually (simple approach)
local jsonParts = {}
for fieldKey, fieldDef in pairs(fieldDefinitions) do
local fieldJson = string.format(
'"%s":{"key":"%s","label":"%s","placeholder":"%s","required":%s}',
fieldKey,
fieldDef.key or "",
fieldDef.label or "",
fieldDef.placeholder or "",
fieldDef.required and "true" or "false"
)
table.insert(jsonParts, fieldJson)
end
return "{" .. table.concat(jsonParts, ",") .. "}"
end
return p