Module:DynamicYSWSList: Difference between revisions
Jump to navigation
Jump to search
U080F22CTJN (talk | contribs) add auto link formatting? |
U080F22CTJN (talk | contribs) m does this work |
||
| Line 98: | Line 98: | ||
for _, item in ipairs(programs) do | for _, item in ipairs(programs) do | ||
out[#out+1] = '|- class="ysws-program" data-start="' .. (item.startDate or '') .. '" data-end="' .. (item.endDate or '') .. '"' | out[#out+1] = '|- class="ysws-program" data-start="' .. (item.startDate or '') .. '" data-end="' .. (item.endDate or '') .. '"' | ||
out[#out+1] = | out[#out+1] = '! scope="row" class="ysws-name" | [[' .. item.name .. '|' .. safe(item.name) .. ']]' | ||
out[#out+1] = '| class="ysws-description" | ' .. (item.description ~= '' and safe(item.description) or '—') | out[#out+1] = '| class="ysws-description" | ' .. (item.description ~= '' and safe(item.description) or '—') | ||
out[#out+1] = dateCell(item.startDate, 'ysws-start') | out[#out+1] = dateCell(item.startDate, 'ysws-start') | ||
Latest revision as of 14:23, 5 November 2026
Documentation for this module may be created at Module:DynamicYSWSList/doc
-- Module:DynamicYSWSList
-- Public API only. Wikitext table avoids unsupported HTML tags in MediaWiki.
local p = {}
local API_URL = 'https://hackclub.com/api/v1/events'
local MONTHS = {'January','February','March','April','May','June','July','August','September','October','November','December'}
local function str(v)
if type(v) ~= 'string' and type(v) ~= 'number' then return '' end
return mw.text.trim(tostring(v))
end
local function first(...)
for i = 1, select('#', ...) do
local v = str(select(i, ...))
if v ~= '' then return v end
end
return ''
end
local function safe(v)
-- Escape user/API text so it cannot inject table delimiters or wiki markup.
return mw.text.nowiki(str(v))
end
local function isoDate(v)
local y,m,d = str(v):match('^(%d%d%d%d)%-(%d%d)%-(%d%d)')
if not y then return nil end
y,m,d = tonumber(y),tonumber(m),tonumber(d)
if m < 1 or m > 12 then return nil end
local lengths = {31,28,31,30,31,30,31,31,30,31,30,31}
if y % 4 == 0 and (y % 100 ~= 0 or y % 400 == 0) then lengths[2] = 29 end
if d < 1 or d > lengths[m] then return nil end
return string.format('%04d-%02d-%02d',y,m,d)
end
local function displayDate(date)
if not date then return '—' end
local y,m,d = date:match('^(%d%d%d%d)%-(%d%d)%-(%d%d)$')
return string.format('%d %s %s', tonumber(d), MONTHS[tonumber(m)], y)
end
local function dateCell(date, class)
return '| data-sort-value="' .. (date or '9999-12-31') .. '" class="' .. class .. '" | ' .. displayDate(date)
end
local function validUrl(v)
local url = str(v)
if url:match('^https?://[^%s%[%]<>|{}"\']+$') then return url end
return nil
end
local function description(v)
local s = str(v):gsub('<[^>]*>', ''):gsub('%s+', ' ')
s = mw.text.trim(mw.text.decode(s))
if mw.ustring.len(s) > 240 then s = mw.ustring.sub(s, 1, 237) .. '…' end
return s
end
local function fetch()
if not (mw.ext and mw.ext.externalData and type(mw.ext.externalData.getExternalData) == 'function') then
return nil, 'External Data is unavailable.'
end
local ok, rows = pcall(mw.ext.externalData.getExternalData, {
url = API_URL,
format = 'json with jsonpath',
data = {
name = '$.data[*].name',
description = '$.data[*].description',
url = '$.data[*].url',
startDate = '$.data[*].startDate',
endDate = '$.data[*].endDate'
}
})
if not ok or type(rows) ~= 'table' then return nil, 'Could not load public events.' end
return rows
end
function p.main(frame)
local data, err = fetch()
if not data then return '<strong class="error">' .. safe(err) .. '</strong>' end
local programs = {}
for _, row in ipairs(data) do
if type(row) == 'table' then
local name = first(row.name, row.title)
if name ~= '' then
programs[#programs + 1] = {
name = name,
description = description(first(row.description, row.summary)),
startDate = isoDate(first(row.startDate,row.start_date,row.start)),
endDate = isoDate(first(row.endDate,row.end_date,row['end'])),
url = validUrl(first(row.url,row.website,row.link))
}
end
end
end
table.sort(programs, function(a,b)
return mw.ustring.lower(a.name) < mw.ustring.lower(b.name)
end)
if #programs == 0 then return 'No public programs were returned.' end
local out = {
'<div class="ysws-directory">',
'<p class="ysws-directory-count">' .. #programs .. ' public event entries listed.</p>',
'<div class="ysws-table-wrapper">',
'{| class="wikitable sortable ysws-table"',
'! Program\n! Description\n! Start date\n! End date\n! Status\n! Website'
}
for _, item in ipairs(programs) do
out[#out+1] = '|- class="ysws-program" data-start="' .. (item.startDate or '') .. '" data-end="' .. (item.endDate or '') .. '"'
out[#out+1] = '! scope="row" class="ysws-name" | [[' .. item.name .. '|' .. safe(item.name) .. ']]'
out[#out+1] = '| class="ysws-description" | ' .. (item.description ~= '' and safe(item.description) or '—')
out[#out+1] = dateCell(item.startDate, 'ysws-start')
out[#out+1] = dateCell(item.endDate, 'ysws-end')
out[#out+1] = '| class="ysws-status" | Unknown'
out[#out+1] = '| class="ysws-website" | ' .. (item.url and ('[' .. item.url .. ' Website]') or '—')
end
out[#out+1] = '|}'
out[#out+1] = '</div>'
out[#out+1] = '<p class="ysws-source-note">Generated from the public Hack Club events API. Dates and descriptions reflect the public feed.</p>'
out[#out+1] = '</div>'
return frame:preprocess(table.concat(out, '\n'))
end
return p