모듈:Metadata: 두 판 사이의 차이
imported>Hsl0 편집 요약 없음 |
편집 요약 없음 |
||
(사용자 3명의 중간 판 46개는 보이지 않습니다) | |||
1번째 줄: | 1번째 줄: | ||
local p = {} | local p = {} -- 패키지 | ||
local get = {} | |||
-- 상수 시작 | |||
function userLink(user, display) | local DEFAULT_IMG = { | ||
progress = "[[파일:Progress unknown.svg|완성도 정도를 입력하세요|링크=|16px]]", | |||
editpolicy = "[[파일:Crystal Clear action info.svg|17px|개방성 정도를 입력하세요|링크=]]", | |||
rating = "[[파일:GRAC Square Template.svg|16px|게임 제작자가 이용가 등급 판정을 하지 않았습니다|링크=]]" | |||
} | |||
local PROGRESS_IMG = { | |||
[0] = "[[파일:Progress base.svg|변경하지 말아야 하는 공사중인 게임|링크=|16px]]", | |||
[1] = "[[파일:Progress low.svg|조금 완성된 게임|링크=|16px]]", | |||
[2] = "[[파일:Progress medium.svg|중간 정도 완성된 게임|링크=|16px]]", | |||
[3] = "[[파일:Progress high.svg|거의 완성된 게임|링크=|16px]]", | |||
[4] = "[[파일:Progress full.svg|완성되었지만 추가할 수 있는 게임|링크=|16px]]", | |||
[5] = "[[파일:백괴게임 완성도 7단계.svg|완성되어 변경하지 말아야 하는 게임|링크=|16px]]" | |||
} | |||
local ABANDON = {} --Symbol | |||
local EDITPOLICY_IMG = { | |||
open = "[[파일:Crystal Clear app clean.png|17px|편집 가능|링크=]]", | |||
limited = "[[파일:Crystal Clear bot on trial2.png|17px|규칙에 따라 편집 가능|링크=]]", | |||
closed = "[[파일:Crystal Clear action editdelete.png|17px|편집 금지|링크=]]", | |||
[ABANDON] = "[[파일:Crystal Clear app logout.png|17px|버려진 게임|링크=]]" | |||
} | |||
local PLATFORM_IMG = { | |||
web = "[[파일:Feather-location-globe.svg|16px|웹|링크=|class=gameicon-platform-web]]", | |||
windows = "[[파일:Windows logo - 2021 (Black).svg|16px|윈도우|링크=|class=gameicon-platform-windows]]", | |||
linux = "[[파일:Tux Mono.svg|16px|리눅스|링크=|class=gameicon-platform-linux]]", | |||
macos = "[[파일:MacOS logo.svg|16px|맥 OS|링크=|class=gameicon-platform-macos]]", | |||
android = "[[파일:Cib-android (CoreUI Icons v1.0.0).svg|16px|안드로이드|링크=|class=gameicon-platform-android]]", | |||
other = "[[파일:OOjs UI icon help-ltr.svg|16px|기타|링크=|class=gameicon-platform-other]]" | |||
} | |||
local RATING_IMG = { | |||
[18] = "[[파일:GRAC 18 Square.svg|16px|청소년 이용불가|링크=]]", | |||
[15] = "[[파일:GRAC 15 Square.svg|16px|15세 이용가|링크=]]", | |||
[12] = "[[파일:GRAC 12 Square.svg|16px|12세 이용가|링크=]]", | |||
all = "[[파일:GRAC All Square.svg|16px|전체 이용가|링크=]]", | |||
test = "[[파일:GRAC Test Square.svg|16px|평가하고 있는 중입니다|링크=]]" | |||
} | |||
-- 상수 끝 코드 시작 | |||
function userLink(user, display) | |||
local title = mw.title.new(user, 'User') | |||
return '[[' .. tostring(title) .. '|' .. (display or title.subpageText) .. ']]' | |||
end | |||
function platformIcons(platforms) | |||
local icons = mw.html.create('span'):addClass('gameicon-platform') | |||
local other = false | |||
if type(platforms) == 'string' then | |||
platforms = {platforms} | |||
end | end | ||
if not platforms or #platforms <= 0 then | |||
platforms = {'web'} | |||
end | |||
for index, platform in ipairs(platforms) do | |||
if platform == 'other' then | |||
other = true | |||
else | |||
local img = PLATFORM_IMG[platform] | |||
if img then | |||
icons:wikitext(img) | |||
else | |||
other = true | |||
end | |||
end | |||
end | |||
if other then | |||
icons:wikitext(PLATFORM_IMG.other) | |||
end | |||
return icons:done() | |||
end | |||
function p.icon(frame) | |||
local title = frame.args[1] | local title = frame.args[1] | ||
local doc = | local doc = p.resolve(title) | ||
if not doc.exists then | if not doc.exists then | ||
61번째 줄: | 97번째 줄: | ||
-- status | -- status | ||
local | local editpolicy = data.editpolicy | ||
if data.abandon then | if data.abandon then | ||
editpolicy = ABANDON | |||
end | end | ||
local status = icon:tag('span'):addClass('gameicon-status') | local status = icon:tag('span'):addClass('gameicon-status') | ||
:wikitext(data.progress ~= nil and | :wikitext(data.progress ~= nil and PROGRESS_IMG[data.progress] or DEFAULT_IMG.progress) | ||
:wikitext( | :wikitext(editpolicy ~= nil and EDITPOLICY_IMG[editpolicy] or DEFAULT_IMG.editpolicy) | ||
:node(platformIcons(data.platform)) | |||
:wikitext(data.rating and | :wikitext(data.rating and RATING_IMG[get.rating(data.rating)] or DEFAULT_IMG.rating) | ||
:attr{ | :attr{ | ||
['data-progress'] = data.progress, | ['data-progress'] = data.progress, | ||
['data- | ['data-editpolicy'] = editpolicy == ABANDON and "true" or tostring(editpolicy), | ||
['data- | ['data-rating'] = get.rating(data.rating) | ||
} | } | ||
if data.abandon then | if data.abandon then | ||
86번째 줄: | 121번째 줄: | ||
-- maker | -- maker | ||
local | local contributor | ||
if data. | if data.contributor then | ||
contributor = "" | |||
if type(data. | if type(data.contributor) == 'string' then | ||
data. | data.contributor = {data.contributor} | ||
end | end | ||
for index, name in pairs(data. | for index, name in pairs(data.contributor) do | ||
contributor = contributor .. userLink(name) .. ", " | |||
end | end | ||
contributor = contributor:sub(1, -3) .. " 사용자께서 도와주셨습니다." | |||
end | end | ||
local author | local author | ||
if type(data.author) == 'table' then | if data.author then | ||
if type(data.author) == 'table' then | |||
author = "" | |||
for index, name in pairs(data.author) do | |||
author = author .. userLink(name) .. ", " | |||
end | |||
author = author:sub(1, -2) | |||
else | |||
author = userLink(data.author) | |||
end | |||
icon:tag('span'):addClass('gameicon-maker') | |||
:wikitext(contributor and frame:expandTemplate{ | |||
title = '툴팁', | |||
args = {author, contributor, sym = 1, class='messagebox gameicon-contributor'} | |||
} or author) | |||
end | |||
return icon:allDone() | return icon:allDone() | ||
end | |||
function rel2abs(rel, base) | |||
return mw.getCurrentFrame():callParserFunction('#rel2abs', rel, base) | |||
end | |||
function getData(source) | |||
local title = p.resolve(rel2abs(source)) | |||
local result, data = pcall(mw.text.jsonDecode, title and title:getContent() or source) | |||
if result then | |||
return data | |||
else | |||
error('유효하지 않은 게임 메타데이터', 2) | |||
end | |||
end | |||
function p.role(frame) | |||
local data = getData(frame.args[1]) | |||
local user = mw.title.new(frame.args[2], 'User') | |||
local authors = type(data.author) == 'string' and {data.author} or data.author | |||
local contributors = type(data.contributor) == 'string' and {data.contributor} or data.contributor | |||
if authors then | |||
for index, author in ipairs(authors) do | |||
if mw.title.equals(user, mw.title.new(author, 'User')) then | |||
return 'author' | |||
end | |||
end | |||
else | |||
error('유효하지 않은 메타데이터: 제작자(author)가 없음', 2) | |||
end | |||
if contributors then | |||
for index, contributor in ipairs(contributors) do | |||
if mw.title.equals(user, mw.title.new(contributor, 'User')) then | |||
return 'contributor' | |||
end | |||
end | |||
end | |||
end | |||
function p.resolve(location) | |||
local title = mw.title.new(location).subjectPageTitle | |||
-- 넘겨주기 대상 탐색 | |||
while title.redirectTarget do | |||
title = title.redirectTarget | |||
location = title.fullText | |||
end | |||
local parts = mw.text.split(title.fullText, '/') | |||
local size = #parts | |||
-- 상위 문서 탐색 | |||
for index = 0, size - 1 do | |||
local pagename = table.concat(parts, '/', 1, size - index) | |||
-- 현재 상위 문서 이름이 game.json이거나 하위 문서로 game.json을 갖는지 확인 | |||
if string.find(pagename, '/game%.json$') == nil then | |||
local title = mw.title.new(pagename .. '/game.json') | |||
if title.exists then | |||
return title | |||
end | |||
end | |||
end | |||
end | |||
function p.root(location) | |||
local meta = p.resolve(location) | |||
return meta and meta.basePageTitle or mw.title.new(location).rootPageTitle | |||
end | |||
function p.subpage(location, path) | |||
local namespace = select(3, path:find('^([^:/]+):')) | |||
local root = p.root(location) | |||
if mw.site.namespaces[namespace].id == 1 then -- namespace = 토론 | |||
root = root.talkPageTitle | |||
path = mw.ustring.sub(path, mw.ustring.len(namespace) + 2) | |||
end | |||
return root:subPageTitle(select(3, tostring(path):find('^/?(.+)$'))) | |||
end | |||
function getRelPage(key) | |||
return function(data, title) | |||
return p.subpage(title, data[key]) | |||
end | |||
end | |||
function p.get(frame) | |||
local data = getData(frame.args[1]) | |||
local prop = frame.args[2] | |||
if not prop then | |||
error('속성을 지정하지 않았습니다', 2) | |||
end | |||
if get[prop] then | |||
return get[prop](data[prop], p.root(frame.args[1])) | |||
else | |||
return data[prop] | |||
end | |||
end | |||
p['if'] = function (frame) | |||
local data = getData(frame.args[1]) | |||
local prop = frame.args[2] | |||
local t, e = frame.args[3], frame.args[4] | |||
if not prop then | |||
error('속성을 지정하지 않았습니다', 2) | |||
end | |||
if get[prop] then | |||
return get[prop](data[prop]) and t or e | |||
else | |||
return data[prop] and t or e | |||
end | |||
end | |||
function get.rating(data) | |||
return data and (data.grac and data.grac.age or data.libertygame and data.libertygame.age) or nil | |||
end | end | ||
return p | return p |
2024년 11월 11일 (월) 17:20 기준 최신판
게임 메타데이터를 다루는 모듈입니다.
icon
게임 메타데이터를 바탕으로 게임아이콘을 만듭니다.
{{#invoke:metadata|icon|(게임 문서)}}
get
게임 메타데이터의 특정 속성을 불러옵니다. 이때, 원본 값을 그대로 불러오는 것이 아닌 활용하기 편한 형태로 불러오는 기능입니다. 일반적으로는 원본 값이지만, 특별히 수정된 경우 가공된 값이 불러와집니다.
{{#invoke:metadata|get|(게임 문서)|(속성)}}
rating
게임 등급을 불러옵니다. 리버티게임 등급 분류와 게임물관리위원회 등급 분류를 구별하지 않고 바로 등급만 가져옵니다.
{{#invoke:metadata|get|(게임 문서)|rating}}
if
게임 메타데이터의 특정 속성의 존재 여부를 확인하고, 있을 때와 없을 때 나올 내용을 구분합니다.
{{#invoke:metadata|if|(게임 문서)|(속성)|(있을 때)|(없을 때)}}
role
특정 사용자가 게임의 제작자인지, 기여자(조력자)인지, 이에 해당되지 않는 지 확인합니다. 제작자일 경우 author가, 기여자일 경우 contributor가 반환됩니다.
{{#invoke:metadata|role|(게임 문서)|(사용자 이름)}}
resolve
게임 메타데이터가 있는 문서를 찾습니다. 대상 문서의 하위 문서에 game.json이 없다면 그 상위 문서에서 찾고, 없으면 반복하며 있으면 찾은 game.json 문서에서 멈춥니다. 대상 문서가 넘겨주기 문서인 경우 넘어가는 문서를 기준으로 찾습니다. mw.title 객체를 반환하며 문자열로 변환해 문서 제목을 얻거나, :getContent() 함수를 통해 game.json 내용을 확인할 수 있습니다. 찾은 game.json 문서가 없다면 nil이 반환됩니다.
p.resolve(title)
root
게임의 최상위 문서를 찾습니다. 정확히는 게임 메타데이터(game.json)을 하위 문서로 갖는 문서를 찾습니다. mw.title 객체를 반환하며 문자열로 변환해 문서 제목을 얻거나, :getContent() 함수를 통해 game.json 내용을 확인할 수 있습니다. 찾은 game.json 문서가 없다면 루트 페이지가 반환됩니다.
p.root(title)
subpage
게임의 최상위 문서 바로 아래에 있는 하위 문서를 찾습니다. 첫번째 인수는 현재 위치 및 최상위 문서입니다. 현재 위치를 넣으면 root 함수를 통해 알아서 최상위 문서를 찾아줍니다. 두번째 인수는 하위 문서의 상대적 경로입니다. 예를 들어, 어떤 게임/하위
라는 문서를 가리키고 싶을 경우 두번째 인수에 /하위
만 입력하면 절대 경로인 어떤 게임/하위
를 반환합니다. mw.title 객체를 반환하며 문자열로 변환해 문서 제목을 얻거나, :getContent() 함수를 통해 game.json 내용을 확인할 수 있습니다. 찾은 game.json 문서가 없다면 nil이 반환됩니다.
p.subpage(title, path)
위 설명은 모듈:Metadata/설명문서의 내용을 가져와 보여주고 있습니다. (편집 | 역사) 이 모듈에 대한 수정 연습과 시험은 연습장 (만들기 | 미러)과 시험장 (만들기)에서 할 수 있습니다. 분류는 /설명문서에 넣어주세요. 이 모듈에 딸린 문서. |
local p = {} -- 패키지
local get = {}
-- 상수 시작
local DEFAULT_IMG = {
progress = "[[파일:Progress unknown.svg|완성도 정도를 입력하세요|링크=|16px]]",
editpolicy = "[[파일:Crystal Clear action info.svg|17px|개방성 정도를 입력하세요|링크=]]",
rating = "[[파일:GRAC Square Template.svg|16px|게임 제작자가 이용가 등급 판정을 하지 않았습니다|링크=]]"
}
local PROGRESS_IMG = {
[0] = "[[파일:Progress base.svg|변경하지 말아야 하는 공사중인 게임|링크=|16px]]",
[1] = "[[파일:Progress low.svg|조금 완성된 게임|링크=|16px]]",
[2] = "[[파일:Progress medium.svg|중간 정도 완성된 게임|링크=|16px]]",
[3] = "[[파일:Progress high.svg|거의 완성된 게임|링크=|16px]]",
[4] = "[[파일:Progress full.svg|완성되었지만 추가할 수 있는 게임|링크=|16px]]",
[5] = "[[파일:백괴게임 완성도 7단계.svg|완성되어 변경하지 말아야 하는 게임|링크=|16px]]"
}
local ABANDON = {} --Symbol
local EDITPOLICY_IMG = {
open = "[[파일:Crystal Clear app clean.png|17px|편집 가능|링크=]]",
limited = "[[파일:Crystal Clear bot on trial2.png|17px|규칙에 따라 편집 가능|링크=]]",
closed = "[[파일:Crystal Clear action editdelete.png|17px|편집 금지|링크=]]",
[ABANDON] = "[[파일:Crystal Clear app logout.png|17px|버려진 게임|링크=]]"
}
local PLATFORM_IMG = {
web = "[[파일:Feather-location-globe.svg|16px|웹|링크=|class=gameicon-platform-web]]",
windows = "[[파일:Windows logo - 2021 (Black).svg|16px|윈도우|링크=|class=gameicon-platform-windows]]",
linux = "[[파일:Tux Mono.svg|16px|리눅스|링크=|class=gameicon-platform-linux]]",
macos = "[[파일:MacOS logo.svg|16px|맥 OS|링크=|class=gameicon-platform-macos]]",
android = "[[파일:Cib-android (CoreUI Icons v1.0.0).svg|16px|안드로이드|링크=|class=gameicon-platform-android]]",
other = "[[파일:OOjs UI icon help-ltr.svg|16px|기타|링크=|class=gameicon-platform-other]]"
}
local RATING_IMG = {
[18] = "[[파일:GRAC 18 Square.svg|16px|청소년 이용불가|링크=]]",
[15] = "[[파일:GRAC 15 Square.svg|16px|15세 이용가|링크=]]",
[12] = "[[파일:GRAC 12 Square.svg|16px|12세 이용가|링크=]]",
all = "[[파일:GRAC All Square.svg|16px|전체 이용가|링크=]]",
test = "[[파일:GRAC Test Square.svg|16px|평가하고 있는 중입니다|링크=]]"
}
-- 상수 끝 코드 시작
function userLink(user, display)
local title = mw.title.new(user, 'User')
return '[[' .. tostring(title) .. '|' .. (display or title.subpageText) .. ']]'
end
function platformIcons(platforms)
local icons = mw.html.create('span'):addClass('gameicon-platform')
local other = false
if type(platforms) == 'string' then
platforms = {platforms}
end
if not platforms or #platforms <= 0 then
platforms = {'web'}
end
for index, platform in ipairs(platforms) do
if platform == 'other' then
other = true
else
local img = PLATFORM_IMG[platform]
if img then
icons:wikitext(img)
else
other = true
end
end
end
if other then
icons:wikitext(PLATFORM_IMG.other)
end
return icons:done()
end
function p.icon(frame)
local title = frame.args[1]
local doc = p.resolve(title)
if not doc.exists then
return mw.html.create('span'):addClass('error')
:wikitext('[[' .. title .. '/game.json]] 문서가 존재하지 않습니다. 메타데이터 문서를 만들어 주세요.')
:done()
end
local data = mw.text.jsonDecode(doc:getContent())
local icon = mw.html.create('span'):addClass('gameicon')
-- status
local editpolicy = data.editpolicy
if data.abandon then
editpolicy = ABANDON
end
local status = icon:tag('span'):addClass('gameicon-status')
:wikitext(data.progress ~= nil and PROGRESS_IMG[data.progress] or DEFAULT_IMG.progress)
:wikitext(editpolicy ~= nil and EDITPOLICY_IMG[editpolicy] or DEFAULT_IMG.editpolicy)
:node(platformIcons(data.platform))
:wikitext(data.rating and RATING_IMG[get.rating(data.rating)] or DEFAULT_IMG.rating)
:attr{
['data-progress'] = data.progress,
['data-editpolicy'] = editpolicy == ABANDON and "true" or tostring(editpolicy),
['data-rating'] = get.rating(data.rating)
}
if data.abandon then
status:attr('data-abandon', '')
end
-- name
icon:tag('span'):addClass('gameicon-name')
:wikitext('[[' .. title .. '|' .. (data.name or title) .. ']]')
-- maker
local contributor
if data.contributor then
contributor = ""
if type(data.contributor) == 'string' then
data.contributor = {data.contributor}
end
for index, name in pairs(data.contributor) do
contributor = contributor .. userLink(name) .. ", "
end
contributor = contributor:sub(1, -3) .. " 사용자께서 도와주셨습니다."
end
local author
if data.author then
if type(data.author) == 'table' then
author = ""
for index, name in pairs(data.author) do
author = author .. userLink(name) .. ", "
end
author = author:sub(1, -2)
else
author = userLink(data.author)
end
icon:tag('span'):addClass('gameicon-maker')
:wikitext(contributor and frame:expandTemplate{
title = '툴팁',
args = {author, contributor, sym = 1, class='messagebox gameicon-contributor'}
} or author)
end
return icon:allDone()
end
function rel2abs(rel, base)
return mw.getCurrentFrame():callParserFunction('#rel2abs', rel, base)
end
function getData(source)
local title = p.resolve(rel2abs(source))
local result, data = pcall(mw.text.jsonDecode, title and title:getContent() or source)
if result then
return data
else
error('유효하지 않은 게임 메타데이터', 2)
end
end
function p.role(frame)
local data = getData(frame.args[1])
local user = mw.title.new(frame.args[2], 'User')
local authors = type(data.author) == 'string' and {data.author} or data.author
local contributors = type(data.contributor) == 'string' and {data.contributor} or data.contributor
if authors then
for index, author in ipairs(authors) do
if mw.title.equals(user, mw.title.new(author, 'User')) then
return 'author'
end
end
else
error('유효하지 않은 메타데이터: 제작자(author)가 없음', 2)
end
if contributors then
for index, contributor in ipairs(contributors) do
if mw.title.equals(user, mw.title.new(contributor, 'User')) then
return 'contributor'
end
end
end
end
function p.resolve(location)
local title = mw.title.new(location).subjectPageTitle
-- 넘겨주기 대상 탐색
while title.redirectTarget do
title = title.redirectTarget
location = title.fullText
end
local parts = mw.text.split(title.fullText, '/')
local size = #parts
-- 상위 문서 탐색
for index = 0, size - 1 do
local pagename = table.concat(parts, '/', 1, size - index)
-- 현재 상위 문서 이름이 game.json이거나 하위 문서로 game.json을 갖는지 확인
if string.find(pagename, '/game%.json$') == nil then
local title = mw.title.new(pagename .. '/game.json')
if title.exists then
return title
end
end
end
end
function p.root(location)
local meta = p.resolve(location)
return meta and meta.basePageTitle or mw.title.new(location).rootPageTitle
end
function p.subpage(location, path)
local namespace = select(3, path:find('^([^:/]+):'))
local root = p.root(location)
if mw.site.namespaces[namespace].id == 1 then -- namespace = 토론
root = root.talkPageTitle
path = mw.ustring.sub(path, mw.ustring.len(namespace) + 2)
end
return root:subPageTitle(select(3, tostring(path):find('^/?(.+)$')))
end
function getRelPage(key)
return function(data, title)
return p.subpage(title, data[key])
end
end
function p.get(frame)
local data = getData(frame.args[1])
local prop = frame.args[2]
if not prop then
error('속성을 지정하지 않았습니다', 2)
end
if get[prop] then
return get[prop](data[prop], p.root(frame.args[1]))
else
return data[prop]
end
end
p['if'] = function (frame)
local data = getData(frame.args[1])
local prop = frame.args[2]
local t, e = frame.args[3], frame.args[4]
if not prop then
error('속성을 지정하지 않았습니다', 2)
end
if get[prop] then
return get[prop](data[prop]) and t or e
else
return data[prop] and t or e
end
end
function get.rating(data)
return data and (data.grac and data.grac.age or data.libertygame and data.libertygame.age) or nil
end
return p