PC Gaming Shelter
An archive dedicated to preserving PC Gaming history and more

Module:Utils: Difference between revisions

From PC Gaming Shelter
No edit summary
No edit summary
 
(18 intermediate revisions by the same user not shown)
Line 1: Line 1:
local p = {}
local p = {}
function p.clean(value)
if value == nil then
return nil
end
value = mw.text.trim(tostring(value))
return value ~= '' and value or nil
end


local function splitBy(str, sep)
local function splitBy(str, sep)
Line 13: Line 22:
local category = string.format('[[Category:%s]]', categoryString)
local category = string.format('[[Category:%s]]', categoryString)
return category
return category
end
function p.setShortDescription(description)
if not description then return end
local frame = mw.getCurrentFrame()
local shortDescription = frame:preprocess(string.format('{{SHORTDESC:%s}}', description))
return shortDescription
end
end


Line 34: Line 50:
end
end
return props
return props
end
function p.infoboxSectionClasses()
return {
['onecolumn'] = 'infobox-section d-flex flex-column py-1 border-0 rounded-0 p-1',
['twocolumns'] = 'infobox-section d-flex flex-column flex-sm-row border-0 rounded-0 p-1'
}
end
local function formatDate(input)
    local year, month, day = input:match("(%d+)%-(%d+)%-(%d+)")
    local time = os.time({
        year = tonumber(year),
        month = tonumber(month),
        day = tonumber(day)
    })
    return os.date("%B %d, %Y", time):gsub("^%a+ (%d+)", function(d)
        return os.date("%B", time) .. " " .. tonumber(d)
    end)
end
end


function p.renderInfoboxValue(frame)
local args = frame:getParent().args or {}
local val = args[1] or nil
local sep = args[2] or nil
local fmt = args[3] or nil
if not val then return end
if not sep and not fmt then return val end
 
  local html = mw.html.create()
  local ul = html:tag('ul')
 
local list = splitBy(val, sep) or {}
for _, subStr in ipairs(list) do
local printout = ''
if fmt == "wikilink" then
printout = string.format('[[%s]]', subStr)
elseif fmt == "external" then
printout = string.format('[%s %s]', subStr, linkText)
elseif fmt == "date" then
printout = formatDate(subStr)
else
printout = subStr
end
ul:tag('li'):wikitext(printout)
end
    return html
end


-- ------------------------------------------------------------------
-- ------------------------------------------------------------------
Line 94: Line 61:
-- ----------
-- ----------
-- * frame      – The current Frame object.
-- * frame      – The current Frame object.
-- * cat        – Main category string to add before the page content.
-- * category  – Main category string to add before the page content.
-- * properties – A mapping of user‑argument names → SMW property keys
-- * properties – A mapping of user‑argument names → SMW property keys
--                (optionally followed by a separator).  These are used
--                (optionally followed by a separator).  These are used
--                to set SMW properties via `mw.smw.set()`.
--                to set SMW properties via `mw.smw.set()`.
-- * order      – An ordered list that defines the order in which
-- * sections  - A plan of sections and properties.
--                sections appear inside the infobox.  Each entry must
--
--                match a key in `properties` or in the `infodata`
--                table below.
-- * subobjects - The table with names of templates responsible for subobjects creation
--                The module expects the the parent template parameter name **is equal**
--                to the name of the template:
--                `...`
--                `|TemplateName={{TemplateName|...}}{{TemplateName|...}}`
-- Returns
-- Returns
-- -------
-- -------
Line 112: Line 72:
--
--
--  * The main category (via `p.setCategory(cat)`),
--  * The main category (via `p.setCategory(cat)`),
--  * A fully populated infobox `<div>` with sections in the order
--  * A fully populated infobox
--    specified by **order** and styled according to **infodata**,
--
--
-- The returned HTML is rendered directly into the page where the module
-- The returned HTML is rendered directly into the page where the module
-- was invoked.
-- was invoked.
--------------------------------------------------------------------
--------------------------------------------------------------------
local function isSectionNotEmpty(args, rows)
for _, rowKey in pairs(rows) do
        if args[rowKey] ~= nil then
return true
        end
    end
    return false
end


function p.createInfobox(frame, category, properties, order, sections, data, subobjects)
function p.createInfobox(frame, cat, properties, sections, footer)
local currentTitle = mw.title.getCurrentTitle().fullText
    local currentTitle = mw.title.getCurrentTitle().fullText
