Module:LD
Jump to navigation
Jump to search
Documentation for this module may be created at Module:LD/doc
-- Module:LD — JSON-LD helpers (safe: no autolinking inside <script>)
-- Use via templates like {{LD-Album}} or {{LD-Article}}
local p = {}
local function argsOf(frame)
local parent = frame:getParent()
return parent and parent.args or frame.args
end
local function nz(s) return (s and s ~= "") and s or nil end
local function num(s) s = tonumber(s); return s end
local function listFrom(args, prefix, maxn)
local t = {}
for i = 1, maxn do
local v = nz(args[prefix .. i])
if v then t[#t+1] = v end
end
return (#t > 0) and t or nil
end
local function scriptTagFrom(obj)
local json = mw.text.jsonEncode(obj)
local safe = mw.text.nowiki(json) -- prevent auto-linking/formatting
return mw.getCurrentFrame():callParserFunction{
name = "#tag",
args = { "script", safe, type = "application/ld+json" }
}
end
-- ===== MusicAlbum =====
function p.album(frame)
local a = argsOf(frame)
local title = mw.title.getCurrentTitle()
local obj = {
["@context"] = "https://schema.org",
["@type"] = "MusicAlbum",
name = a.name or title.text,
datePublished = nz(a.datePublished),
genre = nz(a.genre) or listFrom(a, "genre", 10),
numTracks = tonumber(a.numTracks),
image = nz(a.image),
inLanguage = nz(a.inLanguage),
url = nz(a.url) or title:fullUrl{ protocol = "https" }
}
-- byArtist
local artistName = nz(a.artistName)
local artistType = nz(a.artistType) or "MusicGroup"
if artistName then
obj.byArtist = { ["@type"] = artistType, name = artistName }
end
-- recordLabel
local label = nz(a.recordLabel)
if label then
obj.recordLabel = { ["@type"] = "Organization", name = label }
end
-- sameAs (up to 10)
local sameAs = listFrom(a, "sameAs", 10)
if sameAs then obj.sameAs = sameAs end
-- identifier
if nz(a.identifier) then obj.identifier = a.identifier end
return scriptTagFrom(obj)
end
-- ===== Article / WebPage =====
function p.article(frame)
local a = argsOf(frame)
local title = mw.title.getCurrentTitle()
local url = nz(a.url) or title:fullUrl{ protocol = "https" }
local function logoObj(urlStr, w, h)
if not nz(urlStr) then return nil end
local o = { ["@type"] = "ImageObject", url = urlStr }
local W, H = num(w), num(h)
o.width, o.height = W, H
return o
end
-- Build images array without ever emitting []
local images = {}
if nz(a.image) then table.insert(images, a.image) end
for i=1,5 do
local v = nz(a["image"..i])
if v then table.insert(images, v) end
end
if #images == 0 and nz(a.coverImage) then
images = { a.coverImage }
end
-- If you want a sitewide fallback, uncomment next line:
-- if #images == 0 then images = { "https://roovet.com/static/og/roovet-default.jpg" } end
local obj = {
["@context"] = "https://schema.org",
["@type"] = nz(a.type) or "Article",
name = nz(a.name) or (title.text .. " - Roovet Articles"),
headline = nz(a.headline) or (title.text .. " - Roovet Articles"),
mainEntityOfPage = { ["@type"] = "WebPage", ["@id"] = url },
identifier = url,
url = url,
datePublished = nz(a.datePublished),
dateModified = nz(a.dateModified),
author = {
["@type"] = nz(a.authorType) or "Organization",
name = nz(a.authorName) or "Roovet Articles",
url = nz(a.authorUrl) or "https://roovet.com"
},
publisher = {
["@type"] = "Organization",
name = nz(a.publisherName) or "Roovet Articles",
url = nz(a.publisherUrl) or "https://roovet.com",
logo = logoObj(nz(a.publisherLogo), a.publisherLogoWidth, a.publisherLogoHeight)
},
potentialAction = nz(a.searchTarget) and {
["@type"] = "SearchAction",
target = a.searchTarget,
["query-input"]= "required name=search_term"
} or nil
}
if #images > 0 then obj.image = images end
return scriptTagFrom(obj)
end
return p