Module:External links
MyWikiBiz, Author Your Legacy — Friday November 15, 2024
Jump to navigationJump to searchThis module is designed for implementing templates to display external links in an English Wikipedia article, using the properties defined in that article's Wikidata item. It was based on the Norwegian Wikipedia module no:Modul:External links.
The first test implementation is Template:Sports links (based on no:Mal:Sportslenker). The sports-related link properties and formats are specified in Module:External links/conf/Sports (based on no:Modul:External links/conf/Sport).
Other subject-based implementations at the Norwegian Wikipedia include:
- Arts (no:Mal:Artslenker and no:Modul:External links/conf/Arter)
- Astronomy (no:Mal:Astronomilenker and no:Modul:External links/conf/Astronomi)
- Authority data (no:Mal:Autoritetsdata and no:Modul:External links/conf/Autoritetsdata)
- Film (no:Mal:Filmlenker and no:Modul:External links/conf/Film)
- Film person (no:Mal:Filmperson and no:Modul:External links/conf/Filmperson)
- Music (no:Mal:Musikklenker and no:Modul:External links/conf/Musikk)
- Official links (no:Mal:Offisielle lenker and no:Modul:External links/conf/Offisielle lenker)
- Games (no:Mal:Spill-lenker and no:Modul:External links/conf/Spill)
Usage
{{#invoke:External links|function_name}}
Submodules
require('Module:No globals') -- local genitive = require('Module:Genitive')._genitive local contLang = mw.language.getContentLanguage() local cmodule = {} local conf = require 'Module:External links/conf'(contLang:getCode()) local hasdatafromwikidata = false local hasdatafromlocal = false local haswikidatalink = true -- we assume it's connected local p = {} local function getLabel(entity, use_genitive, pagetitle) local label = (pagetitle and pagetitle ~= '') and pagetitle or nil if not label and not entity then label = mw.title.getCurrentTitle().text elseif not label then label = mw.wikibase.label(entity.id) or mw.title.getCurrentTitle().text end -- return use_genitive and genitive(label, 'sitt') or label return use_genitive and label .. "'s" or label end -- @todo cleanup, this is in production, use the console local function dump(obj) return "<pre>" .. mw.dumpObject(obj) .. "</pre>" end local function stringFormatter( datavalue ) if datavalue == nil or datavalue['type'] ~= 'string' then return nil end return datavalue.value end local pval = {} pval.P1793 = { -- format as a regular expression types = { snaktype = 'value', datatype = 'string', }, } pval.P407 = { -- language of work or name types = { snaktype = 'value', datatype = 'wikibase-item', datavalue = { type = 'wikibase-entityid', } }, } pval.P364 = { -- original language of work types = { snaktype = 'value', datatype = 'wikibase-item', datavalue = { type = 'wikibase-entityid', } }, } pval.P218 = { -- ISO 639-1 language types = { snaktype = 'value', datatype = 'external-id', datavalue = { type = 'string', } }, } pval.P305 = { -- IETF language tag types = { snaktype = 'value', datatype = 'external-id', datavalue = { type = 'string', } }, } pval.P582 = { -- end time types = { snaktype = 'value', datatype = 'time', datavalue = { type = 'string', } }, } -- This is a really makeshift crappy converter, but it'll do some basic -- conversion from PCRE to Lua-style patterns (note that this only work -- in very few cases) local function regexConverter( regex ) local output = regex output = string.gsub(output, "\\d{2}", "%%d%%d") output = string.gsub(output, "\\d{3}", "%%d%%d%%d") output = string.gsub(output, "\\d{4}", "%%d%%d%%d%%d") output = string.gsub(output, "\\d{5}", "%%d%%d%%d%%d%%d") output = string.gsub(output, "\\d{6}", "%%d%%d%%d%%d%%d%%d") output = string.gsub(output, "\\d{7}", "%%d%%d%%d%%d%%d%%d%%d") output = string.gsub(output, "\\d{8}", "%%d%%d%%d%%d%%d%%d%%d%%d") output = string.gsub(output, "\\d", "%%d") return output end local function getFormatterUrl( prop, value ) local head = "" local tail = "" local entity = mw.wikibase.getEntity(prop) -- to avoid deep tests if not entity or not entity.claims then return head end -- get the claims for this entity local statements = entity.claims['P1630'] -- formatter URL -- to avoid deep tests if not statements then return head end local formatters = {} -- let's go through the claims for _, claim in ipairs( statements ) do -- to avoid deep tests if not claim then claim = {} end local valid = claim['type'] == 'statement' and claim['rank'] ~= 'deprecated' if valid then local mainsnak = claim.mainsnak or {} local preferred = claim['rank'] == 'preferred' -- get any qualifiers for this claim (we are interested in P1793 for -- indication of which claim is correct) local qualifiers = claim.qualifiers or {} -- now let's check the qualifier we are interested in local qualid = 'P1793' -- format as a regular expression -- if the claim has this qualifier if qualifiers[qualid] then -- it's here, let's check it out! local items = {} -- traverse all snaks in this qualifier for _, qualsnak in ipairs( qualifiers[qualid] ) do if qualsnak and pval[qualid] then --mw.log("qualsnak = " .. dump(qualsnak)) -- check if the snak is of the correct snaktype and datatype local valid = qualsnak.snaktype == pval[qualid].types.snaktype and qualsnak.datatype == pval[qualid].types.datatype if valid then -- we'll have to convert the regex to Lua-style local regex = regexConverter(qualsnak.datavalue.value) local test = string.match( value, '^'..regex..'$' ) if test then -- it matched, this is correct and overrides any other. if preferred then head = mainsnak.datavalue.value else tail = mainsnak.datavalue.value end end end end end else -- we don't have any qualifier, is it preferred? if (head == '' and preferred) or (tail == '' and not preferred) then -- if we don't have any other, use this one if preferred and head == '' then head = mainsnak.datavalue.value elseif not preferred and tail == '' then tail = mainsnak.datavalue.value end end end end end return head ~= '' and head or tail end local function getLanguageData(prop, qid) local head = {} local tail = {} -- mw.log("getLanguageData, prop="..dump(prop).." qid="..dump(qid)) -- get the entity we are checking local entity = mw.wikibase.getEntityObject(qid) -- to avoid deep tests if not entity then return nil end if not entity.claims then return {} end -- get the claims for this entity local statements = entity.claims[prop] -- to avoid deep tests if not statements then return {} end -- mw.log("getLanguageData going through claims="..dump(statements)) -- let's go through the claims for _, claim in ipairs( statements ) do -- to avoid deep tests if not claim then claim = {} end local valid = claim['type'] == 'statement' and claim['rank'] ~= 'deprecated' if valid then local mainsnak = claim.mainsnak or {} local preferred = claim['rank'] == 'preferred' -- verify the item is what we expect local valid = mainsnak.snaktype == pval[prop].types.snaktype and mainsnak.datatype == pval[prop].types.datatype and mainsnak.datavalue.type == pval[prop].types.datavalue.type if valid then -- mw.log("getLanguageData claim is valid="..dump(claim)) -- if this is the correct P-value, dive into it and get P218 (ISO 639-1) if mainsnak.property == 'P364' then -- original language of work if preferred then head[#head+1] = table.concat(getLanguageData('P218', 'Q'..mainsnak.datavalue.value['numeric-id']), conf:a('mod-filter-separator')) else tail[#tail+1] = table.concat(getLanguageData('P218', 'Q'..mainsnak.datavalue.value['numeric-id']), conf:a('mod-filter-separator')) end elseif mainsnak.property == 'P218' or mainsnak.property == 'P305' then -- ISO 639-1 code or IETF language tag if preferred then head[#head+1] = stringFormatter(mainsnak.datavalue) else tail[#tail+1] = stringFormatter(mainsnak.datavalue) end end end end end -- mw.log("getLanguageData returning head="..dump(head).." tail="..dump(tail)) return #head>0 and head or tail end local langqvalorder = {'P407','P364'} local otherqvalorder = {'P582'} local function getValuesFromWikidata(props) local head = {} local tail = {} -- mw.log("getValuesFromWikidata, props="..dump(props)) -- get the entity we are checking local entity = mw.wikibase.getEntityObject() -- to avoid deep tests if not entity then --mw.log("getValuesFromWikidata no entity") return nil end if not entity.claims or not props or not props.prop or props.prop == '' then --mw.log("getValuesFromWikidata no claims or no props") return {} end -- get the claims for this entity local statements = entity.claims[props.prop] -- to avoid deep tests if not statements then return {} end -- let's go through the claims for _, claim in ipairs( statements ) do -- to avoid deep tests if not claim then claim = {} end local valid = claim['type'] == 'statement' and claim['rank'] ~= 'deprecated' if valid then -- mw.log("getValuesFromWikidata valid claim="..dump(claim)) local mainsnak = claim.mainsnak or {} local preferred = claim['rank'] == 'preferred' -- get the content of the claim (the identifier) local langcode = props.langcode local checklangcode = nil if props.langcode and props.langcode ~= '' then checklangcode = string.find(langcode, "([pP]%d+)") end if checklangcode and checklangcode ~= "" then -- this is a P-value for language-code, so we'll check qualifiers for languagedata -- first get any qualifiers local qualifiers = claim.qualifiers or {} for _, qualid in ipairs( langqvalorder ) do -- if the claim has this qualifier if qualifiers[qualid] then -- it's here, let's check it out! local items = {} -- traverse all snaks in this qualifier for _, qualsnak in ipairs( qualifiers[qualid] ) do if qualsnak and pval[qualid] then -- mw.log("qualsnak = " .. dump(qualsnak)) -- check if the snak is of the correct snaktype and datatype local valid = qualsnak.snaktype == pval[qualid].types.snaktype and qualsnak.datatype == pval[qualid].types.datatype if valid then -- now get the actual data langcode = table.concat(getLanguageData('P305', 'Q'..qualsnak.datavalue.value['numeric-id']), '') end end end end -- mw.log("langcode is now="..dump(langcode)) end if string.find(langcode, "([pP]%d+)") then -- we still don't have any langcode, so we default to "en" langcode = nil end end local stillvalid = true -- we should check a couple of other qualifiers as well -- first get any qualifiers local qualifiers = claim.qualifiers or {} for _, qualid in ipairs( otherqvalorder ) do -- if the claim has this qualifier if qualifiers[qualid] then -- it's here, let's check it out! local items = {} -- traverse all snaks in this qualifier for _, qualsnak in ipairs( qualifiers[qualid] ) do if qualsnak and pval[qualid] then -- mw.log("qualsnak = " .. dump(qualsnak)) -- check if the snak is of the correct snaktype and datatype local valid = qualsnak.snaktype == pval[qualid].types.snaktype and qualsnak.datatype == pval[qualid].types.datatype if not valid then -- sorry, this is not correct mw.log("qualsnak = INCORRECT") stillvalid = false end end end end -- mw.log("langcode is now="..dump(langcode)) end if stillvalid then if preferred then head[#head+1] = { value=stringFormatter(mainsnak.datavalue) } if langcode and langcode ~= '' then head[#head]['langcode'] = langcode end else tail[#tail+1] = { value=stringFormatter(mainsnak.datavalue) } if langcode and langcode ~= '' then tail[#tail]['langcode'] = langcode end end end end end -- mw.log("getValuesFromWikidata returning head="..dump(head).." tail="..dump(tail)) return #head>0 and head or tail end local function findMainLinksOnWikidata(props, pagetitle, short_links) local output = {} local pid = nil -- get the entity we are checking local entity = mw.wikibase.getEntityObject() -- to avoid deep tests if not entity then return nil end local values = getValuesFromWikidata(props) for _, value in ipairs( values ) do local verified_value = nil if props.regex then -- we have a local defined regex, so this will have to pass first -- maybe we'll have to convert the regex to Lua-style local regex = regexConverter(props.regex) local test = string.match( value.value, '^'..regex..'$' ) --mw.log("testing with "..regex.. " and test="..dump(test).." and value="..id) if test then -- it matched, this is correct and overrides any other. verified_value = value.value end else verified_value = value.value end if verified_value then local url = '' output[#output+1] = {} output[#output].langcode = value.langcode output[#output].category = {} if props.url_f then -- we have a local defined url-formatter function, use it as first priority url = props.url_f(verified_value) if props.track and not string.find(props.langcode, "([pP]%d+)") then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-local-wd'), props.prop):plain() elseif props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-wd-wd'), props.prop):plain() end elseif props.url then -- we have a local defined url-formatter string, use it as second priority url = mw.message.newRawMessage(props.url, verified_value):plain() if props.track and not string.find(props.langcode, "([pP]%d+)") then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-local-wd'), props.prop):plain() elseif props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-wd-wd'), props.prop):plain() end else -- get the formatvalue from the property, if it exists local formatterUrl = getFormatterUrl(props.prop, verified_value) if formatterUrl ~= '' then url = mw.message.newRawMessage(formatterUrl, verified_value):plain() if props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-wd-wd'), props.prop):plain() end end end if url ~= '' then local this_wiki = mw.getContentLanguage() local this_wiki_code = this_wiki:getCode() local langlink = (value.langcode and value.langcode ~= '' and value.langcode ~= this_wiki_code) and mw.message.newRawMessage(conf:g('msg-langcode'), value.langcode, mw.language.fetchLanguageName(value.langcode, this_wiki_code)) or "" if short_links and props.short then output[#output].text = mw.message.newRawMessage(props.short, getLabel(entity, props.genitive, pagetitle), url, langlink, verified_value, mw.uri.encode(verified_value, 'PATH')) :plain() else output[#output].text = mw.message.newRawMessage(props.message, getLabel(entity, props.genitive, pagetitle), url, langlink, verified_value, mw.uri.encode(verified_value, 'PATH')) :plain() end end end end --mw.log("findMainLinksOnWikidata returning="..dump(output)) return output end local function getSitelinksFromWikidata(props, entity) local output = {} --mw.log("getSitelinksFromWikidata, props="..dump(props)) -- to avoid deep tests if not entity then entity = mw.wikibase.getEntityObject() if not entity then --mw.log("getSitelinksFromWikidata no entity") return nil end end local requested_sitelink = string.match(props.prop, "SL(%l+)") local sitelinks = entity:getSitelink(requested_sitelink) if sitelinks and sitelinks ~= '' then output[#output+1] = { value = sitelinks } end --mw.log("getSitelinksFromWikidata returning output="..dump(output)) return output end local function findSiteLinksOnWikidata(props, pagetitle, short_links) local output = {} local pid = nil -- get the entity we are checking local entity = mw.wikibase.getEntityObject() -- to avoid deep tests if not entity then return nil end local values = getSitelinksFromWikidata(props) for _, value in ipairs( values ) do local verified_value = nil if props.regex then -- we have a local defined regex, so this will have to pass first -- maybe we'll have to convert the regex to Lua-style local regex = regexConverter(props.regex) local test = string.match( value.value, '^'..regex..'$' ) --mw.log("testing with "..regex.. " and test="..dump(test).." and value="..id) if test then -- it matched, this is correct and overrides any other. verified_value = value.value end else verified_value = value.value end if verified_value then --mw.log("it's verified..") local url = '' output[#output+1] = {} output[#output].langcode = value.langcode output[#output].category = {} if props.url_f then -- we have a local defined url-formatter function, use it as first priority url = props.url_f(verified_value) if props.track and not string.find(props.langcode, "(SL%l+)") then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-local-wd'), props.prop):plain() elseif props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-wd-wd'), props.prop):plain() end elseif props.url then -- we have a local defined url-formatter string, use it as second priority url = mw.message.newRawMessage(props.url, verified_value):plain() if props.track and not string.find(props.langcode, "(SL%l+)") then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-local-wd'), props.prop):plain() elseif props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-wd-wd'), props.prop):plain() end else url = verified_value:gsub(' ','_') if props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-wd-wd'), props.prop):plain() end end if url ~= '' then local this_wiki = mw.getContentLanguage() local this_wiki_code = this_wiki:getCode() local langlink = (value.langcode and value.langcode ~= '' and value.langcode ~= this_wiki_code) and mw.message.newRawMessage(conf:g('msg-langcode'), value.langcode, mw.language.fetchLanguageName(value.langcode, this_wiki_code)) or "" if short_links and props.short then output[#output].text = mw.message.newRawMessage(props.short, getLabel(entity, props.genitive, pagetitle), url, langlink, verified_value, mw.uri.encode(verified_value, 'PATH')) :plain() else output[#output].text = mw.message.newRawMessage(props.message, getLabel(entity, props.genitive, pagetitle), url, langlink, verified_value, mw.uri.encode(verified_value, 'PATH')) :plain() end end end end --mw.log("findSiteLinksOnWikidata returning="..dump(output)) return output end local function findMainLinksLocal(props, pagetitle, short_links, local_value) local output = {} -- to avoid deep tests if not props.prop then return nil end if not (local_value or local_value == '') then -- bail out if no value is present return output end -- get the formatvalue from the property local verified_value = local_value if props.regex and props.regex ~= '' then -- let's verify the id -- maybe we'll have to convert the regex to Lua-style local regex = regexConverter(props.regex) local test = string.match( local_value, '^'..regex..'$' ) if test then -- it matched, this is correct verified_value = local_value else verified_value = nil end end if not verified_value then return output end local wikidata_property = string.find(props.prop, "([pP]%d+)") local wikidata_values = {} if wikidata_property then -- get any wikidata values to see if they are equal to local values wikidata_values = getValuesFromWikidata(props) end if wikidata_property or (props.url and props.url ~= '') or (props.url_f) then output[#output+1] = {} output[#output].langcode = string.find(props.langcode, "([pP]%d+)") and "" or props.langcode --mw.log("findMainLinksLocal - props="..dump(props).." langcode="..output[#output].langcode) output[#output].category = {} local url = '' if props.track and wikidata_property and wikidata_values and #wikidata_values then local local_value_in_wikidata = false for _,value in ipairs( wikidata_values ) do if value.value == verified_value then local_value_in_wikidata = true end end output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), (local_value_in_wikidata and 'track-cat-local-wd-equal' or 'track-cat-local-wd-unequal')), props.prop):plain() end if wikidata_property and wikidata_values and #wikidata_values then hasdatafromwikidata = true -- signal up the chain this article has a wikidata claim end if props.url_f then -- we have a local defined url-formatter function, use it as first priority url = props.url_f(verified_value) if props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-local-local'), props.prop):plain() end elseif props.url then -- we have a local defined url-formatter string, use it as second priority url = mw.message.newRawMessage(props.url, verified_value):plain() if props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-local-local'), props.prop):plain() end elseif wikidata_property then -- get the formatvalue from the property, if it exists local formatterUrl = getFormatterUrl(props.prop, verified_value) if formatterUrl ~= '' then url = mw.message.newRawMessage(formatterUrl, verified_value):plain() if props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-wd-local'), props.prop):plain() end end else -- no other choice, bail out return {} end local this_wiki = mw.getContentLanguage() local this_wiki_code = this_wiki:getCode() local langlink = (output[#output].langcode and output[#output].langcode ~= '' and output[#output].langcode ~= this_wiki_code) and mw.message.newRawMessage(conf:g('msg-langcode'), props.langcode, mw.language.fetchLanguageName(props.langcode, this_wiki_code)) or "" if short_links and props.short then output[#output].text = mw.message.newRawMessage(props.short, getLabel(nil, props.genitive, pagetitle), url, langlink, verified_value, mw.uri.encode(verified_value, 'PATH')) :plain() else output[#output].text = mw.message.newRawMessage(props.message, getLabel(nil, props.genitive, pagetitle), url, langlink, verified_value, mw.uri.encode(verified_value, 'PATH')) :plain() end end --mw.log("findMainLinksLocal returning="..dump(output)) return output end local function findSiteLinksLocal(props, pagetitle, short_links, local_value) local output = {} -- to avoid deep tests if not props.prop then return nil end if not (local_value or local_value == '') then -- bail out if no value is present return output end -- get the formatvalue from the property local verified_value = local_value if props.regex and props.regex ~= '' then -- let's verify the id -- maybe we'll have to convert the regex to Lua-style local regex = regexConverter(props.regex) local test = string.match( local_value, '^'..regex..'$' ) if test then -- it matched, this is correct verified_value = local_value else verified_value = nil end end if not verified_value then return output end local wikidata_property = string.find(props.prop, "(SL.+)") local wikidata_values = {} if wikidata_property then -- get any wikidata values to see if they are equal to local values wikidata_values = getSitelinksFromWikidata(props) end if wikidata_property or (props.url and props.url ~= '') or (props.url_f) then output[#output+1] = {} output[#output].langcode = string.find(props.langcode, "(SL.+)") and "" or props.langcode --mw.log("findSiteLinksLocal - props="..dump(props).." langcode="..output[#output].langcode .." wikidata_values="..dump(wikidata_values)) output[#output].category = {} local url = '' if props.track and wikidata_property and wikidata_values and #wikidata_values then local local_value_in_wikidata = false for _,value in ipairs( wikidata_values ) do if value.value == verified_value then local_value_in_wikidata = true end end output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), (local_value_in_wikidata and 'track-cat-local-wd-equal' or 'track-cat-local-wd-unequal')), props.prop):plain() end if wikidata_property and wikidata_values and #wikidata_values then hasdatafromwikidata = true -- signal up the chain this article has a wikidata claim end if props.url_f then -- we have a local defined url-formatter function, use it as first priority url = props.url_f(verified_value) if props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-local-local'), props.prop):plain() end elseif props.url then -- we have a local defined url-formatter string, use it as second priority url = mw.message.newRawMessage(props.url, verified_value):plain() if props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-local-local'), props.prop):plain() end elseif wikidata_property then url = verified_value:gsub(' ','_') if props.track then output[#output].category[#output[#output].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLang:getCode(), 'track-cat-wd-local'), props.prop):plain() end else -- no other choice, bail out return {} end local this_wiki = mw.getContentLanguage() local this_wiki_code = this_wiki:getCode() local langlink = (output[#output].langcode and output[#output].langcode ~= '' and output[#output].langcode ~= this_wiki_code) and mw.message.newRawMessage(conf:g('msg-langcode'), props.langcode, mw.language.fetchLanguageName(props.langcode, this_wiki_code)) or "" if short_links and props.short then output[#output].text = mw.message.newRawMessage(props.short, getLabel(nil, props.genitive, pagetitle), url, langlink, verified_value, mw.uri.encode(verified_value, 'PATH')) :plain() else output[#output].text = mw.message.newRawMessage(props.message, getLabel(nil, props.genitive, pagetitle), url, langlink, verified_value, mw.uri.encode(verified_value, 'PATH')) :plain() end end --mw.log("findSiteLinksLocal returning="..dump(output)) return output end local function addLinkback(str, property) local id = mw.wikibase.getEntityObject() if not id then return str end if type(id) == 'table' then id = id.id end local class = '' local url = '' if property then class = 'wd_' .. string.lower(property) url = mw.uri.fullUrl('d:' .. id .. '#' .. property) url.fragment = property else url = mw.uri.fullUrl('d:' .. id ) end local title = conf:g('wikidata-linkback-edit') local icon = '[%s [[File:Blue pencil.svg|%s|10px|text-top|link=]] ]' url = tostring(url) local v = mw.html.create('span') :addClass(class) :wikitext(str) :tag('span') :addClass('noprint plainlinks wikidata-linkback') :css('padding-left', '.3em') :wikitext(icon:format(url, title)) :allDone() return tostring(v) end local function getArgument(frame, argument) local args = frame.args if args[1] == nil then local pFrame = frame:getParent(); args = pFrame.args; for k,v in pairs( frame.args ) do args[k] = v; end end if args[argument] then return args[argument] end return nil end local function removeEntry(conf_claims, identifier, property) for i, props in ipairs(conf_claims) do if props[identifier] == property then table.remove(conf_claims, i) end end return conf_claims end function p.getLinks(frame) local configured_conf = getArgument(frame, conf:a('arg-conf')) if configured_conf then cmodule = require ('Module:External_links/conf/'..configured_conf) else error(mw.message.newRawMessage(conf:g('missing-conf'), configured_conf):plain()) end local output = {} local category = {} local conf_claims = cmodule:getConfiguredClaims(contLang:getCode()) local limits = cmodule:getLimits() assert(limits, mw.message.newRawMessage(conf:g('missing-limits'), configured_conf):plain()) local links_shown = getArgument(frame, conf:a('arg-maxlink')) local pagetitle = getArgument(frame, conf:a('arg-title')) -- get a list of tracked properties from the article itself local requested_tracking = getArgument(frame, conf:a('arg-track')) if requested_tracking and requested_tracking ~= '' then -- the properties should be written as P1234, P2345 and other -- version corresponding to the applicable property-identifiers in the config for track_prop in string.gmatch(requested_tracking,"([^ ,;:]+)") do -- get the requested properties and be able to access them -- like req_prop['P345'] to verify if it was requested local remove_track = string.match(track_prop, "^\-(.*)") for i,claim in ipairs ( conf_claims ) do if remove_track == claim.prop or remove_track == conf:a('mod-filter-all') then -- if a property starts with "-", then we'll simply remove that -- property from the conf_claims conf_claims[i]['track'] = false elseif track_prop == claim.prop or track_prop == conf:a('mod-filter-all') then conf_claims[i]['track'] = true end end end end -- get a list of "approved" properties from the article itself local requested_properties = getArgument(frame, conf:a('arg-properties')) --mw.log("requested_properties="..dump(requested_properties)) -- assume all properties are allowed local req_prop = {} local no_req_prop = false -- we'll allow properties to be filtered for now if requested_properties and requested_properties ~= '' then -- the properties should be written as P1234, P2345 and other -- version corresponding to the applicable property-identifiers in the config for i in string.gmatch(requested_properties,"([^ ,;:]+)") do -- get the requested properties and be able to access them -- like req_prop['P345'] to verify if it was requested if i == conf:a('mod-filter-all') then -- this is a special modifier, saying we should ignore -- all previous and future positive filters and remove the -- filter (with exception of negative filters) req_prop = {} no_req_prop = true end local remove_prop = string.match(i, "^\-(.*)") if remove_prop then -- if a property starts with "-", then we'll simply remove that -- property from the conf_claims conf_claims = removeEntry(conf_claims, 'prop', remove_prop) elseif not no_req_prop then -- only if we are allowing properties to be filtered req_prop[i] = 1 -- cheat to make #req_prop indicate populated table req_prop[1] = 1 end end end local requested_langs = getArgument(frame, conf:a('arg-languages')) --mw.log("requested_langs="..dump(requested_langs)) -- assume all languages are allowed local req_lang = {} local no_req_lang = false -- we'll allow languages to be filtered for now if requested_langs and requested_langs ~= '' then -- the languages should be written as langcodes as used in the conf_claims for i in string.gmatch(requested_langs,"([^ ,;:]+)") do -- get the requested languages and be able to access them if i == conf:a('mod-filter-all') then -- this is a special modifier, saying we should ignore -- all previous and future positive filters and remove the -- filter (with exception of negative filters) req_lang = {} no_req_lang = true end -- like req_lang['en'] to verify if it was requested local remove_lang = string.match(i, "^\-(.*)") if remove_lang then -- if a language starts with "-", then we'll simply remove that -- language from the conf_claims conf_claims = removeEntry(conf_claims, 'langcode', remove_lang) elseif not no_req_lang then -- only if we are allowing languages to be filtered req_lang[i] = 1 -- cheat to make #req_lang indicate populated table req_lang[1] = 1 end end end local short_links = getArgument(frame, conf:a('arg-short')) if short_links and short_links ~= '' then short_links = true else short_links = false end local showinline = getArgument(frame, conf:a('arg-inline')) if showinline and showinline ~= '' then showinline = true else showinline = false end if not links_shown or links_shown == '' then links_shown = limits['links-shown'] and limits['links-shown'] or 10 else links_shown = tonumber(links_shown) end local somedataonwikidata = (short_links and false or true) --mw.log("conf_claims="..dump(conf_claims)) --mw.log("req_prop="..dump(req_prop)) --mw.log("req_lang="..dump(req_lang)) --mw.log("short_links="..dump(short_links)) for _, props in ipairs(conf_claims) do -- if we're called with a list of approved properties or languages, check if this one is "approved" if (#req_prop==0 or req_prop[props.prop]) and (#req_lang==0 or req_lang[props.langcode] or string.find(props.langcode, "([pP]%d+)")) then --mw.log("checking claim="..dump(props)) local links = {} local checkedonwikidata = false -- get the any local overriding value from the call local wikivalue = getArgument(frame, props.prop) --mw.log("wikivalue="..dump(wikivalue)) if (not wikivalue or wikivalue == "") and string.find(props.prop, "([pP]%d+)") then -- the property is a Pnnn type, and therefore on Wikidata links = findMainLinksOnWikidata(props, pagetitle, short_links) if links == nil then -- a nil-value indicated no wikidata-link haswikidatalink = false links = {} else checkedonwikidata = true end elseif (not wikivalue or wikivalue == "") and string.find(props.prop, "(SL%l+)") then -- this is a sitelink-type (SLspecieswiki) --mw.log("finding sitelinks..") links = findSiteLinksOnWikidata(props, pagetitle, short_links) if links == nil then -- a nil-value indicated no wikidata-link haswikidatalink = false links = {} else checkedonwikidata = true end elseif (wikivalue and wikivalue ~= "") and string.find(props.prop, "(SL%l+)") then -- this is a sitelink-type (SLspecieswiki) links = findSiteLinksLocal(props, pagetitle, short_links, wikivalue) elseif wikivalue and wikivalue ~= '' then -- the property is of another annotation, and therefore a local construct links = findMainLinksLocal(props, pagetitle, short_links, wikivalue) end --mw.log("links="..dump(links)) for _,v in ipairs(links) do -- we'll have to check langcodes again as they may have come from wikidata if (#req_lang==0 or req_lang[v.langcode]) then if checkedonwikidata and not hasdatafromwikidata then -- add a general tracking category for articles with data from wikidata hasdatafromwikidata = true category[#category+1] = cmodule:getMessage(contLang:getCode(), 'with-data-cat') elseif not checkedonwikidata and not hasdatafromlocal then -- add a general tracking category for articles with data from template-calls in local articles hasdatafromlocal = true category[#category+1] = cmodule:getMessage(contLang:getCode(), 'with-local-cat') end if short_links and props.short and v.text and v.text ~= '' then -- if short links were requested, and a short definition exists for this property, let's use it if #output==0 then output[#output+1] = v.text else output[#output] = output[#output] .. cmodule:getMessage(contLang:getCode(),'short-list-separator') .. v.text end somedataonwikidata = true elseif not short_links and not showinline and v.text and v.text ~= '' then -- only if short links were not requested output[#output+1] = (#output>=1 and conf:g('msg-ul-prepend') or '') -- if this is the first link, we won't output a list-element (msg-ul-prepend) .. (checkedonwikidata and addLinkback(v.text, props.prop) or v.text) -- if the link comes from wikidata, also output a linkback. elseif not short_links and showinline and v.text and v.text ~= '' then -- only if short links were not requested output[#output+1] = v.text end if props.track and v.category and #v.category then -- add category if tracking is on for this property and a category exists in the link-result. for _,cats in ipairs( v.category ) do category[#category+1] = cats end end if links_shown>0 then links_shown = links_shown - 1 else break end end end if links_shown==0 then break end end end local outtext = "" if short_links and #output>0 then -- if these are short links, output the whole thing with linkback to wikidata --mw.log("somedataonwikidata="..dump(somedataonwikidata).." and output="..dump(output).." and #output="..dump(#output)) outtext = (somedataonwikidata and addLinkback(table.concat(output,cmodule:getMessage(contLang:getCode(),'short-list-separator')), nil) or table.concat(output,cmodule:getMessage(contLang:getCode(),'short-list-separator'))) elseif not short_links and not showinline and #output>0 then outtext = table.concat(output,"\n") elseif not short_links and showinline and #output>0 then outtext = table.concat(output,conf:g('msg-inline-separator')) end if not hasdatafromwikidata then category[#category+1] = cmodule:getMessage(contLang:getCode(), 'no-data-cat') if not hasdatafromlocal and not short_links then outtext = cmodule:getMessage(contLang:getCode(), 'no-data-text') end end if not haswikidatalink then category[#category+1] = cmodule:getMessage(contLang:getCode(), 'no-wikilink-cat') if not hasdatafromlocal and not short_links then outtext = cmodule:getMessage(contLang:getCode(), 'no-wikilink') end end local nocategory = getArgument(frame, conf:a('arg-no-categories')) category = #category>0 and "\n" .. table.concat(category,"\n") or "" --mw.log("nocategory="..dump(nocategory).." and outtext="..dump(outtext).." and category="..dump(category)) outtext = outtext .. (nocategory and '' or category) return outtext end function p.getLanguageCode(frame) local prop = getArgument(frame, conf:a('arg-properties')) local output = getLanguageData(prop) return table.concat(output, conf:a('mod-filter-separator')) end return p