Module:Wdinfobox
Appearance
Documentation for this module may be created at Module:Wdinfobox/doc
local p = {}
local PROP_INSTANCE_OF = 'P31'
local PROP_IMAGE = 'P18'
local PROP_MEDIA_LEGEND = 'P2096'
local PROP_DATE_OF_BIRTH = 'P569'
local PROP_PLACE_OF_BIRTH = 'P19'
local PROP_DATE_OF_DEATH = 'P570'
local PROP_PLACE_OF_DEATH = 'P20'
local PROP_FATHER = 'P22'
local PROP_MOTHER = 'P25'
local PROP_FORMATTER_URL = 'P1630'
local PROP_WIKISPORE_ID = 'P7721'
local PROP_ADMIN_LOCATION = 'P131'
local PROP_LOCATION = 'P276'
local PROP_COORDINATES = 'P625'
-- Configure Cargo tables and their columns.
local cargoConfig = {
people = {
wikidata = 'String',
image = 'File',
date_of_birth = 'Date',
date_of_death = 'Date',
parents = 'List (;) of String'
},
places = {
wikidata = 'String',
image = 'File',
coords = 'Coordinates',
admin_location = 'Page',
location = 'Page'
}
}
p.getTableRow = function( header, value, class )
if ( header == nil or header == '' ) and ( value == nil or value == '' ) then
return ''
end
-- Header.
local th = mw.html.create( 'th' )
th:wikitext( header )
-- Data.
local td = mw.html.create( 'td' )
td:wikitext( mw.getCurrentFrame():preprocess( value ) )
-- Row.
local row = mw.html.create( 'tr' )
row:attr( 'class', class )
if header ~= nil and header ~= '' then
row:node( th )
else
td:attr( 'colspan', '2' )
end
if value == nil or value == '' then
th:attr( 'colspan', '2' )
else
row:node( td )
end
return row
end
p.getValue = function ( claim )
for k,v in pairs( claim ) do
-- Is of type item.
if v.mainsnak.datatype == 'wikibase-item' then
return '{{wdl|' .. v.mainsnak.datavalue.value.id .. '}}'
elseif v.mainsnak.datatype == 'monolingualtext' then
return v.mainsnak.datavalue.value.text
elseif v.mainsnak.datatype == 'commonsMedia' then
return '[[File:' .. v.mainsnak.datavalue.value .. '|300px]]'
else
return '<small><em>Type ' .. v.mainsnak.datatype .. ' not handled by Module:Infobox yet.</small>'
end
end
end
p.getErrorHtml = function ( msg )
return '<span class="error">' .. msg .. '</span>'
end
p.dateAndPlace = function ( item, dataCargo, dateProp, placeProp )
-- Dates.
local datesData = item.claims[dateProp] or {}
local dates = {}
local lang = mw.language.getContentLanguage()
for _,claim in pairs( datesData ) do
dates[#dates + 1] = lang:formatDate('j F Y', claim.mainsnak.datavalue.value.time )
if dateProp == PROP_DATE_OF_BIRTH then
dataCargo.date_of_birth = claim.mainsnak.datavalue.value.time
elseif dateProp == PROP_DATE_OF_DEATH then
dataCargo.date_of_death = claim.mainsnak.datavalue.value.time
end
end
local allDates = table.concat( dates, ' or ' )
-- Places.
local placesClaims = item.claims[placeProp] or {}
local places = {}
for _,claim in pairs( placesClaims ) do
places[#places + 1] = '{{wdl|' .. claim.mainsnak.datavalue.value.id .. '}}'
end
local allPlaces = table.concat( places, ' or ' )
-- Put them together.
if allDates ~= '' and allPlaces ~= '' then
return allDates .. ' in ' .. allPlaces
else
return allDates .. allPlaces
end
end
p.parents = function( item, dataCargo )
local out = {}
local parentsIds = ''
if item.claims[PROP_MOTHER] ~= nil then
for _,claim in pairs( item.claims[PROP_MOTHER] ) do
local motherId = claim.mainsnak.datavalue.value.id
parentsIds = parentsIds .. ';' .. motherId
out[#out +1] = '{{wdl|' .. motherId .. '}}'
end
end
if item.claims[PROP_FATHER] ~= nil then
for _,claim in pairs( item.claims[PROP_FATHER] ) do
local fatherId = claim.mainsnak.datavalue.value.id
parentsIds = parentsIds .. ';' .. fatherId
out[#out +1] = '{{wdl|' .. fatherId .. '}}'
end
end
if #out ~= 0 then
dataCargo.parents = parentsIds
end
return out
end
p.children = function ( item )
local results = mw.ext.cargo.query( 'people', 'wikidata', {
where = 'parents HOLDS "' .. item.id .. '"'
} )
local out = {}
for _,res in pairs( results ) do
out[#out +1] = '{{wdl|' .. res.wikidata .. '}}'
end
return out
end
p.getMap = function ( item, dataCargo )
if item == nil or item.claims == nil or item.claims[PROP_COORDINATES] == nil then
return
end
local point = item.claims[PROP_COORDINATES][0].mainsnak.datavalue.value.latitude .. ', '
.. item.claims[PROP_COORDINATES][0].mainsnak.datavalue.value.longitude
dataCargo.coords = point
local mapParams = {
'',
point = point,
zoom = '14',
height = '300px',
width = '100%'
}
return mw.getCurrentFrame():callParserFunction{ name = '#cargo_display_map', args = mapParams }
end
p.getLabel = function ( id )
local item = mw.ext.UnlinkedWikibase.getEntity( id )
local contentLang = mw.language.getContentLanguage()
local langCode = contentLang:getCode()
-- Label in the content language.
if item ~= nil and item.labels ~= nil and item.labels[langCode] ~= nil then
return item.labels[langCode].value
end
-- Check fallback languages.
local fallbackLangs = contentLang:getFallbackLanguages()
for _,fallbackLang in pairs( fallbackLangs ) do
if item.labels[fallbackLang] ~= nil then
return item.labels[fallbackLang].value
end
end
-- Otherwise, show an error.
return 'No label for ' .. id
end
function getSitelinks( item, lang )
if item.sitelinks == nil then
return ''
end
local wikis = {
-- Interwiki prefix, logo filename, Wikidata ID
commonswiki = { 'commons', 'Commons-logo', 'Q565' },
enwiki = { 'wikipedia', 'Wikipedia-logo-v2', 'Q52' },
enwikisource = { 'wikisource', 'Wikisource-logo', 'Q263' },
enwikivoyage = { 'wikivoyage', 'Wikivoyage-logo', 'Q373' }
}
local sitelinks = {}
for site,info in pairs( item.sitelinks ) do
if wikis[site] == nil then
-- Show bare site ID as link if it's not configured above.
sitelinks[#sitelinks + 1] = '[' .. info.url .. ' ' .. site .. ']'
else
local prefix, logo, siteWikidataId = unpack( wikis[site] )
local link = prefix .. ':' .. info.title
local logo = '[[File:' .. logo .. '.svg|16x16px|alt=|link=' .. link .. ']]'
sitelinks[#sitelinks + 1] = logo .. ' [[' .. link .. '|' .. p.getLabel( siteWikidataId, lang ) .. ']]'
end
end
return {
class = 'mdl-infobox-sitelinks',
values = sitelinks
}
end
p.main = function( frame )
local wikidataId = nil
-- Check for 'wikidata' arg.
if frame and frame.args and frame.args.wikidata ~= nil and frame.args.wikidata ~= '' then
wikidataId = frame.args.wikidata
end
-- Check the calling template for 'wikidata' arg.
if frame and type( frame.getParent ) == 'function' and frame:getParent().args and frame:getParent().args.wikidata then
wikidataId = frame:getParent().args.wikidata
end
if wikidataId == nil then
return p.getErrorHtml( 'Wikidata ID not provided.' )
end
local item = mw.ext.UnlinkedWikibase.getEntity( wikidataId )
if item == nil or item.claims == nil then
return p.getErrorHtml( 'Wikidata ID ' .. wikidataId .. ' not found.' )
end
if item == 'invalid-item-id' then
return p.getErrorHtml( 'Wikidata ID ' .. wikidataId .. ' is invalid.' )
end
local lang = mw.getCurrentFrame():callParserFunction( 'int', 'lang' ) or "en"
-- For each row of the infobox, collect a label, and a set of values (for cargo, and for display).
local dataDisplay = {}
local dataCargo = {}
local rowNum = 1
-- Images.
if item.claims[PROP_IMAGE] ~= nil then
dataDisplay[rowNum] = { class = 'mdl-infobox-image', label = '', values = {} }
for claimNum,imageClaim in pairs( item.claims[PROP_IMAGE] ) do
dataDisplay[rowNum].values[claimNum] = '[[File:' .. imageClaim.mainsnak.datavalue.value .. '|300x300px]]'
-- Only add one image to Cargo data.
dataCargo.image = imageClaim.mainsnak.datavalue.value
end
rowNum = rowNum + 1
end
-- Sitelinks
local sitelinksList = getSitelinks( item, lang )
if sitelinksList ~= nil then
dataDisplay[rowNum] = sitelinksList
rowNum = rowNum + 1
end
-- Administrative location
local adminLocation = p.adminLocation( item, dataCargo )
if adminLocation ~= nil then
dataDisplay[rowNum] = adminLocation
rowNum = rowNum + 1
end
-- Birth and death.
local birth = p.dateAndPlace( item, dataCargo, PROP_DATE_OF_BIRTH, PROP_PLACE_OF_BIRTH )
if birth ~= '' then
dataDisplay[rowNum] = { class = 'mdl-infobox-birth', label = 'Born:', values = { birth } }
rowNum = rowNum + 1
end
local death = p.dateAndPlace( item, dataCargo, PROP_DATE_OF_DEATH, PROP_PLACE_OF_DEATH )
if death ~= '' then
dataDisplay[rowNum] = { class = 'mdl-infobox-death', label = 'Died:', values = { death } }
rowNum = rowNum + 1
end
-- Parents.
local parents = p.parents( item, dataCargo )
if #parents > 0 then
dataDisplay[rowNum] = { class = 'mdl-infobox-parents', label = 'Parents:', values = parents }
rowNum = rowNum + 1
end
-- Children.
local children = p.children( item )
if #children > 0 then
dataDisplay[rowNum] = { class = 'mdl-infobox-children', label = 'Children:', values = children }
rowNum = rowNum + 1
end
-- Map.
local map = p.getMap( item, dataCargo )
if map ~= nil then
dataDisplay[rowNum] = { class = 'mdl-infobox-map', values = { map } }
rowNum = rowNum + 1
end
-- Identifiers.
dataDisplay[rowNum] = { class = 'mdl-infobox-authority-control-head', label = 'Authority control', values = {} }
rowNum = rowNum + 1
dataDisplay[rowNum] = { class = 'mdl-infobox-authority-control plainlinks', label = '', values = {} }
dataDisplay[rowNum].values[1] = 'Wikidata: [[wikidata:' .. item.id .. '|' .. item.id .. ']]'
dataCargo.wikidata = item.id
local authControlIndex = 2
for _,claim in pairs( item.claims ) do
for snakNum,snak in pairs( claim ) do
if snak.mainsnak.datatype == 'external-id'
and snak.mainsnak.snaktype == 'value'
and snak.mainsnak.property ~= PROP_WIKISPORE_ID
then
local label = p.getLabel( snak.mainsnak.property )
local value = snak.mainsnak.datavalue.value
local authControlItem = mw.ext.UnlinkedWikibase.getEntity( snak.mainsnak.property )
if authControlItem.claims[PROP_FORMATTER_URL] ~= nil then
local url = string.gsub( authControlItem.claims[PROP_FORMATTER_URL][0].mainsnak.datavalue.value, '$1', value )
value = '[' .. url .. ' ' .. value .. ']'
end
dataDisplay[rowNum].values[authControlIndex] = label .. ': ' .. value
authControlIndex = authControlIndex + 1
end
end
end
rowNum = rowNum + 1
-- Build the table.
local ibTable = mw.html.create( 'table' )
for _,row in pairs( dataDisplay ) do
local value = ''
-- Single value (seems that `#{row.values}` doesn't work?).
if row.values[0] ~= nil and row.values[1] == nil then
value = row.values[0]
elseif row.values[1] ~= nil then
-- Multiple values (shown in a list).
value = '<ul>'
for _,val in pairs( row.values ) do
value = value .. '<li>' .. val .. '</li>'
end
value = value .. '</ul>'
end
ibTable:node( p.getTableRow( row.label, value, row.class ) )
end
local setPageWikibaseId = mw.getCurrentFrame():callParserFunction{ name = '#unlinkedwikibase', args = { '', id = item.id } }
local styles = mw.getCurrentFrame():extensionTag( 'templatestyles', nil, { src = 'Module:Infobox/styles.css' } )
local ib = mw.html.create( 'div' )
ib:attr( 'class', 'mdl-infobox' )
if item.labels.en ~= nil then
ib:wikitext( "<div class='mdl-infobox-label'>'''" .. item.labels.en.value .. "'''" )
if item.descriptions.en ~= nil then
ib:wikitext( "<br>" .. item.descriptions.en.value .. "" )
end
ib:wikitext( '</div>' )
end
ib:node( ibTable )
local cargoStore = p.getCargoStore( frame.args, dataCargo )
return styles .. setPageWikibaseId .. cargoStore .. tostring( ib )
end
p.adminLocation = function( item, dataCargo )
if item.claims[PROP_ADMIN_LOCATION] == nil then
return nil
end
local values = {}
local label = 'Administrative location'
for claimNum,claim in pairs( item.claims[PROP_ADMIN_LOCATION] ) do
local locationItem = mw.ext.UnlinkedWikibase.getEntity( claim.mainsnak.datavalue.value.id )
local localTitle = mw.ext.UnlinkedWikibase.getLocalTitle( claim.mainsnak.datavalue.value.id )
dataCargo.admin_location = claim.mainsnak.datavalue.value.id
if localTitle ~= nil then
dataCargo.admin_location = localTitle.prefixedText
end
if locationItem.claims ~= nil and locationItem.claims[PROP_INSTANCE_OF] ~= nil then
-- Get first 'instance of' label.
local locationInstanceItem = mw.ext.UnlinkedWikibase.getEntity( locationItem.claims[PROP_INSTANCE_OF][0].mainsnak.datavalue.value.id )
if locationInstanceItem.labels ~= nil and locationInstanceItem.labels.en ~= nil then
label = mw.getContentLanguage():ucfirst( locationInstanceItem.labels.en.value )
end
end
values[#values +1] = '{{wdl|' .. claim.mainsnak.datavalue.value.id .. '}}'
end
return {
class = 'mdl-infobox-admin-location',
label = label .. ':',
values = values
}
end
-- --
-- Get the {{#cargo_declare}} parser function, for use on a template page.
--
p.cargoDeclare = function ( frame )
if frame.args.cargo_table ~= nil and frame.args.cargo_table ~= '' and cargoConfig[frame.args.cargo_table] ~= nil then
local declareParts = {}
for col,type in pairs( cargoConfig[frame.args.cargo_table] ) do
declareParts[#declareParts + 1] = col .. ' = ' .. type
end
local declareFull = '{{#cargo_declare: _table= ' .. frame.args.cargo_table .. '\n | '.. table.concat( declareParts, "\n | " ) .. '\n}}'
if mw.getCurrentFrame():getParent() ~= nil then
-- There's no parent frame when testing.
return mw.getCurrentFrame():getParent():preprocess( '<noinclude>' .. declareFull .. '</noinclude>' )
end
end
return ''
end
-- --
-- Get {{#cargo_store}} parser function, wrapped in an includeonly tag.
-- @param {table} args Main module arguments.
-- @param {table} dataCargo Data values, keyed by column name.
-- @return string
--
p.getCargoStore = function ( args, dataCargo )
if args.cargo_table == nil or args.cargo_table == '' or cargoConfig[args.cargo_table] == nil then
return ''
end
local storeParts = {}
for col,type in pairs( cargoConfig[args.cargo_table] ) do
if dataCargo[ col ] ~= nil then
storeParts[#storeParts + 1] = col .. ' = ' .. dataCargo[ col ]
end
end
local storeFull = '{{#cargo_store: _table= ' .. args.cargo_table .. '\n | '.. table.concat( storeParts, "\n | " ) .. '\n}}'
if mw.getCurrentFrame():getParent() ~= nil then
-- There's no parent frame when testing.
return mw.getCurrentFrame():getParent():preprocess( '<includeonly>' .. storeFull .. '</includeonly>' )
end
return ''
end
-- =p.main({args={wikidata='Q8018316'}})
-- =p.main({args={wikidata='Q5501472', cargo_table='places'}})
return p