local args = frame:getParent().args
    local args = frame:getParent().args
    -- SMW properties
    local propMap = properties
    local props = p.setProperties(propMap, args) or {}
    mw.smw.set(props)
    -- Main category
    local category = p.setCategory(cat)
    -- ShortDescription
    local shortDescription = p.setShortDescription(args['Description'])
    -- Assemble
    local html = mw.html.create()
    local infobox = html:tag('div'):addClass('infobox d-flex flex-column card shadow-none rounded-0 bg-transparent mb-4 float-sm-right')
    -- Sections
    for key, cfg in pairs(sections) do
    if isSectionNotEmpty(args, cfg.rows) == true then
        local section = infobox:tag('div'):addClass('infobox-block'):css({
            ['order'] = cfg.order or '0'
        })
        -- Heading
        local heading = cfg.heading or nil
        if heading and heading ~= '' then
            section:tag('div'):addClass('infobox-section-header'):wikitext(heading)
        end
        -- Content
        local data = {}
        local dataProperties = cfg.rows or {}
        for k, v in pairs(dataProperties) do
            local value = args[v]
            if value then
                data[v] = value
            end
        end
        -- Renderer
        local template = tostring(cfg.template) or 'Infobox/Generic'
        local content = frame:expandTemplate{
            title = template,
            args = data
        }
        section:tag('div'):addClass('infobox-row d-flex flex-column'):wikitext(content)
        end
    end
    -- Footer
if footer and footer ~= '' then
local renderFooter = frame:expandTemplate {
title = footer
}
html:tag('div'):addClass('page-footer bg-dark'):wikitext('\n' .. renderFooter)
end
    return html
        :wikitext(category)
        :wikitext(shortDescription)
end


-- SMW properties
function p.availablePropertyValues(property)
local propMap = properties
property = mw.text.trim(tostring(property or ''))
local props = p.setProperties(propMap, args) or {}
mw.smw.set(props)


-- Main category
if property == '' then
local category = p.setCategory(cat)
return {}, { 'A property name is required.' }
-- Assemble
local html = mw.html.create()
-- Infobox
local infoboxData = html:tag('div'):addClass('infobox d-flex flex-col card shadow-none rounded-0 bg-transparent mb-4 float-sm-right')
-- Header
infoboxData:tag('div')
:addClass('infobox-header card-header rounded-0 text-center')
:tag('div'):wikitext(currentTitle)
-- Cover image
local coverImage = args['Image'] or nil
if coverImage then
infoboxData:tag('div')
:addClass('infobox-img card-img border-0')
:wikitext(string.format('[[File:%s|560px|class=pageimage]]', coverImage)):done()
end
end
-- Other props
 
local propOrder = order
local params = mw.text.jsonEncode({
-- Infobox sections
limit = 1000,
local sectionFormat = data
offset = 0,
-- Helper: Render the value
property = property,
local function renderValue(frame, key, rawValue, fmt)
search = '',
if fmt.template == 'render' then
usageCount = true
return frame:expandTemplate{
})
title = 'render',
local query = mw.uri.buildQueryString({
args = {
action = 'smwbrowse',
string.format(fmt.value, rawValue),
browse = 'pvalue',
fmt.separator,
params = params,
fmt.valuefmt
format = 'json'
}
})
}
local url = string.format(
else
'%s%s/api.php?%s',
return string.format(fmt.value, rawValue)
'https://www.pcgamingshelter.eu',
end
'/w',
query
)
local data, errors = mw.ext.externalData.getExternalData({
url = url,
format = 'JSON'
})
 
if errors or not data or not data.__json then
return {}, errors or { 'ExternalData returned no JSON data.' }
end
end
-- Render infobox sections
local number = 1
for _, key in ipairs(propOrder) do
local rawValue = args[key]
if rawValue then
local fmt = sectionFormat[key] or { value = '%s' }
local renderedLabel  = string.format(fmt.label or '%s', key)
local renderedValue  = renderValue(frame, key, rawValue, fmt)
infoboxData:tag('div'):css({order = number}):addClass(fmt.class)
:tag('div'):addClass('infobox-label px-2'):wikitext(renderedLabel):done()
:tag('div'):addClass('infobox-value px-2'):wikitext(renderedValue):done()


number = number + 1
local values = data.__json.query
end
if type(values) ~= 'table' then
end
return {}, { 'The SMW response does not contain a query array.' }
-- SMW subobjects
if subobjects and #subobjects > 0 then
        for _, obj in ipairs(subobjects) do
local key      = obj.subobject
local label    = obj.label or key
local order    = obj.order or '500'
local subobjectData = args[key]
if key and subobjectData then
infoboxData:tag('div'):css({ ['order'] = order})
:addClass(p.infoboxSectionClasses()['twocolumns'])
:tag('div'):addClass('infobox-label px-2')
:wikitext(label)
:done()
:tag('div'):addClass('infobox-value px-2')
:tag('ul'):wikitext(subobjectData):done()
:done()
end
end
end
end
-- Printout
html
:wikitext(category)


return html
return values
end
end


return p
return p

Latest revision as of 09:17, 30 July 2026

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

local p = {}

function p.clean(value)
	if value == nil then
		return nil
	end

	value = mw.text.trim(tostring(value))
	return value ~= '' and value or nil
end

