Difference between revisions of "Module:SMAPI compatibility"
Jump to navigation
Jump to search
Pathoschild (talk | contribs) (tweak printAttribute usage) |
Pathoschild (talk | contribs) (minor optimisations (minimise tables, no need to trim named template arguments, build HTML directly where safe, use numeric for loops), fix missing commas in alt names) |
||
Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
local private = {} | local private = {} | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
--########## | --########## | ||
Line 23: | Line 8: | ||
-- @test mw.log(p.header()) | -- @test mw.log(p.header()) | ||
function p.header() | function p.header() | ||
− | + | return | |
− | + | '<table class="wikitable plainlinks">' | |
− | + | .. "<tr><th>mod name</th><th>author</th><th>compatibility</th><th>broke in</th><th>source</th><th> </th></tr>"; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
Line 45: | Line 24: | ||
function p.entry(frame) | function p.entry(frame) | ||
-- read input args | -- read input args | ||
− | local names = private.parseCommaDelimited( | + | local names = private.parseCommaDelimited((frame.args["name"] or '') .. ',' .. (frame.args["name2"] or '')) |
− | local authors = private.parseCommaDelimited( | + | local authors = private.parseCommaDelimited((frame.args["author"] or '') .. ',' .. (frame.args["author2"] or '')) |
− | local ids = private.parseCommaDelimited( | + | local ids = private.parseCommaDelimited((frame.args["id"] or '') .. ',' .. (frame.args["old ids"] or '')) |
− | local nexusID = private.emptyToNil | + | local nexusID = private.emptyToNil(frame.args["nexus id"]) |
− | local github = private.emptyToNil | + | local github = private.emptyToNil(frame.args["github"]) |
− | local summary = private.emptyToNil | + | local summary = private.emptyToNil(frame.args["summary"]) |
− | local brokeIn = private.emptyToNil | + | local brokeIn = private.emptyToNil(frame.args["broke in"]) |
− | local status = private.emptyToNil | + | local status = private.emptyToNil(frame.args["status"]) |
− | local unofficialVersion = private.emptyToNil | + | local unofficialVersion = private.emptyToNil(frame.args["unofficial version"]) |
− | local unofficialUrl = private.emptyToNil | + | local unofficialUrl = private.emptyToNil(frame.args["unofficial url"]) |
− | local oldIDs = private.emptyToNil | + | local oldIDs = private.emptyToNil(frame.args["old ids"]) |
− | local chucklefishID = private.emptyToNil | + | local chucklefishID = private.emptyToNil(frame.args["cf id"]) |
− | local customUrl = private.emptyToNil | + | local customUrl = private.emptyToNil(frame.args["url"]) |
− | local customSource = private.emptyToNil | + | local customSource = private.emptyToNil(frame.args["source"]) |
− | local name2 = private.emptyToNil | + | local name2 = private.emptyToNil(frame.args["name2"]) |
− | local author2 = private.emptyToNil | + | local author2 = private.emptyToNil(frame.args["author2"]) |
local links = private.parseCommaDelimited(frame.args["links"]) | local links = private.parseCommaDelimited(frame.args["links"]) | ||
local warnings = private.parseCommaDelimited(frame.args["warnings"]) | local warnings = private.parseCommaDelimited(frame.args["warnings"]) | ||
− | local betaSummary = private.emptyToNil | + | local betaSummary = private.emptyToNil(frame.args["beta summary"]) |
− | local betaBrokeIn = private.emptyToNil | + | local betaBrokeIn = private.emptyToNil(frame.args["beta broke in"]) |
− | local betaStatus = private.emptyToNil | + | local betaStatus = private.emptyToNil(frame.args["beta status"]) |
− | local betaUnofficialVersion = private.emptyToNil | + | local betaUnofficialVersion = private.emptyToNil(frame.args["beta unofficial version"]) |
− | local betaUnofficialUrl = private.emptyToNil | + | local betaUnofficialUrl = private.emptyToNil(frame.args["beta unofficial url"]) |
-- parse compatibility | -- parse compatibility | ||
Line 96: | Line 75: | ||
-- get background color | -- get background color | ||
local background = '#999' | local background = '#999' | ||
− | if compat.status == | + | if compat.status == "ok" or compat.status == "optional" then |
background = '#9F9' | background = '#9F9' | ||
− | elseif compat.status == | + | elseif compat.status == "workaround" or compat.status == "unofficial" then |
background = '#CF9' | background = '#CF9' | ||
− | elseif compat.status == | + | elseif compat.status == "broken" then |
background = '#F99' | background = '#F99' | ||
− | elseif compat.status == | + | elseif compat.status == "obsolete" or compat.status == "abandoned" then |
background = '#999' | background = '#999' | ||
end | end | ||
Line 109: | Line 88: | ||
local row = mw.html.create("tr") | local row = mw.html.create("tr") | ||
row:addClass("mod") | row:addClass("mod") | ||
− | row:attr("id", names[1] and mw.uri.anchorEncode(names[1]) | + | row:attr("id", names[1] and mw.uri.anchorEncode(names[1])); |
row:attr("data-name", table.concat(names, ",")) | row:attr("data-name", table.concat(names, ",")) | ||
row:attr("data-id", table.concat(ids, ",")) | row:attr("data-id", table.concat(ids, ",")) | ||
Line 123: | Line 102: | ||
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 | + | row:attr("data-beta-status", betaCompat and betaCompat.status) |
− | row:attr("data-beta-summary", betaCompat and betaCompat.summary | + | row:attr("data-beta-summary", betaCompat and betaCompat.summary) |
− | row:attr("data-beta-broke-in", betaCompat and betaCompat.brokeIn | + | row:attr("data-beta-broke-in", betaCompat and betaCompat.brokeIn) |
− | row:attr("data-beta-unofficial-version", betaCompat and betaCompat.unofficialVersion | + | row:attr("data-beta-unofficial-version", betaCompat and betaCompat.unofficialVersion) |
− | row:attr("data-beta-unofficial-url", betaCompat and betaCompat.unofficialUrl | + | row:attr("data-beta-unofficial-url", betaCompat and betaCompat.unofficialUrl) |
row:attr("data-warnings", private.emptyToNil(table.concat(warnings, ","))) | row:attr("data-warnings", private.emptyToNil(table.concat(warnings, ","))) | ||
row:attr("style", "line-height: 1em; background: " .. background .. ";") | row:attr("style", "line-height: 1em; background: " .. background .. ";") | ||
Line 135: | Line 114: | ||
do | do | ||
local field = mw.html.create("td") | local field = mw.html.create("td") | ||
+ | |||
field:wikitext("[" .. (url or '') .. " " .. (names[1] or '') .. "]") | field:wikitext("[" .. (url or '') .. " " .. (names[1] or '') .. "]") | ||
− | + | local nameCount = #names | |
− | + | if nameCount > 1 then | |
− | for | + | field:wikitext("<br /><small>(aka ") |
− | if | + | for i = 1, nameCount do |
− | + | if i > 1 then | |
+ | field:wikitext(names[i]) | ||
+ | if i < nameCount then | ||
+ | field:wikitext(", ") | ||
+ | end | ||
end | end | ||
end | end | ||
− | + | field:wikitext(")</small>") | |
− | |||
− | |||
end | end | ||
Line 158: | Line 140: | ||
field:wikitext(authors[1]) | field:wikitext(authors[1]) | ||
− | + | ||
− | + | local authorCount = #authors | |
− | for | + | if authorCount > 1 then |
− | if | + | field:wikitext("<br /><small>(aka ") |
− | + | for i = 1, authorCount do | |
+ | if i > 1 then | ||
+ | field:wikitext(authors[i]) | ||
+ | if i < authorCount then | ||
+ | field:wikitext(", ") | ||
+ | end | ||
end | end | ||
end | end | ||
− | + | field:wikitext(")</small>") | |
− | |||
− | |||
end | end | ||
Line 180: | Line 165: | ||
-- stable status | -- stable status | ||
field:wikitext(compat.summaryIcon .. " " .. compat.summary) | field:wikitext(compat.summaryIcon .. " " .. compat.summary) | ||
− | if compat.status == | + | if compat.status == "optional" then |
field:wikitext("<ref name=\"optional-update\" />") | field:wikitext("<ref name=\"optional-update\" />") | ||
end | end | ||
Line 186: | Line 171: | ||
-- beta status | -- beta status | ||
if betaCompat ~= nill then | if betaCompat ~= nill then | ||
− | field: | + | field:wikitext("<br />") |
field:wikitext("'''SDV beta only:''' " .. betaCompat.summaryIcon .. " " .. betaCompat.summary) | field:wikitext("'''SDV beta only:''' " .. betaCompat.summaryIcon .. " " .. betaCompat.summary) | ||
− | if betaCompat.status == | + | if betaCompat.status == "optional" then |
field:wikitext("<ref name=\"optional-update\" />") | field:wikitext("<ref name=\"optional-update\" />") | ||
end | end | ||
Line 194: | Line 179: | ||
-- warnings | -- warnings | ||
− | + | do | |
− | + | local warningCount = #warnings | |
− | + | if warningCount > 0 then | |
− | + | for i = 1, warningCount do | |
+ | field:wikitext("<br />⚠ " .. warnings[i]) | ||
+ | end | ||
end | end | ||
end | end | ||
Line 221: | Line 208: | ||
-- add 'source' field | -- add 'source' field | ||
do | do | ||
− | |||
− | |||
if sourceUrl then | if sourceUrl then | ||
− | + | row:wikitext("<td>[" .. sourceUrl .. " source]</td>") | |
else | else | ||
− | + | row:wikitext("<td><span style=\"color: red; font-size: 0.85em; opacity: 0.5;\">closed source</span></td>") | |
end | end | ||
− | |||
− | |||
row:newline() | row:newline() | ||
end | end | ||
Line 242: | Line 225: | ||
-- reference links | -- reference links | ||
− | for | + | do |
− | + | local linkCount = #links | |
+ | for i = 1, linkCount do | ||
+ | field:wikitext("[" .. links[i] .. " " .. i .. "] ") | ||
+ | end | ||
end | end | ||
Line 252: | Line 238: | ||
-- backwards-compatible metadata (temporary) | -- backwards-compatible metadata (temporary) | ||
− | + | field:wikitext('<div div class="mod-metadata" style="display: none;">') | |
− | + | field:wikitext('<div class="mod-anchor">' .. (names[1] and mw.uri.anchorEncode(names[1])) .. "</div>") | |
− | + | field:wikitext('<div class="mod-id">' .. mw.text.encode(table.concat(ids, ",")) .. "</div>") | |
− | + | field:wikitext('<div class="mod-url">' .. mw.text.encode(url) .. "</div>") | |
− | |||
− | |||
if nexusID ~= nil then | if nexusID ~= nil then | ||
− | + | field:wikitext('<div class="mod-nexus-id">' .. mw.text.encode(nexusID) .. "</div>") | |
end | end | ||
if chucklefishID ~= nil then | if chucklefishID ~= nil then | ||
− | + | field:wikitext('<div class="mod-cf-id">' .. mw.text.encode(chucklefishID) .. "</div>") | |
end | end | ||
if github ~= nil then | if github ~= nil then | ||
− | + | field:wikitext('<div class="mod-github">' .. mw.text.encode(github) .. '</div>') | |
end | end | ||
if customSource ~= nil then | if customSource ~= nil then | ||
− | + | field:wikitext('<div class="mod-custom-source">' .. mw.text.encode(customSource) .. '</div>') | |
end | end | ||
− | + | field:wikitext('<div class="mod-status">' .. mw.text.encode(compat.status) .. '</div>') | |
if compat.brokeIn ~= nil then | if compat.brokeIn ~= nil then | ||
− | + | field:wikitext('<div class="mod-broke-in">' .. mw.text.encode(compat.brokeIn) .. '</div>') | |
end | end | ||
if compat.unofficialVersion ~= nil and compat.unofficialUrl ~= nil then | if compat.unofficialVersion ~= nil and compat.unofficialUrl ~= nil then | ||
− | + | field:wikitext('<div class="mod-unofficial-version">' .. mw.text.encode(compat.unofficialVersion) .. '</div>') | |
− | + | field:wikitext('<div class="mod-unofficial-url">' .. mw.text.encode(compat.unofficialUrl) .. '</div>') | |
end | end | ||
if betaCompat ~= nil then | if betaCompat ~= nil then | ||
− | + | field:wikitext('<div class="mod-beta-status">' .. mw.text.encode(betaCompat.status) .. '</div>') | |
if betaCompat.brokeIn ~= nil then | if betaCompat.brokeIn ~= nil then | ||
− | + | field:wikitext('<div class="mod-beta-broke-in">' .. mw.text.encode(betaCompat.brokeIn) .. '</div>') | |
end | end | ||
if betaCompat.unofficialVersion ~= nil and betaCompat.unofficialUrl ~= nil then | if betaCompat.unofficialVersion ~= nil and betaCompat.unofficialUrl ~= nil then | ||
− | + | field:wikitext('<div class="mod-beta-unofficial-version">' .. mw.text.encode(betaCompat.unofficialVersion) .. '</div>') | |
− | + | field:wikitext('<div class="mod-beta-unofficial-url">' .. mw.text.encode(betaCompat.unofficialUrl) .. '</div>') | |
end | end | ||
end | end | ||
if #warnings > 0 then | if #warnings > 0 then | ||
− | + | field:wikitext('<div class="mod-warnings">' .. mw.text.encode(table.concat(warnings, ",")) .. '</div>') | |
end | end | ||
+ | field.wikitext("</div>") | ||
− | |||
row:node(field) | row:node(field) | ||
end | end | ||
Line 308: | Line 292: | ||
if value == nil or value == "" then | if value == nil or value == "" then | ||
return "" | return "" | ||
− | else | + | else |
return key .. "=\"" .. mw.text.encode(value) .. "\"" | return key .. "=\"" .. mw.text.encode(value) .. "\"" | ||
end | end | ||
Line 318: | Line 302: | ||
--########## | --########## | ||
-- Get the normalised compatibility info for a mod. | -- Get the normalised compatibility info for a mod. | ||
− | -- @param status The specified status code | + | -- @param status The specified status code. If nil or blank, it'll be derived from the other fields. |
-- @param summary A human-readable summary of the compatibility info. If nil or blank, it'll be derived from the other fields. | -- @param summary A human-readable summary of the compatibility info. If nil or blank, it'll be derived from the other fields. | ||
-- @param brokeIn The SMAPI or Stardew Valley version which broke the mod, if applicable. | -- @param brokeIn The SMAPI or Stardew Valley version which broke the mod, if applicable. | ||
Line 325: | Line 309: | ||
-- @param hasSource Whether the mod has public source code available. | -- @param hasSource Whether the mod has public source code available. | ||
function private.getCompatInfo(status, summary, brokeIn, unofficialVersion, unofficialUrl, hasSource) | function private.getCompatInfo(status, summary, brokeIn, unofficialVersion, unofficialUrl, hasSource) | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
-- derive status | -- derive status | ||
if status == nil then | if status == nil then | ||
if unofficialVersion ~= nil then | if unofficialVersion ~= nil then | ||
− | status = | + | status = "unofficial" |
elseif brokeIn ~= nil then | elseif brokeIn ~= nil then | ||
− | status = | + | status = "broken" |
else | else | ||
− | status = | + | status = "ok" |
end | end | ||
end | end | ||
Line 345: | Line 322: | ||
-- derive summary icon | -- derive summary icon | ||
local summaryIcon = "✓" | local summaryIcon = "✓" | ||
− | if status == | + | if status == "unofficial" or status == "workaround" then |
summaryIcon = "⚠" | summaryIcon = "⚠" | ||
− | elseif status == | + | elseif status == "broken" and hasSource then |
summaryIcon = "↻" | summaryIcon = "↻" | ||
− | elseif status == | + | elseif status == "broken" or status == "obsolete" or status == "abandoned" then |
summaryIcon = "✖" | summaryIcon = "✖" | ||
end | end | ||
Line 355: | Line 332: | ||
-- derive summary | -- derive summary | ||
if summary == nil then | if summary == nil then | ||
− | if status == | + | if status == "ok" then |
summary = "use latest version." | summary = "use latest version." | ||
− | elseif status == | + | elseif status == "optional" then |
summary = "use optional download." | summary = "use optional download." | ||
− | elseif status == | + | elseif status == "unofficial" then |
summary = "broken, use [" .. (unofficialUrl or "") .. " " .. "unofficial version]" | summary = "broken, use [" .. (unofficialUrl or "") .. " " .. "unofficial version]" | ||
if unofficialVersion ~= nil then | if unofficialVersion ~= nil then | ||
Line 365: | Line 342: | ||
end | end | ||
summary = summary .. "." | summary = summary .. "." | ||
− | elseif status == | + | elseif status == "workaround" then |
summary = "broken. '''error:''' should specify summary." | summary = "broken. '''error:''' should specify summary." | ||
− | elseif status == | + | elseif status == "broken" then |
if hasSource then | if hasSource then | ||
summary = "broken, not updated yet." | summary = "broken, not updated yet." | ||
Line 373: | Line 350: | ||
summary = "broken, not open-source." | summary = "broken, not open-source." | ||
end | end | ||
− | elseif status == | + | elseif status == "obsolete" then |
summary = "obsolete." | summary = "obsolete." | ||
− | elseif status == | + | elseif status == "abandoned" then |
summary = "no longer maintained." | summary = "no longer maintained." | ||
else | else | ||
Line 390: | Line 367: | ||
unofficialUrl = unofficialUrl | unofficialUrl = unofficialUrl | ||
} | } | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
Line 418: | Line 385: | ||
if value ~= nil then | if value ~= nil then | ||
− | + | local values = mw.text.split(value, ",", true) | |
− | v = mw.text.trim( | + | for i = 1, #values do |
+ | v = mw.text.trim(values[i]) | ||
if v ~= "" then | if v ~= "" then | ||
table.insert(result, v) | table.insert(result, v) |
Revision as of 19:41, 24 October 2018
This module provides the table structure and data for the Modding:Mod compatibility list.
Examples
Compatible mod
{{#invoke:SMAPI compatibility|entry |name = Lookup Anything |author = Pathoschild |id = Pathoschild.LookupAnything |nexus = 541 |github = Pathoschild/StardewMods }}
Lua error: bad argument #1 to 'gsub' (string expected, got nil).
mod name | author | compatibility | broke in | source |
---|
Broken mod
{{#invoke:SMAPI compatibility|entry |name = Lookup Anything |author = Pathoschild |id = Pathoschild.LookupAnything |nexus = 541 |github = Pathoschild/StardewMods |summary = |broke in = Stardew Valley 1.2 }}
Lua error: bad argument #1 to 'gsub' (string expected, got nil).
mod name | author | compatibility | broke in | source |
---|
Unofficial update
For an unofficial update, use the broken-mod template and add these under the other fields:
|unofficial url = https://community.playstarbound.com/attachments/201345000 |unofficial version = 1.18.2-unofficial.1-example
Lua error: bad argument #1 to 'gsub' (string expected, got nil).
mod name | author | compatibility | broke in | source |
---|
Usage
Limitations
The name, author, and id arguments are comma-separated. If the actual value contains a comma, use ,
instead.
Main fields (shown above)
field | purpose |
---|---|
name
|
The normalised display name for the mod. Delimit alternate names with commas. |
author
|
The name of the author, as shown on Nexus or in its manifest.json file. Delimit alternate names with commas. |
id
|
The latest unique mod ID, as listed in its manifest.json file. Delimit alternate/older IDs with commas (ideally in latest to oldest order). For very old mods with no ID, use none to disable validation checks. |
nexus
|
The mod's unique ID on Nexus (if any). This is the number in the mod page's URL. |
github
|
The mod's GitHub repository in the form owner/repo. |
summary
|
Specify custom notes or instructions about the mod's compatibility. Should usually be blank. |
broke in
|
The SMAPI or Stardew Valley update which broke this mod (if applicable). |
Other fields
field | purpose |
---|---|
status
|
Whether the mod is compatible with the latest versions of Stardew Valley and SMAPI (see #Valid statuses). If not specified, it defaults to unofficial if an unofficial URL is given, else broken if broke in is specified, else ok .
|
unofficial url
|
A page URL where the player can download an unofficial update, if any. |
unofficial version
|
The unofficial update's version number, if any. |
chucklefish
|
The mod's ID in the Chucklefish mod repository. |
curse
|
The mod's project ID and key in the CurseForge mod repository. The ID is shown on the mod page next to "Project ID", and the key is shown in the mod page's URL. This must be in the form id,key .
|
moddrop
|
The mod's ID in the ModDrop mod repository. |
url
|
The arbitrary mod URL, if not on a known mod site. Avoid if possible, since this makes crossreferencing more difficult. |
source
|
An arbitrary source code URL, if not on GitHub. Avoid if possible, since this makes crossreferencing more difficult. |
warnings
|
Text explaining additional compatibility warnings about the mod (e.g., not compatible with Linux/Mac). |
content pack for
|
The name of the mod which loads this content pack. |
dev note
|
Special notes intended for developers who maintain unofficial updates or submit pull requests. |
Valid statuses
status | description |
---|---|
ok
|
The mod is compatible. This is the default and doesn't need to be specified. Default summary: use latest version. |
optional
|
The mod is compatible, if you use an optional download on the mod page. Default summary: use optional download.[1] |
unofficial
|
The mod is compatible using an unofficial update. There's no need to specify this; if you also set unofficial url and unofficial version, you can remove the status field. |
workaround
|
The mod isn't compatible, but the player can fix it or there's a good alternative. A summary should be provided manually. If you also set unofficial url and unofficial version, you can remove the status field. |
broken
|
The mod isn't compatible. The message depends on whether the source link is set.Default summary: broken, not updated yet or broken, not open-source. |
abandoned
|
The mod is no longer maintained by the author, and an unofficial update or continuation is unlikely. This should only be used when the author has definitively abandoned the mod (either explicitly, or by removing the mod page or downloads). Default summary: no longer maintained. |
obsolete
|
The mod is no longer needed and should be removed. |
unknown
|
The mod's compatibility status hasn't been tested. This should only be used as a placeholder (e.g., when adding a new beta), it should never be used long since that defeats the purpose of the compatibility list. |
local p = {}
local private = {}
--##########
--## Public functions
--##########
-- Start a SMAPI compatibility table.
-- @test mw.log(p.header())
function p.header()
return
'<table class="wikitable plainlinks">'
.. "<tr><th>mod name</th><th>author</th><th>compatibility</th><th>broke in</th><th>source</th><th> </th></tr>";
end
-- End a SMAPI compatibility table.
-- @test mw.log(p.footer())
function p.footer()
return '</table>'
end
--- Render a mod row in the SMAPI compatibility table.
-- @param frame The arguments passed to the script.
-- @test mw.log(p.entry({ args = { name="Lookup Anything", name2="LookupAnything", author="Pathoschild", author2="Pathos", id="Pathoschild.LookupAnything", ["old ids"]="LookupAnything", ["nexus id"]="541", ["cf id"]="4250", ["github"]="Pathoschild/StardewMods", warnings="warning A, warning B", links="https://google.ca" }}))
function p.entry(frame)
-- read input args
local names = private.parseCommaDelimited((frame.args["name"] or '') .. ',' .. (frame.args["name2"] or ''))
local authors = private.parseCommaDelimited((frame.args["author"] or '') .. ',' .. (frame.args["author2"] or ''))
local ids = private.parseCommaDelimited((frame.args["id"] or '') .. ',' .. (frame.args["old ids"] or ''))
local nexusID = private.emptyToNil(frame.args["nexus id"])
local github = private.emptyToNil(frame.args["github"])
local summary = private.emptyToNil(frame.args["summary"])
local brokeIn = private.emptyToNil(frame.args["broke in"])
local status = private.emptyToNil(frame.args["status"])
local unofficialVersion = private.emptyToNil(frame.args["unofficial version"])
local unofficialUrl = private.emptyToNil(frame.args["unofficial url"])
local oldIDs = private.emptyToNil(frame.args["old ids"])
local chucklefishID = private.emptyToNil(frame.args["cf id"])
local customUrl = private.emptyToNil(frame.args["url"])
local customSource = private.emptyToNil(frame.args["source"])
local name2 = private.emptyToNil(frame.args["name2"])
local author2 = private.emptyToNil(frame.args["author2"])
local links = private.parseCommaDelimited(frame.args["links"])
local warnings = private.parseCommaDelimited(frame.args["warnings"])
local betaSummary = private.emptyToNil(frame.args["beta summary"])
local betaBrokeIn = private.emptyToNil(frame.args["beta broke in"])
local betaStatus = private.emptyToNil(frame.args["beta status"])
local betaUnofficialVersion = private.emptyToNil(frame.args["beta unofficial version"])
local betaUnofficialUrl = private.emptyToNil(frame.args["beta unofficial url"])
-- parse compatibility
local compat = private.getCompatInfo(status, summary, brokeIn, unofficialVersion, unofficialUrl)
local betaCompat = nil
if betaStatus or betaBrokeIn or betaUnofficialUrl or betaUnofficialVersion then
betaCompat = private.getCompatInfo(betaStatus, betaSummary, betaBrokeIn, betaUnofficialVersion, betaUnofficialUrl)
end
-- get main URL
local url = nil
if nexusID then
url = "https://www.nexusmods.com/stardewvalley/mods/" .. mw.uri.encode(nexusID, "PATH")
elseif chucklefishID then
url = "https://community.playstarbound.com/resources/" .. mw.uri.encode(chucklefishID, "PATH")
else
url = customUrl
end
-- get source url
local sourceUrl = customSource
if github then
sourceUrl = "https://github.com/" .. string.gsub(mw.uri.encode(github, "PATH"), "%%2F", "/")
end
-- 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
-- build HTML row
local row = mw.html.create("tr")
row:addClass("mod")
row:attr("id", names[1] and mw.uri.anchorEncode(names[1]));
row:attr("data-name", table.concat(names, ","))
row:attr("data-id", table.concat(ids, ","))
row:attr("data-author", table.concat(authors, ","))
row:attr("data-url", url)
row:attr("data-nexus-id", nexusID)
row:attr("data-cf-id", chucklefishID)
row:attr("data-github", github)
row:attr("data-custom-source", customSource)
row:attr("data-status", compat.status)
row:attr("data-summary", compat.summary)
row:attr("data-broke-in", compat.brokeIn)
row:attr("data-unofficial-version", compat.unofficialVersion)
row:attr("data-unofficial-url", compat.unofficialUrl)
row:attr("data-beta-status", betaCompat and betaCompat.status)
row:attr("data-beta-summary", betaCompat and betaCompat.summary)
row:attr("data-beta-broke-in", betaCompat and betaCompat.brokeIn)
row:attr("data-beta-unofficial-version", betaCompat and betaCompat.unofficialVersion)
row:attr("data-beta-unofficial-url", betaCompat and betaCompat.unofficialUrl)
row:attr("data-warnings", private.emptyToNil(table.concat(warnings, ",")))
row:attr("style", "line-height: 1em; background: " .. background .. ";")
row:newline()
-- add name field
do
local field = mw.html.create("td")
field:wikitext("[" .. (url or '') .. " " .. (names[1] or '') .. "]")
local nameCount = #names
if nameCount > 1 then
field:wikitext("<br /><small>(aka ")
for i = 1, nameCount do
if i > 1 then
field:wikitext(names[i])
if i < nameCount then
field:wikitext(", ")
end
end
end
field:wikitext(")</small>")
end
row:node(field)
row:newline()
end
-- add author field
do
local field = mw.html.create("td")
field:wikitext(authors[1])
local authorCount = #authors
if authorCount > 1 then
field:wikitext("<br /><small>(aka ")
for i = 1, authorCount do
if i > 1 then
field:wikitext(authors[i])
if i < authorCount then
field:wikitext(", ")
end
end
end
field:wikitext(")</small>")
end
row:node(field)
row:newline()
end
-- add summary field
do
local field = mw.html.create("td")
-- stable status
field:wikitext(compat.summaryIcon .. " " .. compat.summary)
if compat.status == "optional" then
field:wikitext("<ref name=\"optional-update\" />")
end
-- beta status
if betaCompat ~= nill then
field:wikitext("<br />")
field:wikitext("'''SDV beta only:''' " .. betaCompat.summaryIcon .. " " .. betaCompat.summary)
if betaCompat.status == "optional" then
field:wikitext("<ref name=\"optional-update\" />")
end
end
-- warnings
do
local warningCount = #warnings
if warningCount > 0 then
for i = 1, warningCount do
field:wikitext("<br />⚠ " .. warnings[i])
end
end
end
row:node(field)
row:newline()
end
-- add 'broke in' field
do
local field = mw.html.create("td")
if betaCompat ~= nil and betaCompat.brokeIn ~= nil then
field:wikitext(betaCompat.brokeIn)
elseif compat.brokeIn ~= nil then
field:wikitext(compat.brokeIn)
end
row:node(field)
row:newline()
end
-- add 'source' field
do
if sourceUrl then
row:wikitext("<td>[" .. sourceUrl .. " source]</td>")
else
row:wikitext("<td><span style=\"color: red; font-size: 0.85em; opacity: 0.5;\">closed source</span></td>")
end
row:newline()
end
-- add metadata field
do
local field = mw.html.create("td")
field:attr("style", "font-size: 0.8em")
-- anchor
field:wikitext("[[#" .. (names[1] or '') .. "|#]] ")
-- reference links
do
local linkCount = #links
for i = 1, linkCount do
field:wikitext("[" .. links[i] .. " " .. i .. "] ")
end
end
-- 'no id' warning
if #ids == 0 then
field:wikitext("⚠ no id")
end
-- backwards-compatible metadata (temporary)
field:wikitext('<div div class="mod-metadata" style="display: none;">')
field:wikitext('<div class="mod-anchor">' .. (names[1] and mw.uri.anchorEncode(names[1])) .. "</div>")
field:wikitext('<div class="mod-id">' .. mw.text.encode(table.concat(ids, ",")) .. "</div>")
field:wikitext('<div class="mod-url">' .. mw.text.encode(url) .. "</div>")
if nexusID ~= nil then
field:wikitext('<div class="mod-nexus-id">' .. mw.text.encode(nexusID) .. "</div>")
end
if chucklefishID ~= nil then
field:wikitext('<div class="mod-cf-id">' .. mw.text.encode(chucklefishID) .. "</div>")
end
if github ~= nil then
field:wikitext('<div class="mod-github">' .. mw.text.encode(github) .. '</div>')
end
if customSource ~= nil then
field:wikitext('<div class="mod-custom-source">' .. mw.text.encode(customSource) .. '</div>')
end
field:wikitext('<div class="mod-status">' .. mw.text.encode(compat.status) .. '</div>')
if compat.brokeIn ~= nil then
field:wikitext('<div class="mod-broke-in">' .. mw.text.encode(compat.brokeIn) .. '</div>')
end
if compat.unofficialVersion ~= nil and compat.unofficialUrl ~= nil then
field:wikitext('<div class="mod-unofficial-version">' .. mw.text.encode(compat.unofficialVersion) .. '</div>')
field:wikitext('<div class="mod-unofficial-url">' .. mw.text.encode(compat.unofficialUrl) .. '</div>')
end
if betaCompat ~= nil then
field:wikitext('<div class="mod-beta-status">' .. mw.text.encode(betaCompat.status) .. '</div>')
if betaCompat.brokeIn ~= nil then
field:wikitext('<div class="mod-beta-broke-in">' .. mw.text.encode(betaCompat.brokeIn) .. '</div>')
end
if betaCompat.unofficialVersion ~= nil and betaCompat.unofficialUrl ~= nil then
field:wikitext('<div class="mod-beta-unofficial-version">' .. mw.text.encode(betaCompat.unofficialVersion) .. '</div>')
field:wikitext('<div class="mod-beta-unofficial-url">' .. mw.text.encode(betaCompat.unofficialUrl) .. '</div>')
end
end
if #warnings > 0 then
field:wikitext('<div class="mod-warnings">' .. mw.text.encode(table.concat(warnings, ",")) .. '</div>')
end
field.wikitext("</div>")
row:node(field)
end
return tostring(row)
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.
-- @param frame The arguments passed to the script.
-- @test mw.log(p.printAttribute({ args = { "someKey", "some \"'<> value" }}))
function p.printAttribute(frame)
local key = frame.args[1]
local value = frame.args[2]
if value == nil or value == "" then
return ""
else
return key .. "=\"" .. mw.text.encode(value) .. "\""
end
end
--##########
--## Private functions
--##########
-- 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 summary A human-readable summary of the compatibility info. If nil or blank, it'll be derived from the other fields.
-- @param brokeIn The SMAPI or Stardew Valley version which broke the mod, if applicable.
-- @param unofficialVersion The unofficial version which fixes compatibility, if applicable.
-- @param unofficialUrl The URL for the unofficial version which fixes compatibility, if applicable.
-- @param hasSource Whether the mod has public source code available.
function private.getCompatInfo(status, summary, brokeIn, unofficialVersion, unofficialUrl, hasSource)
-- derive status
if status == nil then
if unofficialVersion ~= nil then
status = "unofficial"
elseif brokeIn ~= nil then
status = "broken"
else
status = "ok"
end
end
-- derive summary icon
local summaryIcon = "✓"
if status == "unofficial" or status == "workaround" then
summaryIcon = "⚠"
elseif status == "broken" and hasSource then
summaryIcon = "↻"
elseif status == "broken" or status == "obsolete" or status == "abandoned" then
summaryIcon = "✖"
end
-- derive summary
if summary == nil then
if status == "ok" then
summary = "use latest version."
elseif status == "optional" then
summary = "use optional download."
elseif status == "unofficial" then
summary = "broken, use [" .. (unofficialUrl or "") .. " " .. "unofficial version]"
if unofficialVersion ~= nil then
summary = summary .. " (<small>" .. unofficialVersion .. "</small>)"
end
summary = summary .. "."
elseif status == "workaround" then
summary = "broken. '''error:''' should specify summary."
elseif status == "broken" then
if hasSource then
summary = "broken, not updated yet."
else
summary = "broken, not open-source."
end
elseif status == "obsolete" then
summary = "obsolete."
elseif status == "abandoned" then
summary = "no longer maintained."
else
summary = "'''error:''' unknown status '" .. status .. "'."
end
end
return {
status = status,
summaryIcon = summaryIcon,
summary = summary,
brokeIn = brokeIn,
unofficialVersion = unofficialVersion,
unofficialUrl = unofficialUrl
}
end
-- Get a nil value if the specified value is an empty string, else return the value unchanged.
-- @param value The string to check.
function private.emptyToNil(value)
if value ~= "" then
return value
else
return nil
end
end
-- Parse a comma-delimited string into an array.
-- @param value The string to parse.
function private.parseCommaDelimited(value)
local result = {}
if value ~= nil then
local values = mw.text.split(value, ",", true)
for i = 1, #values do
v = mw.text.trim(values[i])
if v ~= "" then
table.insert(result, v)
end
end
end
return result
end
return p