Line 1: |
Line 1: |
| local p = {} | | local p = {} |
| local private = {} | | local private = {} |
| + | |
| + | -- whether to handle Stardew Valley beta fields (don't forget to comment or uncomment the beta fields in /doc) |
| + | local enableBeta = false |
| | | |
| --########## | | --########## |
Line 6: |
Line 9: |
| --########## | | --########## |
| -- Start a SMAPI compatibility table. | | -- Start a SMAPI compatibility table. |
− | -- @test mw.log(p.header()) | + | -- @test mw.log(p.header({})) |
− | function p.header() | + | function p.header(frame) |
| return | | return |
− | '<table class="wikitable sortable plainlinks" id="mod-list">' | + | private.style(frame) |
− | .. "<tr><th style=\"position: sticky; top: 0;\">mod name</th><th style=\"position: sticky; top: 0;\">author</th><th style=\"position: sticky; top: 0;\"><abbr title=\"This only shows whether a mod is *compatible*; it may have bugs unrelated to SMAPI compatibility.\" style=\"position: sticky; top: 0;\">compatibility</abbr></th><th style=\"position: sticky; top: 0;\">broke in</th><th style=\"position: sticky; top: 0; z-index: 100;\">source</th><th style=\"position: sticky; top: 0;\"> </th></tr>"; | + | .. '<table class="wikitable sortable plainlinks" id="mod-list">' |
| + | .. "<tr><th>mod name</th><th>author</th><th><abbr title=\"This only shows whether a mod is *compatible*; it may have bugs unrelated to SMAPI compatibility.\">compatibility</abbr></th><th>broke in</th><th>source</th><th> </th></tr>"; |
| end | | end |
| | | |
Line 17: |
Line 21: |
| function p.footer() | | function p.footer() |
| return '</table>' | | return '</table>' |
| + | end |
| + | |
| + | --- Render the SMAPI compatibility table based on JSON input. |
| + | -- @param frame The arguments passed to the script. |
| + | -- @test mw.log(p.table({ args = { [1]='[ { "name": "24h Clock", "author": "Lajna", // test\n"id": "Lajna.24hClock", "nexus": 1695, "github": "LajnaLegenden/Stardew_Valley_Mods", "brokeIn": "SMAPI 3.0", "unofficial": [ "1.0.1-unofficial.1-pathoschild", "https://community.playstarbound.com/threads/updating-mods-for-stardew-valley-1-3.142524/page-76#post-3342641" ] } ]' }})) |
| + | function p.table(frame) |
| + | -- parse data |
| + | local json = string.gsub(frame.args[1], '%s*//[^"\n]+', '') |
| + | local data = mw.text.jsonDecode(json, mw.text.JSON_TRY_FIXING) |
| + | |
| + | -- start table |
| + | local table = mw.html.create("table") |
| + | table:addClass("wikitable sortable plainlinks") |
| + | table:attr("id", "mod-list") |
| + | table:wikitext("<tr><th>mod name</th><th>author</th><th><abbr title=\"This only shows whether a mod is *compatible*; it may have bugs unrelated to SMAPI compatibility.\">compatibility</abbr></th><th>broke in</th><th>source</th><th> </th></tr>") |
| + | |
| + | -- add mod rows |
| + | for index,mod in pairs(data) do |
| + | -- temporarily passthrough args to avoid duplicating code until we migrate fully to JSON |
| + | -- (We need tostring on numeric fields since the previous code doesn't support numbers) |
| + | mod.chucklefish = private.toSafeString(mod.chucklefish) |
| + | mod.curse = private.toSafeString(mod.curse) |
| + | mod.moddrop = private.toSafeString(mod.moddrop) |
| + | mod.nexus = private.toSafeString(mod.nexus) |
| + | |
| + | local row = p.entry({ args = mod }) |
| + | table:node(row) |
| + | end |
| + | |
| + | -- return output |
| + | return private.style(frame) .. tostring(table) |
| end | | end |
| | | |
| --- Render a mod row in the SMAPI compatibility table. | | --- Render a mod row in the SMAPI compatibility table. |
| -- @param frame The arguments passed to the script. | | -- @param frame The arguments passed to the script. |
− | -- @test mw.log(p.entry({ args = { name="Lookup Anything, LookupAnything", author="Pathoschild, Pathos", id="Pathoschild.LookupAnything, LookupAnything", ["nexus id"]="541", ["chucklefish id"]="4250", ["github"]="Pathoschild/StardewMods", warnings="warning A, warning B", links="https://google.ca" }})) | + | -- @test mw.log(p.entry({ args = { name="Content Patcher, ContentPatcher", author="Pathoschild, Pathos", id="Pathoschild.ContentPatcher, ContentPatcher", nexus="1915", chucklefish="4250", curse="309243,content-patcher", github="Pathoschild/StardewMods", warnings="warning A, warning B" }})) |
| function p.entry(frame) | | function p.entry(frame) |
| -- read input args | | -- read input args |
Line 27: |
Line 62: |
| local authors = private.parseCommaDelimited(frame.args["author"] or '') | | local authors = private.parseCommaDelimited(frame.args["author"] or '') |
| local ids = private.parseCommaDelimited(frame.args["id"] or '') | | local ids = private.parseCommaDelimited(frame.args["id"] or '') |
− | local nexusId = private.emptyToNil(frame.args["nexus id"]) | + | local nexusId = private.emptyToNil(frame.args["nexus"]) |
| local github = private.emptyToNil(frame.args["github"]) | | local github = private.emptyToNil(frame.args["github"]) |
| local summary = private.emptyToNil(frame.args["summary"]) | | local summary = private.emptyToNil(frame.args["summary"]) |
Line 35: |
Line 70: |
| local unofficialVersion = private.emptyToNil(frame.args["unofficial version"]) | | local unofficialVersion = private.emptyToNil(frame.args["unofficial version"]) |
| local unofficialUrl = private.emptyToNil(frame.args["unofficial url"]) | | local unofficialUrl = private.emptyToNil(frame.args["unofficial url"]) |
− | local chucklefishId = private.emptyToNil(frame.args["chucklefish id"]) | + | local chucklefishId = private.emptyToNil(frame.args["chucklefish"]) |
− | local curseforgeId = private.emptyToNil(frame.args["curseforge id"]) | + | local curseforgeId = private.emptyToNil(frame.args["curse"]) |
− | local curseforgeKey = private.emptyToNil(frame.args["curseforge key"])
| + | local moddropId = private.emptyToNil(frame.args["moddrop"]) |
− | local moddropId = private.emptyToNil(frame.args["moddrop id"]) | |
| local customUrl = private.emptyToNil(frame.args["url"]) | | local customUrl = private.emptyToNil(frame.args["url"]) |
| local customSource = private.emptyToNil(frame.args["source"]) | | local customSource = private.emptyToNil(frame.args["source"]) |
− | local pullRequestUrl = private.emptyToNil(frame.args["pull request"])
| |
− | local links = private.parseCommaDelimited(frame.args["links"])
| |
| | | |
| local warnings = private.parseCommaDelimited(frame.args["warnings"]) | | local warnings = private.parseCommaDelimited(frame.args["warnings"]) |
| local devNote = private.emptyToNil(frame.args["dev note"]) | | local devNote = private.emptyToNil(frame.args["dev note"]) |
| local contentPackFor = private.emptyToNil(frame.args["content pack for"]) | | local contentPackFor = private.emptyToNil(frame.args["content pack for"]) |
− | local mapLocalVersions = private.emptyToNil(frame.args["map local versions"])
| |
− | local mapRemoteVersions = private.emptyToNil(frame.args["map remote versions"])
| |
− | local changeUpdateKeys = private.emptyToNil(frame.args["change update keys"])
| |
| | | |
− | local betaSummary = private.emptyToNil(frame.args["beta summary"]) | + | local betaSummary = nil |
− | local betaBrokeIn = private.emptyToNil(frame.args["beta broke in"])
| + | local betaBrokeIn = nil |
− | local betaStatus = private.emptyToNil(frame.args["beta status"])
| + | local betaStatus = nil |
− | local betaUnofficialVersion = private.emptyToNil(frame.args["beta unofficial version"])
| + | local betaUnofficialVersion = nil |
− | local betaUnofficialUrl = private.emptyToNil(frame.args["beta unofficial url"])
| + | local betaUnofficialUrl = nil |
| + | if enableBeta then |
| + | betaSummary = private.emptyToNil(frame.args["beta summary"]) |
| + | betaBrokeIn = private.emptyToNil(frame.args["beta broke in"]) |
| + | betaStatus = private.emptyToNil(frame.args["beta status"]) |
| + | betaUnofficialVersion = private.emptyToNil(frame.args["beta unofficial version"]) |
| + | betaUnofficialUrl = private.emptyToNil(frame.args["beta unofficial url"]) |
| + | end |
| | | |
| -- get source url | | -- get source url |
Line 67: |
Line 103: |
| local compat = private.getCompatInfo(status, summary, brokeIn, unofficialVersion, unofficialUrl, hasSource) | | local compat = private.getCompatInfo(status, summary, brokeIn, unofficialVersion, unofficialUrl, hasSource) |
| local betaCompat = nil | | local betaCompat = nil |
− | if betaStatus or betaBrokeIn or betaUnofficialUrl or betaUnofficialVersion then | + | if enableBeta and (betaStatus or betaBrokeIn or betaUnofficialUrl or betaUnofficialVersion) then |
| betaCompat = private.getCompatInfo(betaStatus, betaSummary, betaBrokeIn, betaUnofficialVersion, betaUnofficialUrl, hasSource) | | betaCompat = private.getCompatInfo(betaStatus, betaSummary, betaBrokeIn, betaUnofficialVersion, betaUnofficialUrl, hasSource) |
| end | | end |
Line 78: |
Line 114: |
| url = "https://www.moddrop.com/stardew-valley/mods/" .. mw.uri.encode(moddropId, "PATH") | | url = "https://www.moddrop.com/stardew-valley/mods/" .. mw.uri.encode(moddropId, "PATH") |
| elseif curseforgeId then | | elseif curseforgeId then |
− | url = "https://www.curseforge.com/stardewvalley/mods/" .. mw.uri.encode(curseforgeKey, "PATH") | + | url = "https://www.curseforge.com/projects/" .. mw.uri.encode(curseforgeId, "PATH") |
| elseif chucklefishId then | | elseif chucklefishId then |
| url = "https://community.playstarbound.com/resources/" .. mw.uri.encode(chucklefishId, "PATH") | | url = "https://community.playstarbound.com/resources/" .. mw.uri.encode(chucklefishId, "PATH") |
− | else | + | elseif customUrl then |
| url = customUrl | | url = customUrl |
− | end
| + | elseif hasSource then |
− | | + | url = sourceUrl |
− | -- get background color
| |
− | local background = '#999'
| |
− | if compat.status == "ok" or compat.status == "optional" then
| |
− | background = '#9F9'
| |
− | elseif compat.status == "workaround" or compat.status == "unofficial" then
| |
− | background = '#CF9'
| |
− | elseif compat.status == "broken" then
| |
− | background = '#F99'
| |
− | elseif compat.status == "obsolete" or compat.status == "abandoned" then | |
− | background = '#999' | |
| end | | end |
| | | |
Line 106: |
Line 132: |
| row:attr("data-cf-id", chucklefishId) | | row:attr("data-cf-id", chucklefishId) |
| row:attr("data-curseforge-id", curseforgeId) | | row:attr("data-curseforge-id", curseforgeId) |
− | row:attr("data-curseforge-key", curseforgeKey)
| |
| row:attr("data-moddrop-id", moddropId) | | row:attr("data-moddrop-id", moddropId) |
| row:attr("data-nexus-id", nexusId) | | row:attr("data-nexus-id", nexusId) |
Line 117: |
Line 142: |
| row:attr("data-unofficial-version", compat.unofficialVersion) | | row:attr("data-unofficial-version", compat.unofficialVersion) |
| row:attr("data-unofficial-url", compat.unofficialUrl) | | row:attr("data-unofficial-url", compat.unofficialUrl) |
− | row:attr("data-beta-status", betaCompat and betaCompat.status) | + | if enableBeta then |
− | row:attr("data-beta-summary", betaCompat and betaCompat.summary)
| + | row:attr("data-beta-status", betaCompat and betaCompat.status) |
− | row:attr("data-beta-broke-in", betaCompat and betaCompat.brokeIn)
| + | row:attr("data-beta-summary", betaCompat and betaCompat.summary) |
− | row:attr("data-beta-unofficial-version", betaCompat and betaCompat.unofficialVersion)
| + | row:attr("data-beta-broke-in", betaCompat and betaCompat.brokeIn) |
− | row:attr("data-beta-unofficial-url", betaCompat and betaCompat.unofficialUrl)
| + | row:attr("data-beta-unofficial-version", betaCompat and betaCompat.unofficialVersion) |
− | row:attr("data-pr", pullRequestUrl) | + | row:attr("data-beta-unofficial-url", betaCompat and betaCompat.unofficialUrl) |
| + | end |
| row:attr("data-warnings", private.emptyToNil(table.concat(warnings, ","))) | | row:attr("data-warnings", private.emptyToNil(table.concat(warnings, ","))) |
| row:attr("data-content-pack-for", contentPackFor) | | row:attr("data-content-pack-for", contentPackFor) |
| row:attr("data-dev-note", devNote) | | row:attr("data-dev-note", devNote) |
− | row:attr("data-map-local-versions", mapLocalVersions)
| |
− | row:attr("data-map-remote-versions", mapRemoteVersions)
| |
− | row:attr("data-change-update-keys", changeUpdateKeys)
| |
− | row:attr("style", "line-height: 1em; background: " .. background .. ";")
| |
| row:newline() | | row:newline() |
| | | |
Line 193: |
Line 215: |
| if betaCompat ~= nil then | | if betaCompat ~= nil then |
| field:wikitext("<br />") | | field:wikitext("<br />") |
− | field:wikitext("'''SDV beta only:''' <span class=\"mod-beta-summary\">" .. betaCompat.summaryIcon .. " " .. betaCompat.summary .. "</span>") | + | field:wikitext("'''SDV 1.6 beta only:''' <span class=\"mod-beta-summary\">" .. betaCompat.summaryIcon .. " " .. betaCompat.summary .. "</span>") |
| if betaCompat.status == "optional" then | | if betaCompat.status == "optional" then |
| field:wikitext("<ref name=\"optional-update\" />") | | field:wikitext("<ref name=\"optional-update\" />") |
Line 230: |
Line 252: |
| do | | do |
| if sourceUrl then | | if sourceUrl then |
− | row:wikitext("<td>[" .. sourceUrl .. " source]</td>") | + | row:wikitext("<td class=\"mod-source\">[" .. sourceUrl .. " source]</td>") |
| else | | else |
− | row:wikitext("<td><span style=\"color: red; font-size: 0.85em; opacity: 0.5;\">closed source</span></td>") | + | row:wikitext("<td class=\"mod-source\"><span>closed source</span></td>") |
| end | | end |
| row:newline() | | row:newline() |
Line 240: |
Line 262: |
| do | | do |
| local field = mw.html.create("td") | | local field = mw.html.create("td") |
− | field:attr("style", "font-size: 0.8em") | + | field:attr("class", "mod-metadata") |
| | | |
| -- anchor | | -- anchor |
| field:wikitext("[[#" .. (names[1] or '') .. "|#]] ") | | field:wikitext("[[#" .. (names[1] or '') .. "|#]] ") |
− |
| |
− | -- pull request
| |
− | if pullRequestUrl then
| |
− | field:wikitext("[" .. pullRequestUrl .. " PR] ")
| |
− | end
| |
| | | |
| -- dev note | | -- dev note |
Line 262: |
Line 279: |
| if #ids == 0 then | | if #ids == 0 then |
| field:wikitext("[⚠ no id] ") | | field:wikitext("[⚠ no id] ") |
− | end
| |
− | if (not curseforgeId) ~= (not curseforgeKey) then
| |
− | field:wikitext("<abbr title=\"The mod data is invalid: can't specify Curseforge key or ID without the other.\">[⚠ invalid data]</abbr>")
| |
| end | | end |
| | | |
Line 273: |
Line 287: |
| end | | end |
| | | |
− | -- Print an HTML attribute (like key="value") if a non-empty value is specified. This is a temporary method during the migration to Lua. | + | |
| + | --########## |
| + | --## Private functions |
| + | --########## |
| + | -- Get the <templatestyles> tag for the module's stylesheet. |
| -- @param frame The arguments passed to the script. | | -- @param frame The arguments passed to the script. |
− | -- @test mw.log(p.printAttribute({ args = { "someKey", "some \"'<> value" }}))
| + | function private.style(frame) |
− | function p.printAttribute(frame) | + | if frame.extensionTag ~= nil then |
− | local key = frame.args[1] | + | return frame:extensionTag('templatestyles', '', {src = 'Module:SMAPI compatibility/styles.css'}) |
− | local value = frame.args[2]
| |
− | | |
− | if value == nil or value == "" then
| |
− | return "" | |
| else | | else |
− | return key .. "=\"" .. mw.text.encode(value) .. "\"" | + | return "" -- called from the debug console |
| end | | end |
| end | | end |
| | | |
− |
| |
− | --##########
| |
− | --## Private functions
| |
− | --##########
| |
| -- Get the normalised compatibility info for a mod. | | -- Get the normalised compatibility info for a mod. |
| -- @param status The specified status code. If nil or blank, it'll be derived from the other fields. | | -- @param status The specified status code. If nil or blank, it'll be derived from the other fields. |
Line 357: |
Line 367: |
| unofficialUrl = unofficialUrl | | unofficialUrl = unofficialUrl |
| } | | } |
| + | end |
| + | |
| + | -- Call tostring() on the value if it's not nil, else return the value as-is. |
| + | -- @param value The value to format. |
| + | function private.toSafeString(value) |
| + | if value then |
| + | return tostring(value) |
| + | else |
| + | return nil |
| + | end |
| end | | end |
| | | |
| -- Get a nil value if the specified value is an empty string, else return the value unchanged. | | -- Get a nil value if the specified value is an empty string, else return the value unchanged. |
− | -- @param value The string to check. | + | -- @param value The string to format. |
| function private.emptyToNil(value) | | function private.emptyToNil(value) |
| if value ~= "" then | | if value ~= "" then |