local function splitBy(str, sep)
	local t = {}
	for part in str:gmatch("[^" .. sep .. "]+") do
		table.insert(t, mw.text.trim(part))
	end
	return t
end

function p.setCategory(categoryString)
	if not categoryString then return end
	local category = string.format('[[Category:%s]]', categoryString)
	return category
end

function p.setShortDescription(description)
	if not description then return end
	local frame = mw.getCurrentFrame()
	local shortDescription = frame:preprocess(string.format('{{SHORTDESC:%s}}', description))
	return shortDescription
end

function p.setProperties(propMap, args)
	local props = {}
	for argName, def in pairs(propMap) do
    	local val = args[argName]
		if val ~= nil then
			local propKey = def[1]
			local sep     = def[2] or nil
			if type(val) == "string" and #def > 1 and sep == "file" then
				if val:find("^%w+:%s*") then
					val = val:gsub("^%w+:%s*", "")  -- strip namespace, we add it later
            	end
				val = string.format("File:%s", val)
			elseif type(val) == "string" and #def > 1 and sep then
				val = splitBy(val, sep)
			end
			props[propKey] = val
		end
	end
	return props
end


-- ------------------------------------------------------------------
-- |  p.createInfobox()
-- ------------------------------------------------------------------
-- Build an infobox from the template arguments that called this module.
--
-- Parameters
-- ----------
-- * frame      – The current Frame object.
-- * category   – Main category string to add before the page content.
-- * properties – A mapping of user‑argument names → SMW property keys
--                (optionally followed by a separator).  These are used
--                to set SMW properties via `mw.smw.set()`.
-- * sections   - A plan of sections and properties.
--
-- Returns
-- -------
-- An `mw.html` object that contains:
--
--   * The main category (via `p.setCategory(cat)`),
--   * A fully populated infobox
--
-- The returned HTML is rendered directly into the page where the module
-- was invoked.
--------------------------------------------------------------------
local function isSectionNotEmpty(args, rows)
	for _, rowKey in pairs(rows) do
        if args[rowKey] ~= nil then
			return true
        end
    end
    return false
end

function p.createInfobox(frame, cat, properties, sections, footer)
    local currentTitle = mw.title.getCurrentTitle().fullText
    local args = frame:getParent().args
    -- SMW properties
    local propMap = properties
    local props = p.setProperties(propMap, args) or {}
    mw.smw.set(props)
    -- Main category
    local category = p.setCategory(cat)
    -- ShortDescription
    local shortDescription = p.setShortDescription(args['Description'])
    -- Assemble
    local html = mw.html.create()
    local infobox = html:tag('div'):addClass('infobox d-flex flex-column card shadow-none rounded-0 bg-transparent mb-4 float-sm-right')
    -- Sections
    for key, cfg in pairs(sections) do
    	if isSectionNotEmpty(args, cfg.rows) == true then
        local section = infobox:tag('div'):addClass('infobox-block'):css({
            ['order'] = cfg.order or '0'
        })
        -- Heading
        local heading = cfg.heading or nil
        if heading and heading ~= '' then
            section:tag('div'):addClass('infobox-section-header'):wikitext(heading)
        end
        -- Content
        local data = {}
        local dataProperties = cfg.rows or {}
        for k, v in pairs(dataProperties) do
            local value = args[v]
            if value then
                data[v] = value
            end
        end
        -- Renderer
        local template = tostring(cfg.template) or 'Infobox/Generic'
        local content = frame:expandTemplate{
            title = template,
            args = data
        }
        section:tag('div'):addClass('infobox-row d-flex flex-column'):wikitext(content)
        end
    end
    -- Footer
	if footer and footer ~= '' then
		local renderFooter = frame:expandTemplate {
			title = footer
		}
		html:tag('div'):addClass('page-footer bg-dark'):wikitext('\n' .. renderFooter)
	end
    return html
        :wikitext(category)
        :wikitext(shortDescription)
end

function p.availablePropertyValues(property)
	property = mw.text.trim(tostring(property or ''))

	if property == '' then
		return {}, { 'A property name is required.' }
	end

	local params = mw.text.jsonEncode({
		limit = 1000,
		offset = 0,
		property = property,
		search = '',
		usageCount = true
	})
	local query = mw.uri.buildQueryString({
		action = 'smwbrowse',
		browse = 'pvalue',
		params = params,
		format = 'json'
	})
	local url = string.format(
		'%s%s/api.php?%s',
		'https://www.pcgamingshelter.eu',
		'/w',
		query
	)
	local data, errors = mw.ext.externalData.getExternalData({
		url = url,
		format = 'JSON'
	})

	if errors or not data or not data.__json then
		return {}, errors or { 'ExternalData returned no JSON data.' }
	end

	local values = data.__json.query
	if type(values) ~= 'table' then
		return {}, { 'The SMW response does not contain a query array.' }
	end

	return values
end

return p