Open main menu
Home
Random
Donate
Recent changes
Special pages
Community portal
Preferences
About Stockhub
Disclaimers
Search
User menu
Talk
Contributions
Create account
Log in
Editing
Module:Wikidata Infobox
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
-- copied from https://commons.wikimedia.org/wiki/Module:Wikidata_Infobox local p = {} require('strict') local WikidataIB = require( 'Module:WikidataIB' ) local i18n = require( 'Module:Wikidata Infobox/i18n' ).i18n local getBestStatements = mw.wikibase.getBestStatements local frame = mw.getCurrentFrame() local config = { -- toggle/customize infobox features: defaultsort = true, interwiki = true, autocat = true, uploadlink = true, sitelinks = true, authoritycontrol = true, helperlinks = true, coordtemplate = 1, -- 0 = none, 1 = Geohack, 2 = Coord mapwidth = 250, mapheight = 250, imagesize = '230x500px', -- parameters for WikidataIB: spf = '', -- suppressfields fwd = 'ALL', -- fetchwikidata osd = 'no', -- onlysourced noicon = 'yes', -- pencil icon wdlinks = 'id', -- add links to Wikidata if no label found collapse = 10, -- collapse list of values if too many values maxvals = 30, -- stop fetching Wikidata after this number of values } -- variables set by main(): local ITEM -- mw.wikibase.entity table local QID -- qid of ITEM, e.g. 'Q42' local CLAIMS -- ITEM.claims local ISTAXON -- whether ITEM is a biological taxon local INSTANCEOF = {} -- Hash set of ITEM's best "instance of" values local MYLANG -- user's languge code local LANG -- language object of user's language local FALLBACKLANGS -- list containing MYLANG and its fallback languages -- Can't have more than one {{#coordinates:primary}}, so keep track of count local primary_coordinates = 0 --- Returns label of given Wikidata entity in user's language. --- If label doesn't exist, returns the id as link to Wikidata. --- @param id string --- @param nolink? boolean: Whether to return link to Wikidata if no label found local function getLabel( id, nolink ) local label = mw.wikibase.getLabel( id ) if label then return mw.text.nowiki( label ) -- nowiki to prevent wikitext injection elseif nolink then return id else return '[[d:' .. id .. ']]' end end --- Query Wikidata entity for the first best value of property _pid_. --- Returns nil if first best value is novalue or somevalue. --- Returns nil if entityOrId is neither table nor string. --- @param entityOrId table|string: getEntity() or qid. --- @param pid string --- @return unknown|nil local function getSingleValue( entityOrId, pid ) local claim if type( entityOrId ) == 'table' then claim = entityOrId:getBestStatements( pid )[1] elseif type( entityOrId ) == 'string' then claim = getBestStatements( entityOrId, pid )[1] end return claim and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value end --- Iterator function over a list of Wikidata claims/statements --- @param t table as returned by wikibase.getBestStatements local function iclaims( t ) local i = 1 return function() while i <= #t do local dv = t[i].mainsnak.datavalue local v = dv and dv.value i = i + 1 if v then return v end end end end --- Returns Commons sitelink (full page title), preferably to category --- @param qid string --- @return string|nil local function getCommonsLink( qid ) local sitelink = mw.wikibase.getSitelink( qid, 'commonswiki' ) if sitelink and sitelink:sub(1,9) == 'Category:' then return sitelink -- sitelink to category page end local maincat = getSingleValue( qid, 'P910' ) -- topic's main category if maincat and maincat.id then local sl = mw.wikibase.getSitelink( maincat.id, 'commonswiki' ) if sl then return sl end end local listcat = getSingleValue( qid, 'P1754' ) -- category related to list if listcat and listcat.id then local sl = mw.wikibase.getSitelink( listcat.id, 'commonswiki' ) if sl then return sl end end local P373 = getSingleValue( qid, 'P373' ) -- Commons category if P373 then return 'Category:' .. P373 end return sitelink -- sitelink to gallery page end local getSitelink = (mw.wikibase.getGlobalSiteId() == 'commonswiki') and getCommonsLink or mw.wikibase.getSitelink --- Returns sitelink to Commons as wikilink or the label of the given Q-item --- @param qid string local function getLinkOrLabel( qid ) local sitelink = getSitelink( qid ) if sitelink then return "[[:" .. sitelink .. "|" .. getLabel( qid, true ) .. "]]" else return getLabel( qid ) end end --- Renders snak as rich wikitext. Returns nil if snak is nil or false. --- @param snak table: claim.mainsnak or claim.qualifiers[pid] local function renderSnak( snak ) if not snak then return end local snaktype = snak.snaktype if snaktype == 'value' then local datatype = snak.datatype local value = snak.datavalue.value if datatype == 'wikibase-item' then return getLinkOrLabel( value.id ) else return mw.wikibase.formatValue( snak ) end elseif snaktype == 'somevalue' then local label = mw.message.new('Wikibase-snakview-variations-somevalue-label'):inLanguage(MYLANG):plain() return '<i style="color:#54595d">'..label..'</i>' end end --- Returns value whose "language of work or name" (P407) qualifier matches --- langcode, or nil if none matches. --- @param claims table as returned by getBestStatements() --- @param langcode string, e.g. "en" --- @return unknown|nil local function getValueByLang( claims, langcode ) for _, claim in ipairs( claims or {} ) do for _, qual in ipairs( claim.qualifiers and claim.qualifiers['P407'] or {} ) do if getSingleValue( qual.datavalue.value.id, 'P424' ) == langcode then return claim.mainsnak.datavalue and claim.mainsnak.datavalue.value end end end end --- If the given snaks of datatype monolingualtext contain a string in one of --- the user's fallback languages, the string is returned; otherwise a random --- string is retuned. The second return value indicates whether finding a --- string in one of the user's fallback languages was successful. --- @param snaks table, e.g. claims.qualifiers['P2096'] --- @return string?, boolean? success local function extractMonolingualText( snaks ) if not snaks or snaks == {} then return end -- collect strings into hash table with langcodes as keys local monotext = {} for _, snak in ipairs( snaks ) do local ms = snak.mainsnak or snak local v = ms and ms.datavalue and ms.datavalue.value if v then monotext[v.language] = v.text end end for _, lang in ipairs( FALLBACKLANGS ) do if monotext[lang] then return monotext[lang], true end end -- return random string local _, v = next( monotext ) return v, false end --- Parses a string in WikiHiero syntax local function expandhiero( hiero ) return frame:callParserFunction{ name = '#tag:hiero', args = {hiero} } end --- Returns a string containing two table rows local function format2rowline( header, content ) return '<tr><th class="wikidatainfobox-lcell" style="text-align:left" colspan="2">'..header..'</th></tr><tr><td style="vertical-align:top" colspan="2">'..content..'</td></tr>' end --- Returns a string containing a single table row local function format1rowline( trqid, header, content ) return '<tr id="'..trqid..'"><th class="wikidatainfobox-lcell">'..header..'</th><td style="vertical-align:top">'..content..'</td></tr>' end --- Returns a string containing the HTML markup for an infobox row. --- Returns nil if content is empty. --- @param eid string: ID of Wikidata entity whose label shall be used as heading --- @param content string|nil --- @param mobile? boolean: Set to true to show on devices with narrow screens local function formatLine( eid, content, mobile ) if not content or content == '' then return end local row = mw.html.create( 'tr' ) if not mobile then row:addClass( 'wdinfo_nomobile' ) -- [[Template:Wikidata_Infobox/styles.css]] end row:tag( 'th' ) :addClass( 'wikidatainfobox-lcell' ) :node( LANG:ucfirst( getLabel(eid) ) ) row:tag( 'td' ) :node( content ) return tostring( row ) end --- Returns unbulleted HTML list if given a sequence table. --- @param list string[] local function ubl( list ) return require('Module:List').unbulleted(list) end --- Given a language code, returns its databaseId (as used by Wikidata sitelinks). --- All databaseIds that a wiki knows are stored in its [[mw:Manual:sites table]]. --- @param langcode string local function databaseId( langcode ) local exceptions = { ['be-tarask'] = 'be_x_old', -- Belarusian (Taraškievica orthography) ['bho'] = 'bh', -- Bhojpuri ['cbk-zam'] = 'cbk_zam', -- Chavacano de Zamboanga ['gsw'] = 'als', -- Alemannic ['ike'] = 'iu', -- Inuktitut ['lzh'] = 'zh_classical', -- Classical Chinese ['map-bms'] = 'map_bms', -- Basa Banyumasan ['nan'] = 'zh_min_nan', -- Min Nan Chinese ['nb'] = 'no', -- Norwegian Bokmål ['nds-nl'] = 'nds_nl', -- Low Saxon ['mo'] = 'ro', -- Moldaawisk ['roa-tara'] = 'roa_tara', -- Tarantino ['rup'] = 'roa_rup', -- Aromanian ['sgs'] = 'bat_smg', -- Samogitian ['vro'] = 'fiu_vro', -- Võro ['yue'] = 'zh_yue', -- Cantonese -- I did my best to make this list as comprehensive as possible. -- Useful pages for finding exceptions: -- [[mw:Manual:$wgExtraLanguageCodes]] -- [[meta:Special_language codes]] -- [[meta:List_of_Wikipedias#Nonstandard_language_codes]] -- [[meta:Template:N en/list]] -- [[meta:Template:Wikilangcode]] } local exception = exceptions[langcode] if exception then return exception end return langcode:gsub("-.*", "") -- delete everything after hyphen end --- Wrapper around WikidataIB. Returns nil if the item has no _pid_ statement. --- @param pid string: Wikidata property id --- @param args? table: arguments for WikidataIB --- @return string|nil local function getValue( pid, args ) args = args or {} -- linking many values harms performance if the value items are big and the sitelink needs to be taken from P910, P1754 or P373 local collapse = args.collapse or config.collapse local linked if pid ~= 'P2789' and pid ~= 'P527' and pid ~= 'P1382' then -- See [[Template_talk:Wikidata_Infobox#P2789_-_connects_with]] linked = #getBestStatements(args.qid or QID, pid) <= collapse end return WikidataIB._getValue{ pid, name = pid, qid = args.qid or QID, linked = args.linked or linked, wdlinks = args.wdlinks or config.wdlinks, prefix = args.prefix, postfix = args.postfix, linkprefix = ':', -- suppress categorization qlinkprefix = ':', -- suppress categorization sorted = args.sorted, qual = args.qual or 'MOST', qualsonly = args.qualsonly, maxvals = args.maxvals or config.maxvals, postmaxvals = '…', collapse = collapse, spf = args.spf or config.spf, fwd = args.fwd or config.fwd, osd = args.osd or config.osd, rank = 'best', noicon = args.noicon or config.noicon, list = args.list or 'Unbulleted list', sep = args.sep, unitabbr = args.unitabbr, df = args.df, -- date format plaindate = args.plaindate, lang = args.lang, gendered = args.gendered, } end --- Used if no custom logic was specified for pid. local function defaultFunc( pid, args ) return formatLine( pid, getValue(pid, args) ) end local function defaultFuncMobile( pid, args ) return formatLine( pid, getValue(pid, args), true ) end local function defaultFuncMobileGendered( pid ) return formatLine( pid, getValue(pid, {gendered=true}), true ) end local function getAudio( pid ) local audiofile = getSingleValue( ITEM, pid ) return audiofile and formatLine( pid, '[[File:' .. audiofile .. '|100px]]' ) end local function getAudioByLang( pid ) local claims = ITEM:getBestStatements( pid ) for i = 1, #FALLBACKLANGS do local audiofile = getValueByLang( claims, FALLBACKLANGS[i] ) if audiofile then return formatLine( pid, '[[File:' .. audiofile .. '|100px]]' ) end end end -- Example at [[Category:Thutmosis III]] local function getHieroglyphs() local rows = {} for _, v in ipairs( ITEM:getBestStatements('P7383') ) do -- name in hiero markup local idv = v.mainsnak.datavalue.value if v.qualifiers and v.qualifiers['P3831'] then for _, w in ipairs( v.qualifiers['P3831'] ) do if w.datavalue then local label = getLabel( w.datavalue.value.id ) rows[#rows+1] = format2rowline( label, expandhiero(idv) ) end end else rows[#rows+1] = format2rowline( getLabel('Q82799', true), expandhiero(idv) ) end end return table.concat( rows ) end --- WikidataIB arguments for birth and death related properties local birthdeath_args = { list = '', quals = table.concat({ 'P4241', -- refine date 'P805', -- statement is subject of 'P1932', -- object stated as 'P1810', -- subject named as 'P5102', -- nature of statement 'P1480', -- sourcing circumstances 'P459', -- determination method 'P1013', -- criterion used 'P1441', -- present in work 'P10663', -- applies to work }, ',') } local function getBirth( pid ) local out = {} out[#out+1] = getValue( pid, birthdeath_args ) -- date out[#out+1] = CLAIMS['P19'] and getValue( 'P19', birthdeath_args ) -- place out[#out+1] = extractMonolingualText( ITEM:getBestStatements('P1477') ) -- name return formatLine( pid, table.concat(out, '<br>') ) end local function getDeath( pid ) local out = {} out[#out+1] = getValue( pid, birthdeath_args ) -- date out[#out+1] = CLAIMS['P20'] and getValue( 'P20', birthdeath_args ) -- place return formatLine( pid, table.concat(out, '<br>') ) end local function getWebsite( pid ) for _, claim in ipairs( ITEM:getBestStatements(pid) ) do local quals = claim.qualifiers local url = claim.mainsnak.datavalue and claim.mainsnak.datavalue.value if url and not (quals and quals['P582']) then -- no "end time" qualifier return '<tr><td colspan=2 style="text-align:center">['..url..' '..getLabel(pid)..']</td></tr>' end end end local function getSignature( pid ) local img = getSingleValue( ITEM, pid ) if img then local alt = LANG:ucfirst( getLabel(pid, true) ) return '<tr class="wdinfo_nomobile"><td colspan=2 style="text-align:center"><span class="wpImageAnnotatorControl wpImageAnnotatorCaptionOff">[[File:'..img..'|150px|alt='..alt..']]</span></td></tr>' -- equivalent to {{ImageNoteControl | caption=off | type=inline}} end end --- If ITEM has a pid statement, this behaves exactly like defaultFunc. Otherwise --- figures out the region that ITEM is in and queries the region item for pid. --- It finds the region by first checking if ITEM has a regionPid value. --- Otherwise it takes the region from the P971 (category combines topics) --- statement of ITEM's main category. --- Examples at Cat:Health_in_Gabon, Cat:Economy_of_Germany, Q7246071 --- @param pid string, e.g. "P2250" for life expectancy --- @param regionPid string: usually "P17" (country) or "P276" (location) --- @param collapse number: argument for WikidataIB local function getByRegion( pid, regionPid, collapse ) local region = getSingleValue( ITEM, regionPid ) if CLAIMS[pid] then region = QID elseif region then region = region.id else local maincat = getSingleValue( ITEM, 'P910' ) -- topic's main category if maincat then for topic in iclaims( getBestStatements(maincat.id, 'P971') ) do local id = topic.id if id ~= 'Q12147' and id ~= 'Q8434' and id ~= 'Q159810' then -- assume id is QID of a region if it's not the QID for "health", "education", or "economy" region = id end end end end return region and defaultFunc( pid, { qid = region, collapse = collapse, }) end local function getByCountry( pid ) return getByRegion( pid, 'P17', 10 ) end local function getByLocation( pid ) return getByRegion( pid, 'P276', 10 ) end local function getByLocationCollapse4( pid ) return getByRegion( pid, 'P276', 4 ) end local function getPrimeFactors() local out = {} for _, claim in ipairs( ITEM:getBestStatements('P5236') ) do local quals = claim.qualifiers and claim.qualifiers['P1114'] local quantity = quals and quals[1].datavalue.value.amount if quantity then quantity = quantity:sub(2) -- strip plus sign out[#out+1] = renderSnak(claim.mainsnak) .. '<sup>'..quantity..'</sup>' else out[#out+1] = renderSnak(claim.mainsnak) end end return formatLine( 'Q4846249', table.concat(out, ' × ') ) end local function getUnicodeChars( pid ) local rows = {} for _, v in ipairs( ITEM:getBestStatements(pid) ) do local idv = v.mainsnak.datavalue.value for _, w in ipairs( v.qualifiers and v.qualifiers['P3831'] or {} ) do if w.datavalue then local qualid = w.datavalue.value.id rows[#rows+1] = format1rowline( qualid, getLabel(qualid), idv ) end end end return table.concat( rows ) end local function getCodes( pid ) local rows = {} for _, v in ipairs( ITEM:getBestStatements(pid) ) do local idv = v.mainsnak.datavalue.value for _, w in ipairs( v.qualifiers and v.qualifiers['P3294'] or {} ) do if w.datavalue then local qualid = w.datavalue.value.id if qualid == "Q68101340" then idv = expandhiero( idv ) end rows[#rows+1] = format1rowline( qualid, getLinkOrLabel(qualid), idv ) end end end return table.concat( rows ) end local function getCodeImages( pid ) local rows = {} for _, v in ipairs( ITEM:getBestStatements(pid) ) do local idv = v.mainsnak.datavalue.value for _, w in ipairs( v.qualifiers and v.qualifiers['P3294'] or {} ) do if w.datavalue then local qualid = w.datavalue.value.id local img = '[[File:' .. idv .. '|none|35px]]' rows[#rows+1] = format1rowline( qualid, getLabel(qualid), img ) end end end return table.concat( rows ) end local function getLocation() local function fallback() local set = {} -- locations as keys local out = {} -- locations as values for _, pid in ipairs{ 'P706', 'P276', 'P131', 'P17' } do for _, claim in ipairs( ITEM:getBestStatements(pid) ) do local location if pid == 'P17' then -- don't link to countries local dv = claim.mainsnak.datavalue location = dv and getLabel( dv.value.id ) else location = renderSnak( claim.mainsnak ) end if location and not set[location] then local n = #out + 1 set[location] = true -- we don't want duplicate values out[n] = location -- we want to preserve the order if n > config.maxvals then out[n] = '…' -- postmaxvals return formatLine( 'P276', ubl(out) ) end end end end return formatLine( 'P276', ubl(out) ) end local P131,P276,P706 = CLAIMS['P131'] or {}, CLAIMS['P276'] or {}, CLAIMS['P706'] or {} if (#P131 < 2) and (#P276 < 2) and (#P706 < 2) then return formatLine( 'P276', WikidataIB.location{ args={QID} } ) or fallback() else return fallback() end end local function getAddress() local address if CLAIMS['P6375'] then -- street address as string address = getValue( 'P6375' ) elseif CLAIMS['P669'] then -- located on street address = getValue( 'P669', { qual='P670, P281' } ) end return formatLine( 'P6375', address ) end local function getAuthors() if CLAIMS['P50'] or CLAIMS['P2093'] then local args = { list='', sep='</li><li>' } local authors = getValue( 'P50', args ) or '' local namestrings = getValue( 'P2093', args ) return formatLine( 'P50', ubl{authors, namestrings} ) end end local function getDifferentFrom() local out = {} local i = 0 for different in iclaims( ITEM:getBestStatements('P1889') ) do i = i + 1 if i > config.maxvals then break end local href = getSitelink( different.id ) or ( 'd:'..different.id ) local label = getLabel( different.id, true ) local class = getSingleValue( different.id, 'P31' ) local isdab = class and class.id == 'Q4167410' local icon = isdab and ' [[File:Disambig.svg|18px|alt='..mw.wikibase.getLabel('Q4167410')..']]' local desc = mw.wikibase.getDescription( different.id ) if desc then label = '<span title="'..mw.text.nowiki(desc)..'">'..label..'</span>' end out[#out+1] = string.format( '[[:%s|%s]]%s', href, label, icon or '' ) end return formatLine( 'P1889', ubl(out) ) end --- Returns common taxon name using [[Module:Wikidata4Bio]] local function getVernacularName() if ISTAXON then local vn = frame:expandTemplate{ title = 'VNNoDisplay', args = { useWikidata = QID }} if vn:sub(3,10) ~= 'Category' and not vn:match('class="error') then -- we found at least one common name and there are no errors local label = LANG:ucfirst( getLabel('Q502895') ) return '<tr><td colspan=2><table style="width:100%"><tr><th style="background: #cfe3ff>'..label..'</th></tr><tr><td><div style="overflow-wrap: break-word" class="mw-collapsible mw-collapsed wikidatainfoboxVN" id="wdinfoboxVN">'..vn..'</div></td></tr></table></td></tr>' end end end --[[ Taxontree doesn't exist locally local function getTaxontree() local content = require('Module:Taxontree').show{ args = { qid = QID, authorcite = 'y', first = 'y', }} local label = LANG:ucfirst( getLabel('Q8269924') ) return '<tr><td colspan=2><table style="width:100%" id="wdinfo_taxon" class="mw-collapsible"><tr><th style="background: #cfe3ff" colspan=2>'..label..'</th></tr>'..content..'</table></td></tr>' end ]] local function getOriginalCombination() local ocomb = getSingleValue( ITEM, 'P1403' ) ocomb = ocomb and ocomb.id local taxoname = ocomb and getSingleValue( ocomb, 'P225' ) or '' local citation = ocomb and getSingleValue( ocomb, 'P6507' ) or '' if taxoname then return formatLine( 'P1403', '<i>'..taxoname..'</i>' .. ' ' .. citation ) end end --- Creates a taxon author citation from P405 and P574 qualifiers if --- P6507 (taxon author citation as string) not present since otherwise --- Taxontree already shows the citation. local function getTaxonAuthor() local claims = CLAIMS['P225'] -- P225 = taxon name if #claims > 1 then return defaultFunc( 'P225' ) -- Example at [[Category:Acacia stricta]] elseif #claims == 1 then if CLAIMS['P6507'] then -- P6507 = taxon author citation (string) return -- Taxontree already shows citation, see [[Ophiogymna]] end local quals = claims[1].qualifiers local author = renderSnak( quals and quals['P405'] and quals['P405'][1] ) local year = renderSnak( quals and quals['P574'] and quals['P574'][1] ) if author and year then return formatLine( 'P405', author .. ', ' .. year ) elseif year then return formatLine( 'P574', year ) -- [[Cat:Porphyrophora polonica]] end end end --- Given an area, returns a map zoom level to use with mw:Extension:Kartographer. --- Fallback output is 15. Needed by [[Template:Infobox theatre/wikidata]] and --- [[Template:Infobox library/wikidata]] function p.autoMapZoom( area ) if not area then return 15 end if area.unit == 'http://www.wikidata.org/entity/Q35852' then -- hectare area = area.amount / 100 -- convert to km² elseif area.unit == 'http://www.wikidata.org/entity/Q25343' then -- m² area = area.amount / 1e6 -- convert to km² elseif area.unit == 'http://www.wikidata.org/entity/Q81292' then -- acre area = area.amount * 0.004 -- convert to km² else area = tonumber( area.amount ) -- assume the unit is km² end local LUT = { 5000000, 1000000, 100000, 50000, 10000, 2000, 150, 50, 19, 14, 5, 1, 0.5 } for zoom, scale in ipairs( LUT ) do if area > scale then return zoom + 1 end end return 15 end local function getCoordinates( pid ) local coords = getSingleValue( ITEM, pid ) if coords then local geohack_link = require('Module:Coordinates')._GeoHack_link local out local long = coords.longitude local lat = coords.latitude local globeId = coords.globe:match( "Q%d+" ) if globeId == 'Q2' then -- coords are on Earth local externaldata = { -- [[mw:Help:Extension:Kartographer]] type = "ExternalData", service = "geoshape", ids = QID, properties = { ['fill'] = "#999999", ['stroke'] = "#636363", ['stroke-width'] = 2 } } -- detect roads, mountain passes, rivers, borders etc. if CLAIMS['P2043'] or CLAIMS['P16'] -- length, transport network or CLAIMS['P974'] or CLAIMS['P4552'] -- tributary, mountain range or CLAIMS['P177'] or CLAIMS['P1064'] -- crosses, track gauge or CLAIMS['P15'] or CLAIMS['P14'] -- route map, traffic sign or CLAIMS['P930'] or CLAIMS['P3858'] then -- electrification, route diagram externaldata.service = 'geoline' externaldata.properties['stroke'] = "#ff0000" end local geojson = { externaldata, { type = "Feature", geometry = { type="Point", coordinates = {long, lat} }, properties = { ['marker-size'] = "medium", ['marker-color'] = "006699" }, }, } local zoom if CLAIMS['P402'] then -- OpenStreetMap relation ID -- Let Kartographer figure out zoom level based on OSM geoshape. -- Kartographer uses [[mw:Wikimedia_Maps/API#OSM_Geoshapes_and_lines]] -- instead of P402 to find the OSM relation but there is no Lua -- interface for that. You can help adding P402 statements using -- https://mix-n-match.toolforge.org/#/catalog/688 else local area = getSingleValue( ITEM, 'P2046' ) zoom = p.autoMapZoom( area ) end out = frame:extensionTag( 'mapframe', mw.text.jsonEncode(geojson), { frameless = 1, lang = MYLANG, width = config.mapwidth, height = config.mapheight, zoom = zoom, align = 'center', }) if config.trackingcats then out = out ..'[[Category:Uses of Wikidata Infobox with maps]]' end if config.coordtemplate == 1 then if primary_coordinates == 0 then out = out .. frame:callParserFunction('#coordinates:primary', lat, long) primary_coordinates = 1 end out = out .. '<small>'..geohack_link{ lat=lat, lon=long, lang=MYLANG }..'</small>' elseif config.coordtemplate == 2 then local args = { display = 'inline,title', format = 'dms', nosave = 1, qid = QID } -- TODO use the module directly... out = out .. '<small>'..frame:expandTemplate{ title = 'Coord', args = args }..'</small>' end else -- coords not on Earth local globe = mw.wikibase.getLabelByLang( globeId, 'en' ) out = geohack_link{ lat=lat, lon=long, globe=globe, lang=MYLANG } end return '<tr class="wdinfo_nomobile"><td colspan=2 style="text-align:center">'..out..'</td></tr>' elseif config.trackingcats and (CLAIMS['P706'] or CLAIMS['P131']) then return '[[Category:Uses of Wikidata Infobox with no coordinate]]' end end --- Show map using [[mw:Help:Map Data]] if ITEM has no coordinates local function getCommonsMapData() if CLAIMS['P625'] then return end local commonsdata = getSingleValue( QID, 'P3896' ) if not commonsdata then return end local geojson = {{ type = "ExternalData", service = 'page', title = commonsdata:sub(6), -- strip "Data:" prefix }} local out = frame:extensionTag( 'mapframe', mw.text.jsonEncode(geojson), { frameless = 1, lang = MYLANG, width = config.mapwidth, height = config.mapheight, align = 'center', }) if config.trackingcats then out = out ..'[[Category:Uses of Wikidata Infobox with maps]]' end return '<tr class="wdinfo_nomobile"><td colspan=2 style="text-align:center">'..out..'</td></tr>' end local function getCelestialCoordinates() local ra = getSingleValue( ITEM, 'P6257' ) -- right ascension local de = getSingleValue( ITEM, 'P6258' ) -- declination if ra and de then local url = 'http://www.wikisky.org/?ra='..(ra.amount / 15)..'&de='..de.amount..'&de=&show_grid=1&show_constellation_lines=1&show_constellation_boundaries=1&show_const_names=1&show_galaxies=1&img_source=DSS2&zoom=9 ' local ra_unit = getLabel( ra.unit:match('Q%d+') ) local de_unit = getLabel( de.unit:match('Q%d+') ) local ra_fmt = LANG:formatNum( tonumber(ra.amount) ) local de_fmt = LANG:formatNum( tonumber(de.amount) ) local text = LANG:ucfirst( getLabel('P6257') )..' '..ra_fmt..' '..ra_unit.. '<br>'..LANG:ucfirst( getLabel('P6258') )..' '..de_fmt..' '..de_unit return '<tr class="wdinfo_nomobile"><td colspan=2 style="text-align:center">['..url..text..']</td></tr>' end end local autocats_by_id = { P649 = 'National Register of Historic Places with known IDs', P709 = 'Listed buildings in Scotland with known IDs', P758 = 'Cultural heritage monuments in Norway with known IDs', P808 = 'Cultural heritage monuments in Spain by ID', P809 = 'Uses of Wikidata Infobox providing WDPA ids', P1216 = 'Listed buildings in England with known IDs', P1459 = 'Listed buildings in Wales with known IDs', P1700 = 'SIPA with known IDs', P1702 = 'IGESPAR with known IDs', P2783 = 'Listed buildings in Denmark with known IDs', P2917 = 'Buildings of Madrid with COAM Register number', P2961 = 'Periodicals in the Biblioteca Virtual de Prensa Histórica', P3596 = 'Archaeological monuments in Denmark with known IDs', P3759 = 'Uses of Wikidata Infobox providing SAHRA ids', P4009 = 'Cultural heritage monuments in Finland with known IDs', P4244 = 'Uses of Wikidata Infobox providing Bavaria ids', P5094 = 'HPIP with known IDs', P5500 = 'IPHAN with known IDs', } --- qualifiers for "headquarters location" (P159) local hq_quals = table.concat({ 'P6375', -- street address 'P669', -- located on street 'P670', -- street number 'P4856', -- conscription number 'P281', -- postal code 'P580', -- start time 'P582', -- end time 'P585', -- point in time 'P1264', -- valid in period 'P3831', -- object has role 'P1810', -- subject named as 'P5102', -- nature of statement }, ',' ) --- associates pids with a table of arguments for WikidataIB or with a function --- that will be called with pid as the only argument local property_logic = { P51 = getAudio, -- audio P989 = getAudioByLang, -- spoken text audio P443 = getAudioByLang, -- pronunciation audio P990 = getAudioByLang, -- recording of subject's voice P7383 = getHieroglyphs, -- name in hiero markup P569 = getBirth, -- date of birth P570 = getDeath, -- date of death P69 = { qual='P580,P582,P585,P512,P812' }, -- educated at P185 = { collapse=4, maxvals=20 }, -- doctoral student P106 = defaultFuncMobileGendered, -- occupation P39 = { qual='P642,P580,P582,P585', collapse=6 }, -- position held P2522 = { collapse=4 }, -- victory P26 = { qual='DATES' }, -- spouse, TODO: sort by date qualifier (also P793) P451 = { qual='DATES' }, -- partner P166 = { qual='P585' }, -- award received P856 = getWebsite, -- official website P109 = getSignature, -- signature P31 = defaultFuncMobile, -- instance of P2250 = getByCountry, -- life expectancy P4841 = getByCountry, -- total fertility rate P5236 = getPrimeFactors, -- prime factor P487 = getUnicodeChars, -- Unicode character P3295 = getCodes, -- code P7415 = getCodeImages, -- code (image) P3270 = getByLocation, -- compulsory education (minimum age) P3271 = getByLocation, -- compulsory education (maximum age) P6897 = getByLocationCollapse4, -- literacy rate P2573 = getByLocationCollapse4, -- number of out-of-school children P971 = { osd='no' }, -- category combines topics P180 = { list='prose', qual='' }, -- depicts P6375 = getAddress, -- street address P276 = getLocation, -- location P50 = getAuthors, -- author P2789 = { qual='' }, -- connects with P85 = { qual='DATES' }, -- anthem P953 = { qual='P407', prefix="[", postfix="]" }, -- full work at P127 = { qual='DATES' }, -- owned by P159 = { qual=hq_quals }, -- headquarters location P466 = { collapse=5 }, -- occupant P126 = { collapse=5, maxvals=20 }, -- maintained by P348 = { qual='P548,P577,P805' }, -- software version identifier P286 = { collapse=3 }, -- head couch P527 = { collapse=5, maxvals=20 }, -- has part P1382 = { collapse=5, maxvals=20 }, -- partially coincident with P1990 = { collapse=5 }, -- species kept P1923 = { collapse=5, maxvals=10 }, -- participating team P1346 = { collapse=5, maxvals=20 }, -- winner P112 = { maxvals=20 }, -- founded by P577 = { linked = 'no', -- make film categories load much quicker rank = 'preferred normal', -- See [[d:Property_talk:P577#Constraint_about_unique_best_value]] }, P140 = { qual='P585' }, -- population (qual = point in time) P200 = { collapse=4, maxvals=20 }, -- lake inflows P205 = { collapse=5, maxvals=20 }, -- basin country P974 = { collapse=5, maxvals=20 }, -- tributary P726 = { collapse=5 }, -- candidate P1889 = getDifferentFrom, -- different from P460 = { collapse=20, list='' }, -- same as (lots of values for given names) P1843 = getVernacularName, -- taxon common name -- P171 = getTaxontree, -- parent taxons P1403 = getOriginalCombination, -- original combination P225 = getTaxonAuthor, -- taxon name (and qualifiers) P2078 = getWebsite, -- user manual URL P625 = getCoordinates, -- coordinate location P3896 = getCommonsMapData, -- geoshape P6257 = getCelestialCoordinates, -- right ascension } --[==[---------------------------------------------------------------------- This table is used by main() to generate the infobox and by doc() to generate [[Template:Wikidata Infobox/doc/properties]]. * `humans_allowed` determines whether the group should be displayed if the item is a human (Q5) or a fictional human (Q15632617). It defaults to false. * A group will only be displayed if `P31_allowed_values` contains the "instance of" (P31) value of the item or `P31_allowed_values` is not present. * If `bypass_property_exists_check` is set to true, the infobox tries to fetch the values for each pid in the group, even if the item has no pid statement. * `logic` can be a function that will be called with pid as the only argument. `logic` can also be a WikidataIB arguments table for defaultFunc. ]==] local property_groups = { { groupname = 'Switchable images', -- this group needs to be at index 1 comment = 'Users can switch between these images using [[MediaWiki:Gadget-Infobox.js|Gadget-Infobox.js]].', humans_allowed = true, pids = {'P18','P117','P8224','P1442','P1801','P2716','P3383','P4640','P4291','P3451','P5252','P2713','P8592','P8517','P5555','P5775','P7417','P9721','P3311','P7420','P7457','P8195','P1543','P996','P3030','P154','P2910','P41','P94','P4004','P158','P2425','P8766','P14','P1766','P15','P8512','P181','P207','P242','P1944','P1943','P1846','P1621','P367','P491','P6655','P10','P4896'}, }, { groupname = 'Audio and hieroglyphs', humans_allowed = true, pids = {'P51','P989','P443','P990','P7383'}, }, { groupname = 'Human', P31_allowed_values = { 'Q5', 'Q15632617' }, humans_allowed = true, pids = {'P1559','P569','P570','P1196','P509','P157','P119','P742','P2031','P2032','P1317','P27','P1532','P551','P69','P184','P185','P106','P2416','P54','P108','P463','P102','P39','P101','P135','P66','P103','P97','P2962','P2522','P53','P22','P25','P3373','P40','P26','P451','P937','P800','P1441','P166','P856','P109'}, }, { groupname = 'Instance/subclass of', pids = {'P31','P279'}, }, { groupname = 'Health by region', P31_allowed_values = { 'Q64027457' }, pids = {'P2250','P4841'}, bypass_property_exists_check = true, }, { groupname = 'Natural number', P31_allowed_values = { 'Q21199' }, pids = {'P5236','P487','P3295','P7415'}, }, { groupname = 'Education by region', P31_allowed_values = { 'Q64801076' }, pids = {'P3270','P3271','P6897','P2573'}, bypass_property_exists_check = true, }, { groupname = 'National economy', P31_allowed_values = { 'Q6456916' }, pids = {'P38','P2299','P4010','P2131','P2132','P2219','P1279','P2134','P2855'}, bypass_property_exists_check = true, logic = getByLocationCollapse4, }, { groupname = 'Miscellaneous 1', pids = {'P361','P1639','P1269','P921','P629','P1559','P452','P7163','P971','P4224','P831','P2317','P138','P825','P417','P547','P180','P2596','P186','P136','P376','P3018','P7532'}, }, { groupname = 'Location', comment = 'The properties {{P|131}}, {{P|276}}, {{P|706}}, and {{P|17}} together produce a single infobox row.', pids = {'P276'}, bypass_property_exists_check = true, }, { groupname = 'Miscellaneous 2', pids = {'P1001','P206','P5353','P4856','P9759'}, }, { groupname = 'Address', comment = 'If {{P|6375}} does not exist, the address will be formed from {{P|670}}, {{P|669}}, and {{P|281}}.', pids = {'P6375'}, bypass_property_exists_check = true, }, { groupname = 'Miscellaneous 3', pids = {'P495','P1885','P149','P708','P2872','P16','P2789','P59','P65','P215','P223','P196','P36','P122','P194','P208','P209','P37','P85','P38','P35','P6','P210'}, }, { groupname = 'Author', comment = 'Will be displayed together with {{P|2093}}.', pids = {'P50'}, bypass_property_exists_check = true, }, { groupname = 'Miscellaneous 4', pids = {'P655','P123','P1433','P84','P193','P170','P86','P676','P87','P61','P189','P98','P58','P110','P162','P175','P393','P291','P407','P2635','P437','P953','P275','P1441','P1080','P88','P6291','P199','P169','P366','P121','P127','P159','P466','P137','P126','P177','P2505','P144','P822','P115','P5138','P118','P505','P286','P527','P1990','P2522','P1427','P1444','P1923','P1132','P1346','P176','P1071','P617','P504','P532','P8047','P289','P426','P113','P114','P375','P619','P1145','P522','P664','P823','P5804','P57','P161','P195','P217','P178','P112','P400','P306','P1435','P814','P141','P348','P585','P606','P729','P730','P580','P571','P577','P5444','P575','P1619','P3999','P582','P576','P2669','P793','P516','P2957','P2109','P618','P128','P129','P111','P179'}, }, { groupname = 'Quantities', pids = {'P1093','P2067','P2261','P2262','P2049','P2386','P2043','P3157','P2583','P2048','P5524','P2808','P2144','P3439','P4183','P5141','P4552','P2660','P2659','P610','P559','P7309','P1082','P2052','P2217','P2046','P2044','P2050','P2047'}, logic = { unitabbr='yes' }, }, { groupname = 'Miscellaneous 5', pids = {'P140','P1083','P2351','P2324','P6801','P6855','P3032','P3137','P770','P1398','P167','P81','P197','P833','P834'}, }, { groupname = 'Water', pids = {'P885','P403','P200','P201','P4614','P205','P974','P4792','P4661','P469','P2673','P2674'}, }, { groupname = 'Miscellaneous 6', pids = {'P155','P156','P1365','P1366','P3730','P3729'}, }, { groupname = 'Elections', pids = {'P991','P726','P1831','P1867','P1868','P1697','P5043','P5045','P5044'}, }, { groupname = 'Miscellaneous 7', pids = {'P1590','P1120','P1446','P1339','P1092','P784','P783','P785','P786','P787','P788','P789','P183','P2130','P2769','P1174','P859','P218','P78','P238','P239','P1889','P460','P1382','P2010','P2009','P2033','P1531','P8193'}, }, { groupname = 'Taxon common name', comment = "Common names are taken from the item's label, sitelink, and {{P|1843}}.", pids = {'P1843'}, bypass_property_exists_check = true, }, { groupname = 'Taxonomy', pids = {'P171','P1403','P225'}, }, { groupname = 'Miscellaneous 8', pids = {'P6591','P7422','P2078','P856','P6257'}, }, { groupname = 'Maps', comment = '{{P|3896}} is only used if no {{P|625}} statement exists. Tracked at {{c|Uses of Wikidata Infobox with maps}}.', pids = {'P625','P3896'}, bypass_property_exists_check = true, }, } local externalIDs = { { groupname = 'Authority control', pids = {'P213','P214','P227','P244','P268','P269','P270','P349','P409','P508','P640','P651','P691','P886','P902','P906','P947','P949','P950','P1003','P1006','P1015','P1048','P1157','P1207','P1225','P1415','P1695','P2558','P2581','P4819','P5034','P5587','P7293','P8189','P9371','P10539',} }, { groupname = 'Books/magazines/authors/libraries', pids = {'P236','P271','P396','P648','P723','P724','P2961','P5199',} }, { groupname = 'Science', pids = {'P356','P496','P549','P698','P717','P932','P1053','P2349','P3083','P8273',} }, { groupname = 'Biology', pids = {'P428','P627','P685','P687','P6535','P815','P830','P838','P842','P846','P850','P938','P959','P960','P961','P962','P1070','P1076','P1348','P1391','P1421','P1727','P1745','P1746','P1747','P1761','P1772','P1832','P1895','P1940','P1991','P1992','P2007','P2026','P2036','P2040','P2426','P2434','P2455','P2464','P2752','P2833','P2946','P3031','P3060','P3064','P3099','P3100','P3101','P3102','P3151','P3240','P3288','P3398','P3420','P3444','P3591','P3594','P3606','P3746','P4024','P4122','P4194','P4301','P4526','P4567','P4728','P4758','P4855','P5036','P5037','P5055','P5216','P5221','P5257','P5299','P6678','P7051',} }, { groupname = 'Art', pids = {'P245','P347','P434','P650','P781','P1212','P1882','P1901','P3293','P3634','P4399','P4659','P4701','P5950','P6506','P6631','P7704','P8386',} }, { groupname = 'Culture', pids = {'P345','P539','P1219','P1220','P1248','P1362','P6113','P6132',} }, { groupname = 'Sports', pids = {'P1146','P1440','P1469','P1665','P2020','P2276','P2446','P2458','P2574','P3171','P3537','P3538','P3681','P3924','P8286',} }, { groupname = 'Cultural heritage and architecture', pids = {'P359','P380','P381','P481','P649','P709','P718','P757','P758','P762','P808','P1216','P1459','P1483','P1600','P1700','P1702','P1708','P1764','P1769','P2424','P2783','P2081','P2917','P3038','P3177','P3178','P3318','P3596','P3758','P3759','P4009','P4075','P4102','P4244','P4360','P4372','P4868','P5094','P5310','P5313','P5500','P5525','P5528','P6102','P6542','P6736','P7006','P7170','P7304','P7630','P7659','P7694','P7900','P9148','P9154','P9339','P9342','P10486',} }, { groupname = 'Protected areas', pids = {'P809','P3425','P3613','P3974','P5965','P6602','P6230','P6280','P6478','P6560','P6659','P3296','P677',} }, { groupname = 'Places and geographical features', pids = {'P402','P3120','P3580','P3616','P3628','P4266','P6630','P7350','P7352','P7548','P8655','P8988','P10451','P4533',} }, { groupname = 'Administrative subdivisions', pids = {'P772','P836','P1894','P3118','P3615','P3639','P3419','P7526','P2788','P7577','P7606','P7635','P7636','P7579','P7752','P7673','P7674','P7736','P7735',} }, { groupname = 'Other', pids = {'P458','P587','P2037','P3112','P10557','P3479','P4344','P6228','P7721',} }, } --- @param group table local function groupIsAllowed( group ) local ishuman = INSTANCEOF['Q5'] or INSTANCEOF['Q15632617'] if ishuman and not group.humans_allowed then return false end local allowlist = group.P31_allowed_values if not allowlist then return true end for _, class in ipairs( allowlist ) do if INSTANCEOF[class] then return true end end return false end local function noImage() -- Wikidata classes that don't need an image local dontNeedImg = { 'Q4167410', -- disambiguation page 'Q4167836', -- Wikimedia category 'Q11266439', -- Wikimedia template 'Q101352', -- family name 'Q202444', -- given name 'Q12308941', -- male given name 'Q11879590', -- female given name 'Q3409032', -- unisex given name } for _, class in ipairs( dontNeedImg ) do if INSTANCEOF[class] then return end end local hasImg for _, imgPid in ipairs( property_groups[1].pids ) do if CLAIMS[imgPid] then hasImg = true break end end if not hasImg then return '[[Category:Uses of Wikidata Infobox with no image]]' end end --- Returns string with all labels/descs/aliases for search engine optimization local function seo() local out = {} for lang, v in pairs( ITEM.labels or {} ) do out[#out+1] = v.value end for lang, v in pairs( ITEM.descriptions or {} ) do out[#out+1] = v.value end for lang, v in pairs( ITEM.aliases or {} ) do for _, w in ipairs( v ) do out[#out+1] = w.value end end return table.concat( out, '; ' ) end -- wikiprojects that are not Wikipedia despite their IDs ending with 'wiki' local excludedProjects = { wikidatawiki = true, commonswiki = true, specieswiki = true, metawiki = true, mediawikiwiki = true, sourceswiki = true, wikimaniawiki = true, incubatorwiki = true, } -- Returns interwiki link if site is Wikipedia local function interwikilink( site, title ) if site:sub(-4) == 'wiki' and not excludedProjects[site] then local iwprefix = site:sub(1, -5):gsub('_', '-') -- "zh_yuewiki" to "zh-yue" return string.format( '[[%s:%s]]', iwprefix, title ) end end --- Adds Wikipedia sitelinks from similar items. Example at Cat:Moore_(surname) local function interwikis() local out = {} -- ITEM is usually P301 of connected item, so this is not redundant: for site, v in pairs( ITEM.sitelinks or {} ) do out[#out+1] = interwikilink( site, v.title ) end for _, pid in ipairs{ 'P2354', 'P1753', 'P1753'} do -- has list, related list, said to be same as for similar in iclaims( ITEM:getBestStatements(pid) ) do for site, v in pairs( mw.wikibase.getEntity(similar.id).sitelinks or {} ) do out[#out+1] = interwikilink( site, v.title ) end end end return table.concat( out ) end local charMap -- memoized local function stripDiacritics( str ) if not charMap then local from = 'ÁÀÂÄǍĂĀÃÅẠĄƏĆĊĈČÇĎĐḐḌÐÉÈĖÊËĚĔƐƎỀỂỄẾỆĒẼĘẸĠĜĞĢĤĦḤİÍÌÎÏǏĬĪĨĮỊĴĶĹĿĽĻŁḶḸṂŃŇÑŅṆŊÓÒÔÖǑŎŌÕǪỌŐØꝚŔŘŖⱤɌƦȐȒṘṚṜŚŜŠŞȘṢŤŢȚṬÚÙÛÜǓŬŪŨŮŲỤŰǗǛǙǕŴÝŶŸỸȲŹŻŽ'.. 'ằắắáẳàẵâäǎăāãåặầẩẫấậảạąəćċĉčçḑďđḍðéèėêëěɛǝềểễếệĕēẽęẹġĝğģḩĥħḥıíìîïǐĭīĩįịĵķĺŀľļłḷḹṃńňñņṇŋơóồòôöǒŏōõǫọőøꝛŕɽřŗṛṝɍʀȑȓṙśŝšşșṣťţțṭưúùûứừüǔŭūũůųụűǘǜǚǖŵýŷÿỹȳźżž' local to = 'AAAAAAAAAAAACCCCCDDDDDEEEEEEEEEEEEEEEEEEGGGGHHHIIIIIIIIIIIJKLLLLLLLMNNNNNNOOOOOOOOOOOORRRRRRRRRRRRSSSSSSTTTTUUUUUUUUUUUUUUUUWYYYYYZZZ'.. 'aaaaaaaaaaaaaaaaaaaaaaaacccccdddddeeeeeeeeeeeeeeeeeegggghhhhiiiiiiiiiiijklllllllmnnnnnnoooooooooooooorrrrrrrrrrrrssssssttttuuuuuuuuuuuuuuuuuuuwyyyyyzzz' charMap = {} for i = 1, mw.ustring.len( from ) do charMap[mw.ustring.sub(from, i, i)] = mw.ustring.sub(to, i, i) end charMap['ß'] = 'ss'; charMap['ẞ'] = 'SS' charMap['æ'] = 'ae'; charMap['ǣ'] = 'ae'; charMap['ǽ'] = 'ae' charMap['Æ'] = 'AE'; charMap['Ǣ'] = 'AE'; charMap['Ǽ'] = 'AE' charMap['œ'] = 'oe'; charMap['Œ'] = 'OE' charMap['þ'] = 'th'; charMap['Þ'] = 'Th' end return (string.gsub( str, '[^\128-\191][\128-\191]*', charMap )) end local function humannames( out ) local surname = ITEM:formatPropertyValues('P734').value:gsub(',.*', '') local givennames = ITEM:formatPropertyValues('P735').value:gsub(', ', ' ') local spanish2nd = ITEM:formatPropertyValues('P1950').value:gsub(',.*', '') if config.trackingcats then if surname == '' then out[#out+1] = '[[Category:Uses of Wikidata Infobox with no family name]]' end if givennames == '' then out[#out+1] = '[[Category:Uses of Wikidata Infobox with no given name]]' end end if config.autocat then for _, pid in ipairs{ 'P734', 'P1950', 'P9139' } do for name in iclaims( ITEM:getBestStatements(pid) ) do name = mw.wikibase.getLabelByLang( name.id, 'en' ) if givennames == '' then out[#out+1] = name and string.format('[[Category:%s (surname)]]', name) else out[#out+1] = name and string.format('[[Category:%s (surname)|%s]]', name, stripDiacritics(givennames)) end end end for name in iclaims( ITEM:getBestStatements('P735') ) do name = mw.wikibase.getLabelByLang( name.id, 'en' ) out[#out+1] = name and string.format('[[Category:%s (given name)]]', name) -- no sort key needed because DEFAULTSORT starts with family name end end if not config.defaultsort then out[#out+1] = '[[Category:Uses of Wikidata Infobox with defaultsort suppressed]]' elseif surname ~= '' and surname ~= 'no value' and surname ~= 'some value' then if spanish2nd ~= '' then surname = surname .. ' ' .. spanish2nd end local sortkey = stripDiacritics( surname..', '..givennames ) out[#out+1] = frame:preprocess('{{DEFAULTSORT:'..sortkey..'}}') end end --- @param pid "P569"|"P570" --- @param event "birth"|"death" local function datecat( pid, event, out ) local year = WikidataIB._getValue{ pid, qid=QID, ps=1, df='y', plaindate='adj', lang='en', maxvals=1 } if year and year ~= 'unknown value' then local cat = 'Category:' .. year .. ' ' .. event .. 's' if mw.title.new( cat ).exists then out[#out+1] = '[['..cat..']]' elseif config.trackingcats then mw.addWarning( 'Categorization under [[:'..cat..']] supressed' ) out[#out+1] = '[[Category:Uses of Wikidata Infobox with unknown '..event..' category|'..year..']]' end end end local function countrycat( out ) local exceptions = { Q30 = 'United States', } for country in iclaims( ITEM:getBestStatements('P27') ) do local countryLabel = exceptions[country.id] or mw.wikibase.getLabelByLang( country.id, 'en' ) if countryLabel then local sex = getSingleValue( ITEM, 'P21' ) local sexLabel = sex and ({ Q6581097 = 'Men', Q2449503 = 'Men', Q6581072 = 'Women', Q1052281 = 'Women', })[sex.id] if sexLabel then local cat1 = 'Category:'..sexLabel..' of the '..countryLabel..' by name' local cat2 = 'Category:'..sexLabel..' of '..countryLabel..' by name' if mw.title.new( cat1 ).exists then out[#out+1] = '[['..cat1..']]' elseif mw.title.new( cat2 ).exists then out[#out+1] = '[['..cat2..']]' elseif config.trackingcats then mw.addWarning( 'Categorization under [[:'..cat2..']] supressed' ) out[#out+1] = '[[Category:Uses of Wikidata Infobox with unknown country category|'..countryLabel..']]' end end end end end local function autocat( out, pid, dict ) for _, claim in ipairs( ITEM:getAllStatements(pid) ) do if claim.rank ~= "deprecated" then local dv = claim.mainsnak.datavalue local cat = dict[dv and dv.value.id] out[#out+1] = cat and '[[Category:'..cat..']]' end end end local function metadata() local out = {} if config.trackingcats then out[#out+1] = noImage() if not (CLAIMS['P31'] or CLAIMS['P279']) then out[#out+1] = '[[Category:Uses of Wikidata Infobox with no instance of]]' end if INSTANCEOF['Q5'] and not CLAIMS['P569'] then out[#out+1] = '[[Category:Uses of Wikidata Infobox with no year of birth]]' end end out[#out+1] = '<div style="display:none">'..seo()..'</div>' -- Add interwiki links from related items, inspired by Module:Interwiki if config.interwiki and mw.title.getCurrentTitle().namespace == 14 then out[#out+1] = interwikis() end if config.autocat then for pid, cat in pairs( autocats_by_id ) do local val = getSingleValue( ITEM, pid ) out[#out+1] = val and string.format( '[[Category:%s|%s]]', cat, val ) end out[#out+1] = CLAIMS['P757'] and '[[Category:World Heritage Sites by name]]' autocat( out, 'P1435', { -- heritage designation Q34932610 = 'Conjuntos de Interesse Municipal in Portugal by name', Q28419115 = 'Conjuntos de Interesse Público in Portugal by name', Q54171320 = 'Monuments under study in Portugal by name', Q15697324 = 'Imóveis de Interesse Público in Portugal by name', Q11791 = 'Imóveis de Interesse Municipal in Portugal by name', Q53806418 = 'Monuments included in classified sites in Portugal by name', Q28423275 = 'Monumentos de Interesse Municipal in Portugal by name', Q22222923 = 'Monumentos de Interesse Público in Portugal by name', Q908411 = 'Monumentos Nacionais in Portugal by name', Q28419400 = 'Sítios de Interesse Municipal in Portugal by name', Q28419109 = 'Sítios de Interesse Público in Portugal by name', Q54163210 = 'Pending classification monuments in Portugal by name', }) autocat( out, 'P31', { -- instance of Q235670 = 'Common years starting and ending on Sunday', Q235673 = 'Common years starting and ending on Saturday', Q235676 = 'Common years starting and ending on Wednesday', Q235680 = 'Common years starting and ending on Friday', Q235684 = 'Common years starting and ending on Tuesday', Q235687 = 'Common years starting and ending on Monday', Q235690 = 'Common years starting and ending on Thursday', Q217041 = 'Leap years starting on Sunday and ending on Monday', Q217026 = 'Leap years starting on Saturday and ending on Sunday', Q217015 = 'Leap years starting on Wednesday and ending on Thursday', Q217036 = 'Leap years starting on Friday and ending on Saturday', Q217034 = 'Leap years starting on Tuesday and ending on Wednesday', Q217024 = 'Leap years starting on Monday and ending on Tuesday', Q217019 = 'Leap years starting on Thursday and ending on Friday', Q66010119 = 'Months starting on Monday', Q66010126 = 'Months starting on Tuesday', Q66010132 = 'Months starting on Wednesday', Q66010139 = 'Months starting on Thursday', Q66010148 = 'Months starting on Friday', Q66010153 = 'Months starting on Saturday', Q66010158 = 'Months starting on Sunday', Q3305213 = 'Individual painting categories', }) if INSTANCEOF['Q5'] and mw.title.getCurrentTitle().namespace == 14 then humannames( out ) datecat( 'P569', 'birth', out ) datecat( 'P570', 'death', out ) countrycat( out ) autocat( out, 'P21', { -- sex or gender Q6581097 = 'Men by name', Q6581072 = 'Women by name', Q1052281 = 'LGBT people by name]][[Category:Women by name', Q2449503 = 'LGBT people by name]][[Category:Men by name', Q48270 = 'Non-binary people by name', Q12964198 = 'LGBT people by name', -- genderqueer Q1097630 = 'LGBT people by name', -- intersex Q18116794 = 'LGBT people by name', -- genderfluid Q505371 = 'LGBT people by name', -- agender }) out[#out+1] = '[[Category:People by name]]' out[#out+1] = CLAIMS['P570'] and '[[Category:Deceased people by name]]' out[#out+1] = WikidataIB.getAwardCat{ args = {qid=QID, fwd='ALL', osd=config.osd, noicon='yes'} } if not CLAIMS['P570'] then -- This person has no death date, but are they really alive? local birth = getSingleValue( ITEM, 'P569' ) local year = tonumber( birth and birth.time:gsub('-.*', '') ) if year and os.date('%Y') - year < 100 then out[#out+1] = '[[Category:Living people]]' end end end end return table.concat( out ) end --- @return string|nil local function getImage( pid ) local claim = ITEM:getBestStatements( pid )[1] local ms = claim and claim.mainsnak local file = ms and ms.datavalue and ms.datavalue.value if file then local panoramalink = (pid == 'P4640') and '|link=https://panoviewer.toolforge.org/#'..mw.uri.encode(file, 'WIKI') or '' local img = '<span class="wpImageAnnotatorControl wpImageAnnotatorCaptionOff">[[File:'..file..'|'..config.imagesize..panoramalink..']]</span>' -- equivalent to {{ImageNoteControl | caption=off | type=inline}} local medialegends = claim.qualifiers and claim.qualifiers['P2096'] if medialegends then return img .. '<div>'..extractMonolingualText( medialegends )..'</div>' else return img -- no image caption end end end --- Returns images and sitelinks --- @param uploadlink? boolean: Whether to show the "Upload media" link local function header( uploadlink ) local imgs = {} for _, imgPid in ipairs( property_groups[1].pids ) do local formatted_img = getImage(imgPid) imgs[#imgs+1] = formatted_img and { imgPid, formatted_img } end local switcherContainer = mw.html.create( 'div' ) switcherContainer:addClass( 'switcher-container' ) -- Only show switching labels if we have more than one image to show if #imgs > 1 then for _, img in ipairs( imgs ) do switcherContainer:tag( 'div' ) :addClass( 'center' ) :node( img[2] ) :tag( 'span' ) :attr{ class = "switcher-label", style = "display:none" } :node( ' ' .. getLabel(img[1]) .. ' ' ) end elseif #imgs == 1 then switcherContainer:tag( 'div' ) :addClass( 'center' ) :node( imgs[1][2] ) end local images = mw.html.create( 'tr' ) images:tag( 'td' ) :attr{ colspan=2, class="wdinfo_nomobile" } :css( 'text-align', 'center' ) :tag( 'div' ) :node( ITEM:getDescription() or '') :done() :node( switcherContainer ) local out = {} if INSTANCEOF['Q4167410'] or INSTANCEOF['Q15407973'] then -- disambiguation page/category if config.trackingcats then out[1] = '[[Category:Uses of Wikidata Infobox for disambig pages]]' end elseif uploadlink then local url = tostring(mw.uri.fullUrl('Special:UploadWizard', { categories = mw.title.getCurrentTitle().text })) local text = mw.message.new('Cx-contributions-upload'):inLanguage(MYLANG):plain() out[1] = '<tr><td colspan=2 style="text-align:center"><b>['..url..' '..text..']</b></td></tr>' end local sitelinks = ITEM.sitelinks if config.sitelinks and sitelinks then out[#out+1] = '<tr><td colspan=2 style="text-align:center; font-weight:bold">' local langId = databaseId(MYLANG) local langprefix = langId:gsub('_', '-') local wikis = { -- wikiId, prefix logo, qid, multilang { 'wiki', '', 'Wikipedia-logo-v2', 'Q52', false }, { 'wikiquote', 'q', 'Wikiquote-logo', 'Q369', false }, { 'wikisource', 's', 'Wikisource-logo', 'Q263', false }, { 'wikibooks', 'b', 'Wikibooks-logo', 'Q367', false }, { 'wikinews', 'n', 'Wikinews-logo', 'Q964', false }, { 'wikiversity', 'v', 'Wikiversity-logo', 'Q370', false }, { 'specieswiki', 'species', 'Wikispecies-logo', 'Q13679', true }, { 'wikivoyage', 'voy', 'Wikivoyage-logo', 'Q373', false }, } for _, v in ipairs( wikis ) do local wikiId, prefix, logo, qid, multilang = unpack( v ) logo = '[[File:'..logo..'.svg|16x16px|alt=|link=]] ' if multilang then local sitelink = sitelinks[wikiId] if sitelink then out[#out+1] = '<div>'..logo..'[['..prefix..':'..sitelink.title..'|'..getLabel(qid)..']]</div>' end else local sitelink = sitelinks[langId .. wikiId] if sitelink then out[#out+1] = '<div>'..logo..'[['..prefix..':'..langprefix..':'..sitelink.title..'|'..getLabel(qid)..']]</div>' end end end out[#out+1] = '</td></tr>' end return tostring( images ) .. table.concat( out ) end --- Returns "Edit at Wikidata" pencil local function pencil() local msg, lang = i18n( 'editlink-alttext', FALLBACKLANGS ) local out = mw.html.create( 'tr' ) out :addClass( "wdinfo_nomobile" ) :tag( 'td' ) :css( 'text-align', 'right' ) :attr{ lang = lang, colspan = 2 } :node( string.format('[[File:Blue pencil.svg|15px|link=d:%s|%s]]', QID, msg) ) return tostring( out ) end --- Evaluates all non-image property groups and adds generated HTML rows to --- the table given as argument. local function getBodyContent( t ) for i, group in ipairs( property_groups ) do if i > 1 and groupIsAllowed( group ) then for _, pid in ipairs( group.pids ) do if CLAIMS[pid] or group.bypass_property_exists_check then local x = property_logic[pid] or group.logic or defaultFunc if type(x) == 'function' then t[#t+1] = x( pid ) else -- type(x) == 'table' t[#t+1] = defaultFunc( pid, x ) end end end end end end --- Returns the infobox's main content local function body() if not CLAIMS then return '' end local out = {} getBodyContent( out ) -- If category combines at most 2 topics, show subinfoboxes for those topics. -- See Category:Uses_of_Wikidata_Infobox_with_subinfoboxes local topics = ITEM:getBestStatements( 'P971' ) if not topics or #topics > 2 then return table.concat( out ) end -- country (Q6256), continent (Q5107), sovereign state (Q3624078), ocean (Q9430) local geoEntities = { 'Q6256', 'Q5107', 'Q3624078', 'Q9430' } -- The loop below modifies these variables and restores them afterwards local qid, item, claims, istaxon, instanceof = QID, ITEM, CLAIMS, ISTAXON, INSTANCEOF local map for _, claim in ipairs( topics ) do QID = claim.mainsnak.datavalue.value.id ITEM = mw.wikibase.getEntity( QID ) if not ITEM then out[#out+1] = '[[Category:Uses of Wikidata Infobox for deleted Wikidata items]]' break end CLAIMS = ITEM.claims or {} ISTAXON = CLAIMS['P105'] or CLAIMS['P171'] or CLAIMS['P225'] or CLAIMS['P1843'] INSTANCEOF = {} for class in iclaims( ITEM:getBestStatements('P31') ) do INSTANCEOF[class.id] = true end local skip for _, geoEnt in ipairs( geoEntities ) do if INSTANCEOF[geoEnt] then skip = true map = getCoordinates( 'P625' ) break end end -- Skip if topic is a calendar year (Q3186692) or decade (Q39911) skip = skip or INSTANCEOF['Q3186692'] or INSTANCEOF['Q39911'] if not skip and #getBestStatements(QID, 'P279') == 0 then -- subclass of if config.trackingcats then out[#out+1] = '[[Category:Uses of Wikidata Infobox with subinfoboxes]]' end out[#out+1] = '<tr><th colspan=2>'..(ITEM:getLabel() or QID)..'</th></tr>' out[#out+1] = header( false ) getBodyContent( out ) out[#out+1] = pencil() end end out[#out+1] = map QID, ITEM, CLAIMS, ISTAXON, INSTANCEOF = qid, item, claims, istaxon, instanceof return table.concat( out ) end local function authoritycontrol() if not config.authoritycontrol then return '' end local ids = {} for _, group in ipairs( externalIDs ) do for _, pid in ipairs( group.pids ) do if CLAIMS[pid] then local icon = getSingleValue( pid, 'P2910' ) icon = icon and '[[File:'..icon..'|18px|alt=|link=]] ' or '' local fmtSt = ITEM:formatStatements( pid ) if fmtSt.value ~= '' then ids[#ids+1] = icon .. fmtSt.label .. ': ' .. fmtSt.value end end end end local wdlogo = '[[File:Wikidata-logo.svg|20px|alt='..getLabel('Q2013')..'|link=d:'..QID..']]' return table.concat{ '<tr><th style="background: #cfe3ff">', LANG:ucfirst( getLabel('Q36524') ), '</th></tr>', '<tr><td style="text-align: center;">', '<div style="overflow-wrap: break-word; font-size: smaller">', wdlogo..' [[d:'..QID..'|'..QID..']]<br>', '<span class="wdinfo_nomobile">', table.concat(ids, '<br>'), '</span>', '</div>', '</td></tr>', } end local function helperlinks() if not config.helperlinks then return '' end local hl = {} local title = mw.title.getCurrentTitle() local pagename = title.text local pagenamee = mw.uri.encode(pagename, 'WIKI') local coords = getSingleValue( ITEM, 'P625' ) local otherplanet = coords and coords.globe ~= 'http://www.wikidata.org/entity/Q2' hl[#hl+1] = '[https://reasonator.toolforge.org/?q='..QID..' '..getLabel('Q20155952')..']' hl[#hl+1] = '[https://scholia.toolforge.org/'..QID..' '..getLabel('Q45340488')..']' if title.namespace == 14 then hl[#hl+1] = '[https://petscan.wmflabs.org/?language=en&categories='..pagenamee..'&project=wikimedia&ns%5B6%5D=1 '..getLabel('Q23665536')..']' hl[#hl+1] = '[https://glamtools.toolforge.org/glamorgan.html?&category='..pagenamee..'&depth=1&month=last '..getLabel('Q12483')..']' if not otherplanet then hl[#hl+1] = '[https://wikimap.toolforge.org/?cat='..pagenamee..'&subcats=true&subcatdepth=1&cluster=true '..getLabel('Q99232292')..']' hl[#hl+1] = '[https://locator-tool.toolforge.org/#/geolocate?category='..pagenamee..' '..getLabel('Q66498380')..']' end end hl[#hl+1] = '[https://iw.toolforge.org/kmlexport?project=enwiki&article='..mw.uri.encode(title.prefixedText)..' '..getLabel('P3096')..']' if coords and not otherplanet then hl[#hl+1] = '[https://wikishootme.toolforge.org/#q='..QID..' '..getLabel('Q26964791')..']' hl[#hl+1] = '[https://overpass-api.de/api/interpreter?data='..mw.uri.encode('[out:custom];node[wikidata='..QID..'];if(count(nodes)==0){way[wikidata='..QID..'];};if(count(ways)==0){rel[wikidata='..QID..'];};out 1;', 'PATH')..' '..getLabel('Q936')..']' end for i, v in ipairs( hl ) do hl[i] = '<span style="white-space:nowrap">' .. v .. '</span>' end hl[#hl+1] = '[[Special:Search/haswbstatement:P180='..QID..'|'..i18n('search-depicted', FALLBACKLANGS)..']]' hl[#hl+1] = ISTAXON and '[https://commons-query.wikimedia.org/#%23defaultView%3AImageGrid%0ASELECT%20%3Ffile%20%3Fimage%0AWITH%20%7B%0A%20%20SELECT%20%3Fitem%20WHERE%20%7B%0A%20%20%20%20SERVICE%20%3Chttps%3A%2F%2Fquery.wikidata.org%2Fsparql%3E%20%7B%0A%20%20%20%20%20%20%20%20%3Fitem%20wdt%3AP171%2Fwdt%3AP171%2a%20wd%3A'..QID..'.%0A%20%20%20%20%7D%20%0A%20%20%7D%0A%7D%20AS%20%25get_items%0AWHERE%20%7B%0A%20%20INCLUDE%20%25get_items%0A%20%20%3Ffile%20wdt%3AP180%20%3Fitem%20.%0A%20%20%3Ffile%20schema%3AcontentUrl%20%3Furl%20.%0A%20%20BIND%28IRI%28CONCAT%28%22http%3A%2F%2Fcommons.wikimedia.org%2Fwiki%2FSpecial%3AFilePath%2F%22%2C%20wikibase%3AdecodeUri%28SUBSTR%28STR%28%3Furl%29%2C53%29%29%29%29%20AS%20%3Fimage%29%0A%7D '..i18n('taxon-depicted', FALLBACKLANGS)..']' return table.concat{ '<tr class="wdinfo_nomobile">', '<td colspan=2 style="text-align: center"><small>', '<div class="hlist hlist-separated"><ul>', '<li>' .. table.concat(hl, '</li><li>') .. '</li>', '</ul></div>', '</small></td>', '</tr>', } end local function footer() return (config.authoritycontrol or config.helperlinks) and table.concat{ '<tr><td colspan=2>', '<table style="width:100%" id="wdinfo_ac" class="mw-collapsible">', authoritycontrol(), helperlinks(), '</table>', '</td></tr>', } or '' end --- @param eid string: Wikidata entity ID starting with Q or P local function entityLink( eid ) local label = getLabel( eid, true ) local ns = ( eid:sub(1, 1) == 'P' ) and 'Property:' or '' return '[[d:'..ns..eid..'|'..label..' <small>('..eid..')</small>]]' end --- Generates [[Template:Wikidata Infobox/doc/properties]] function p.doc() local out = {} for _, group in ipairs( property_groups ) do out[#out+1] = '<h2>' .. group.groupname .. '</h2>' if group.comment then out[#out+1] = frame:preprocess( group.comment ) end if group.P31_allowed_values then local classes = {} for _, class in ipairs( group.P31_allowed_values ) do classes[#classes+1] = entityLink( class ) end out[#out+1] = 'This group is only shown if the connected Wikidata item is an instance of ' .. table.concat(classes, ' or ') .. '.' elseif group.humans_allowed then out[#out+1] = 'This group is always shown.' end local props = {} for _, pid in ipairs( group.pids ) do props[#props+1] = entityLink( pid ) end out[#out+1] = table.concat( props, ' • ' ) end -- authority control out[#out+1] = '<h2>'..getLabel('Q36524')..'</h2>' out[#out+1] = 'This group is always shown.' for _, group in ipairs( externalIDs ) do out[#out+1] = '<h3>' .. group.groupname .. '</h3>' local props = {} for _, pid in ipairs( group.pids ) do props[#props+1] = entityLink( pid ) end out[#out+1] = table.concat( props, ' • ' ) end return table.concat( out, '\n\n' ) end local function configure( t ) config.defaultsort = t['defaultsort'] == 'y' config.interwiki = t['interwiki'] == 'yes' config.autocat = t['autocat'] == 'yes' config.trackingcats = t['trackingcats'] == 'yes' config.uploadlink = t['conf_upload'] == 'yes' config.sitelinks = t['conf_sitelinks'] == 'yes' config.authoritycontrol = t['conf_authoritycontrol'] == 'yes' config.helperlinks = t['conf_helperlinks'] == 'yes' if t['conf_coordtemplate'] then config.coordtemplate = tonumber( t['conf_coordtemplate'] ) end if t['conf_mapwidth'] then config.mapwidth = t['conf_mapwidth'] end if t['conf_mapheight'] then config.mapheight = t['conf_mapheight'] end if t['conf_imagesize'] then config.imagesize = t['conf_imagesize'] end if t['spf'] then config.spf = t['spf'] end if t['fwd'] then config.fwd = t['fwd'] end if t['osd'] then config.osd = t['osd'] end if t['noicon'] then config.noicon = t['noicon'] end end function p.main( frame ) MYLANG = frame:callParserFunction( 'int', 'lang' ) or "en" LANG = mw.language.new( MYLANG ) FALLBACKLANGS = { MYLANG, unpack(mw.language.getFallbacksFor(MYLANG)) } QID = frame.args[1] ITEM = mw.wikibase.getEntity( QID ) if not ITEM then return '[[Category:Uses of Wikidata Infobox for deleted Wikidata items]]' end CLAIMS = ITEM.claims if not CLAIMS then local msg = string.format( i18n('noclaims', FALLBACKLANGS), '[[d:'..QID..'|'..QID..']]' ) return '[[Category:Uses of Wikidata Infobox with no claims]]<table id="wdinfobox" class="fileinfotpl-type-information vevent infobox mw-content-'..LANG:getDir()..'"><tr><td><strong class="error">'..msg..'</td></strong></tr>' end -- identifying a taxon by checking whether it has a taxon property is faster than checking whether its P31 value is a subclass of taxon ISTAXON = CLAIMS['P105'] or CLAIMS['P171'] or CLAIMS['P225'] or CLAIMS['P1843'] local parentframe = frame:getParent() if parentframe then configure( parentframe.args ) end for class in iclaims( ITEM:getBestStatements('P31') ) do INSTANCEOF[class.id] = true end local out = { metadata(), '<table id="wdinfobox" class="fileinfotpl-type-information vevent infobox mw-collapsible mw-content-'..LANG:getDir()..'">', '<caption class="fn org" id="wdinfoboxcaption">', '<b>' .. (ITEM:getLabel() or QID) .. ' </b>', '</caption>', header( config.uploadlink ), body(), footer(), pencil(), '</table>', } if config.trackingcats and os.clock() > 3.0 then -- longer than 3 seconds out[#out+1] = '[[Category:Uses of Wikidata Infobox with bad performance]]' end return table.concat( out ) end function p.debug( qid ) frame.args = { qid or 'Q42' } return p.main( frame ) end return p -- Credits: -- Original authors: Mike Peel with contributions by Jura1 -- 2022 rewrite: LennardHofmann
Summary:
Please note that all contributions to Stockhub may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
Stockhub:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Templates used on this page:
Template:Lua
(
edit
)
Template:Tl
(
edit
)
Module:Wikidata Infobox/doc
(
edit
)