|
|
| Linje 1: |
Linje 1: |
| require('Module:No globals')
| |
| local genitiv = require('Modul:Genitiv')._genitiv
| |
| 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 themainentity = mw.wikibase.getEntity()
| |
|
| |
| local p = {}
| |
|
| |
| local function getLabel(entity, use_genitiv, 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_genitiv and genitiv(label, 'sitt') 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
| |
|
| |
| function string.urlEncode( str )
| |
| if ( str ) then
| |
| str = string.gsub( str, "\n", "\r\n" )
| |
| str = string.gsub( str, "([^-%w/ ])",
| |
| function (c) return string.format( "%%%02X", string.byte(c) ) end )
| |
| str = string.gsub( str, " ", "+" )
| |
| end
| |
| return str
| |
| 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 mainsnak.datavalue and (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 = themainentity
| |
| -- 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 = themainentity
| |
| -- 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, string.urlEncode(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, string.urlEncode(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.genitiv, pagetitle),
| |
| url,
| |
| langlink,
| |
| verified_value,
| |
| mw.uri.encode(verified_value, 'PATH'))
| |
| :plain()
| |
| else
| |
| output[#output].text =
| |
| mw.message.newRawMessage(props.message,
| |
| getLabel(entity, props.genitiv, 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 = themainentity
| |
| 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 = themainentity
| |
| -- 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.genitiv, pagetitle),
| |
| url,
| |
| langlink,
| |
| verified_value,
| |
| mw.uri.encode(verified_value, 'PATH'))
| |
| :plain()
| |
| else
| |
| output[#output].text =
| |
| mw.message.newRawMessage(props.message,
| |
| getLabel(entity, props.genitiv, 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, string.urlEncode(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, string.urlEncode(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.genitiv, pagetitle),
| |
| url,
| |
| langlink,
| |
| verified_value,
| |
| mw.uri.encode(verified_value, 'PATH'))
| |
| :plain()
| |
| else
| |
| output[#output].text =
| |
| mw.message.newRawMessage(props.message,
| |
| getLabel(nil, props.genitiv, 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.genitiv, pagetitle),
| |
| url,
| |
| langlink,
| |
| verified_value,
| |
| mw.uri.encode(verified_value, 'PATH'))
| |
| :plain()
| |
| else
| |
| output[#output].text =
| |
| mw.message.newRawMessage(props.message,
| |
| getLabel(nil, props.genitiv, 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 = themainentity
| |
| 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|baseline|link=]] ]'
| |
| url = tostring(url)
| |
| local v = mw.html.create('span')
| |
| :addClass(class)
| |
| :wikitext(str)
| |
| :tag('span')
| |
| :addClass('noprint plainlinks wikidata-linkback')
| |
| :css('padding-left', '.5em')
| |
| :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
| |
| 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
| |