Module:Calendar

From Angelina Jordan Wiki
Revision as of 20:45, 27 September 2025 by Most2dot0 (talk | contribs) (bugfixes, refactored)

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

local p = {}

local function trim(s)
    if not s then return nil end
    return (tostring(s):gsub("^%s*(.-)%s*$", "%1"))
end

local function escAttr(s)
    if not s then return "" end
    return tostring(s):gsub("&", "&amp;"):gsub('"', "&quot;"):gsub("<", "&lt;"):gsub(">", "&gt;")
end

function p.month(frame)
    local args = frame.args or (frame.getParent and frame:getParent().args) or {}
    local year = tonumber(trim(args.year))
    local month = tonumber(trim(args.month))
    if not year or not month then
        return "Error: module requires numeric |year= and |month= parameters."
    end
    if month < 1 or month > 12 then
        return "Error: |month must be between 1 and 12."
    end

    local tableName = trim(args.table) or "DatedEvents"
    local dateField = trim(args.datefield) or "Date"
    local urlField  = trim(args.urlfield)  or "Link"

    -- month bounds
    local daysInMonth = tonumber(os.date("*t", os.time{year=year, month=month+1, day=0}).day)
    local firstWday = tonumber(os.date("*t", os.time{year=year, month=month, day=1}).wday) -- 1 = Sunday

    -- Cargo check
    if not (mw and mw.ext and mw.ext.cargo and mw.ext.cargo.query) then
        return "Error: Cargo extension is not available."
    end

    local fields = dateField .. "," .. urlField
    local startDate = string.format("%04d-%02d-01", year, month)
    local endDate   = string.format("%04d-%02d-%02d", year, month, daysInMonth)
    local where = string.format("%s >= '%s' AND %s <= '%s'", dateField, startDate, dateField, endDate)

    local ok, results = pcall(mw.ext.cargo.query, tableName, fields, { where = where, limit = 1000 })
    if not ok then
        return "Error querying Cargo: " .. tostring(results)
    end
    results = results or {}

    -- map events by day (allow multiple entries per day)
    local events = {}
    for _, row in ipairs(results) do
        local dateVal = row[dateField] or row[1]
        local linkVal = row[urlField]  or row[2]
        if dateVal and linkVal then
            local y, m, d = tostring(dateVal):match("(%d%d%d%d)%-(%d%d)%-(%d%d)")
            if d then
                local dnum = tonumber(d)
                if dnum then
                    events[dnum] = events[dnum] or {}
                    table.insert(events[dnum], tostring(linkVal))
                end
            end
        end
    end

    -- build table
    local out = {}
    table.insert(out, '{| class="wikitable calendar" style="text-align:center;"')
    table.insert(out, '!Sun !!Mon !!Tue !!Wed !!Thu !!Fri !!Sat')

    local day, col = 1, 1
    table.insert(out, '|-')

    -- leading empty cells
    for i = 1, firstWday - 1 do
        table.insert(out, '| ')
        if i < 7 then table.insert(out, '||') end
        col = col + 1
    end

    while day <= daysInMonth do
        if col > 7 then
            table.insert(out, '\n|-')
            col = 1
        end

        if events[day] and #events[day] > 0 then
            local firstLink = trim(events[day][1])
            local tip = escAttr(table.concat(events[day], ", "))
            local cell = string.format(' style="color:red;" | <span title="%s">[[%s|%d]]</span>', tip, firstLink, day)
            table.insert(out, '|' .. cell)
        else
            table.insert(out, '|' .. day)
        end

        if col < 7 then table.insert(out, '||') end
        day = day + 1
        col = col + 1
    end

    -- trailing empty cells
    while col <= 7 do
        table.insert(out, '| ')
        if col < 7 then table.insert(out, '||') end
        col = col + 1
    end

    table.insert(out, '\n|}')
    return table.concat(out, ' ')
end

return p