Jump to content

Module:External links/sandbox

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Lemondoge (talk | contribs) at 20:49, 23 June 2024 (Change "return nil" in findMainLinksLocal and findSiteLinksLocal into an explicit error condition (as it already would've errored because p.getLinks would've tried to iterate over the nil)). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
require('strict')
-- local genitive = require('Module:Genitive')._genitive
local contLangCode = mw.language.getContentLanguage():getCode()

local cmodule = {}
local conf = require 'Module:External links/conf'(contLangCode)
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 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 = output:gsub("\\d{(%d)}", function(num) return string.rep("%d", num) end)
	
	return output
end


local function getFormatterUrl( prop, value )
	local fUrl = ""
	local statements = mw.wikibase.getBestStatements(prop, "P1630")
	-- to avoid deep tests
	if #statements == 0 then
		return ""
	end
	-- let's go through the claims
	for _, claim in ipairs( statements ) do
		local mainsnak = claim.mainsnak or {}
		-- 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!
			-- traverse all snaks in this qualifier
			for _, qualsnak in ipairs( qualifiers[qualid] ) do
				if qualsnak
					and qualsnak.snaktype == pval[qualid].types.snaktype -- check if the snak is of the correct snaktype and datatype
					and qualsnak.datatype == pval[qualid].types.datatype
				then
					-- we'll have to convert the regex to Lua-style
					local regex = regexConverter(qualsnak.datavalue.value)
					local test = string.find( value, '^'..regex..'$' )
					if test then
						-- it matched, this is correct and overrides any other.
						fUrl = mainsnak.datavalue.value
						break
					end
				end
			end
		elseif fUrl == '' then -- if we don't have any other, use this one
			fUrl = mainsnak.datavalue.value
		end
	end
	return fUrl
end



local function getLanguageData(prop, qid, separator)
	-- Formerly outputted a table, but this function was always run through table.concat() when invoked, so it has been simplified to yield a string
	separator = separator or ''
	local output = ''
	if not mw.wikibase.entityExists(qid) then -- yield error, which would originally happen because table.concat(nil) errors
		error("getLanguageData was given a nonexistent entity")
	end
	-- get claims
	local statements = mw.wikibase.getBestStatements(qid, prop)
	-- to avoid deep tests
	if #statements == 0 then
		return ''
	end
	-- mw.log("getLanguageData going through claims="..dump(statements))
	-- let's go through the claims
	for _, claim in ipairs( statements ) do
		local mainsnak = claim.mainsnak or {}
		-- verify the item is what we expect
		if mainsnak.snaktype == pval[prop].types.snaktype
			and mainsnak.datatype == pval[prop].types.datatype
			and mainsnak.datavalue.type == pval[prop].types.datavalue.type
		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 prop == 'P364' then -- original language of work
				output = output .. separator .. getLanguageData('P218', 'Q'..mainsnak.datavalue.value['numeric-id'], conf:a('mod-filter-separator'))
			elseif prop == 'P218' or prop == 'P305' then -- ISO 639-1 code or IETF language tag
				output = output .. separator .. stringFormatter(mainsnak.datavalue)
			end
		end
	end
	return output
end

local langqvalorder = {'P407','P364'} -- check `language of work or name` first, `original language of film or TV show` second
local otherqvalorder = {'P582'}

local function getValuesFromWikidata(linkTemplate)
	local output = {}
	-- mw.log("getValuesFromWikidata, linkTemplate="..dump(linkTemplate))
	-- get statements
	local entity = mw.wikibase.getEntity()
	-- check if the entity exists
	-- TODO: check if we can skip distinguishing between no entity vs. no statements
	if not entity then -- check if the entity exists
		return nil
	end
	local statements = entity:getBestStatements(linkTemplate.prop)
	-- to avoid deep tests
	if #statements == 0 then
		return {}
	end
	-- let's go through the claims
	for _, claim in ipairs( statements ) do
		-- mw.log("getValuesFromWikidata valid claim="..dump(claim))
		local mainsnak = claim.mainsnak or {}
		local qualifiers = claim.qualifiers or {}
		-- get the content of the claim (the identifier)
		local langcode = linkTemplate.langcode
		if langcode
			and langcode ~= ''
			and string.find(langcode, "[pP]%d+") 
		then
			-- this is a P-value for language-code, so we'll check qualifiers for languagedata
			-- first get any qualifiers
			for _, qualid in ipairs( langqvalorder ) do
				-- if the claim has this qualifier
				if qualifiers[qualid] then
					-- it's here, let's check it out!
					-- traverse all snaks in this qualifier
					for _, qualsnak in ipairs( qualifiers[qualid] ) do
						if qualsnak
							and pval[qualid]
							and qualsnak.snaktype == pval[qualid].types.snaktype
							and qualsnak.datatype == pval[qualid].types.datatype
						then
							-- now get the actual data
							langcode = getLanguageData('P305', 'Q'..qualsnak.datavalue.value['numeric-id'])
						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
		for _, qualid in ipairs( otherqvalorder ) do
			-- if the claim has this qualifier
			if qualifiers[qualid] then
				-- it's here, let's check it out!
				-- traverse all snaks in this qualifier
				for _, qualsnak in ipairs( qualifiers[qualid] ) do
					if qualsnak and pval[qualid]
						and (qualsnak.snaktype ~= pval[qualid].types.snaktype -- check if the snak is of the correct snaktype and datatype
						or   qualsnak.datatype ~= pval[qualid].types.datatype) 
					then
						-- sorry, this is not correct
						mw.log("qualsnak = INCORRECT")
						stillvalid = false
						break
					end
				end
			end
			-- mw.log("langcode is now="..dump(langcode))
		end
		if stillvalid then
			output[#output+1] = { value=stringFormatter(mainsnak.datavalue) }
			if langcode and langcode ~= '' then
				output[#output]['langcode'] = langcode
			end
		end
	end
	-- mw.log("getValuesFromWikidata returning head="..dump(head).." tail="..dump(tail))
	return output
end

local function findMainLinksOnWikidata(linkTemplate, pagetitle, short_links)
	local output = {}
	-- get the entity we are checking
	local entity = mw.wikibase.getEntity()
	-- to avoid deep tests
	if not entity then
		return nil
	end
	local values = getValuesFromWikidata(linkTemplate)
	for _, value in ipairs( values ) do
		local verified_value = value.value
		if not (linkTemplate.regex and
			string.find( value.value, '^'..regexConverter(linkTemplate.regex)..'$') )
		then
			local url = ''
			output[#output+1] = {
				langcode = value.langcode,
				category = {}
			}
			-- Search for a url formatter
			if linkTemplate.url_f then
				-- we have a locally defined url-formatter function from the config, use it as first priority
				url = linkTemplate.url_f(verified_value)
				if linkTemplate.track and not string.find(linkTemplate.langcode, "[pP]%d+") then 
					output[#output].category[1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-local-wd'), linkTemplate.prop):plain()
				elseif linkTemplate.track then 
					output[#output].category[1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-wd-wd'), linkTemplate.prop):plain()
				end
			elseif linkTemplate.url then
				-- we have a locally defined url-formatter string from the config, use it as second priority
				url = mw.message.newRawMessage(linkTemplate.url, verified_value):plain()
				if linkTemplate.track and not string.find(linkTemplate.langcode, "[pP]%d+") then 
					output[#output].category[1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-local-wd'), linkTemplate.prop):plain()
				elseif linkTemplate.track then 
					output[#output].category[1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-wd-wd'), linkTemplate.prop):plain()
				end
			else
				-- config has no url formatter; check if Wikidata has one on the property
				local formatterUrl = getFormatterUrl(linkTemplate.prop, verified_value)
				if formatterUrl ~= '' then
					url = mw.message.newRawMessage(formatterUrl, verified_value):plain()
					if linkTemplate.track then 
						output[#output].category[1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-wd-wd'), linkTemplate.prop):plain()
					end
				end
			end
			if url ~= '' then
				local langlink = (value.langcode and value.langcode ~= '' and value.langcode ~= contLangCode) and mw.message.newRawMessage(conf:g('msg-langcode'), value.langcode, mw.language.fetchLanguageName(value.langcode, contLangCode)) or ""
				output[#output].text =
					mw.message.newRawMessage(short_links and linkTemplate.short or linkTemplate.message,
						getLabel(entity, linkTemplate.genitive, pagetitle),
						url,
						langlink,
						verified_value,
						mw.uri.encode(verified_value, 'PATH'))
					:plain()
			end
		end
	end
	--mw.log("findMainLinksOnWikidata returning="..dump(output))
	return output
end

local function getSitelinkFromWikidata(linkTemplate, entity)
	-- to avoid deep tests
	if not entity then
		entity = mw.wikibase.getEntity()
		if not entity then
			--mw.log("getSitelinkFromWikidata no entity")
			return nil
		end
	end
	local requested_sitelink = string.match(linkTemplate.prop, "SL(%l+)") -- a specific wiki to be linked to can be specified by config; otherwise, default to this wiki
	local sitelink = entity:getSitelink(requested_sitelink)
	return sitelink or nil
end

-- This function has a bug: :getSitelink does not return an object - only a string; yet this function tries to access value.langcode
local function findSiteLinksOnWikidata(linkTemplate, pagetitle, short_links)
	local output = {}
	
	local value = getSitelinkFromWikidata(linkTemplate)
	-- verify existence of sitelink
	if not value then
		return nil
	end
	if not (linkTemplate.regex and
		string.find( value, '^'..regexConverter(linkTemplate.regex)..'$') )
	then
		--mw.log("it's verified..")
		local url = ''
		output[1] = {
			langcode = value.langcode,
			category = {}
		}
		-- Search for a url-formatter
		if linkTemplate.url_f then
			-- we have a locally defined url-formatter function from the config, use it as first priority
			url = linkTemplate.url_f(value)
		elseif linkTemplate.url then
			-- we have a locally defined url-formatter string from the config, use it as second priority
			url = mw.message.newRawMessage(linkTemplate.url, value):plain()
		else
			url = value:gsub(' ','_')
		end
		
		if linkTemplate.track and not string.find(linkTemplate.langcode, "SL%l+") and (linkTemplate.url_f or linkTemplate.url) then
			output[1].category[1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-local-wd'), linkTemplate.prop):plain()
		elseif linkTemplate.track then
			output[1].category[1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-wd-wd'), linkTemplate.prop):plain()
		end
		
		if url ~= '' then
			local langlink = (value.langcode and value.langcode ~= '' and value.langcode ~= contLangCode) and mw.message.newRawMessage(conf:g('msg-langcode'), value.langcode, mw.language.fetchLanguageName(value.langcode, contLangCode)) or ""
			output[1].text =
				mw.message.newRawMessage(short_links and linkTemplate.short or linkTemplate.message,
					getLabel(entity, linkTemplate.genitive, pagetitle),
					url,
					langlink,
					value,
					mw.uri.encode(value, 'PATH'))
				:plain()
		end
	end
	--mw.log("findSiteLinksOnWikidata returning="..dump(output))
	return output
end


local function findMainLinksLocal(linkTemplate, pagetitle, short_links, local_value)
	local output = {}
	-- to avoid deep tests
	if not linkTemplate.prop then -- original module would error because p.getLinks would attempt to iterate over nil
		error("findMainLinksLocal was fed a linkTemplate that did not have an associated property")
	end
	if not local_value or local_value == '' -- bail out if no value is present
		or (linkTemplate.regex and linkTemplate.regex ~= ''
			and not string.find( local_value, '^'..regexConverter(linkTemplate.regex)..'$' ))
	then
		return {}
	end
	local wikidata_property = string.find(linkTemplate.prop, "[pP]%d+")
	local wikidata_values = nil
	if wikidata_property then
		-- get any wikidata values to see if they are equal to local values
		wikidata_values = getValuesFromWikidata(linkTemplate)
	end
	if wikidata_property or (linkTemplate.url and linkTemplate.url ~= '') or (linkTemplate.url_f) then
		output[1] = {
			langcode = string.find(linkTemplate.langcode, "[pP]%d+") and "" or linkTemplate.langcode,
			category = {}
		}
		local url = ''
		assert(not wikidata_values or type(wikidata_values) == 'table', "Something went wrong: wikidata_values is neither a table nor nil")
		if linkTemplate.track and wikidata_values then
			local local_value_in_wikidata = false
			for _,value in ipairs( wikidata_values ) do
				if value.value == local_value then
					local_value_in_wikidata = true
					break
				end
			end
			output[1].category[1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, (local_value_in_wikidata and 'track-cat-local-wd-equal' or 'track-cat-local-wd-unequal')), linkTemplate.prop):plain()
		end
		if wikidata_values then
			hasdatafromwikidata = true -- signal up the chain this article has a wikidata claim
		end
		-- Search for a url-formatter
		if linkTemplate.url_f then
			-- we have a locally defined url-formatter function from the config, use it as first priority
			url = linkTemplate.url_f(local_value)
			if linkTemplate.track then 
				output[1].category[#output[1].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-local-local'), linkTemplate.prop):plain()
			end
		elseif linkTemplate.url then
			-- we have a locally defined url-formatter string from the config, use it as second priority
			url = mw.message.newRawMessage(linkTemplate.url, local_value):plain()
			if linkTemplate.track then 
				output[1].category[#output[1].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-local-local'), linkTemplate.prop):plain()
			end
		else -- we know wikidata_property exists
			-- config has no url formatter; check if Wikidata has one on the property
			local formatterUrl = getFormatterUrl(linkTemplate.prop, local_value)
			if formatterUrl ~= '' then
				url = mw.message.newRawMessage(formatterUrl, local_value):plain()
				if linkTemplate.track then
					output[1].category[#output[1].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-wd-local'), linkTemplate.prop):plain()
				end
			end
		end
		local langlink = (output[1].langcode and output[1].langcode ~= '' and output[1].langcode ~= contLangCode) and mw.message.newRawMessage(conf:g('msg-langcode'), linkTemplate.langcode, mw.language.fetchLanguageName(linkTemplate.langcode, contLangCode)) or ""
		output[1].text =
			mw.message.newRawMessage(short_links and linkTemplate.short or linkTemplate.message,
				getLabel(nil, linkTemplate.genitive, pagetitle),
				url,
				langlink,
				local_value,
				mw.uri.encode(local_value, 'PATH'))
			:plain()
	end
	--mw.log("findMainLinksLocal returning="..dump(output))
	return output
end

local function findSiteLinksLocal(linkTemplate, pagetitle, short_links, local_value)
	local output = {}
	-- to avoid deep tests
	if not linkTemplate.prop then -- original module would error because p.getLinks would attempt to iterate over nil
		error("findSiteLinksLocal was fed a linkTemplate that did not have an associated property")
	end
	if not local_value or local_value == '' -- bail out if no value is present
		or (linkTemplate.regex and linkTemplate.regex ~= ''
			and not string.find( local_value, '^'..regexConverter(linkTemplate.regex)..'$' ))
	then
		return {}
	end
	local wikidata_property = string.find(linkTemplate.prop, "SL.+")
	local wikidata_sitelink = nil
	if wikidata_property then
		-- get any wikidata values to see if they are equal to local values
		wikidata_sitelink = getSitelinkFromWikidata(linkTemplate)
	end
	if wikidata_property or (linkTemplate.url and linkTemplate.url ~= '') or (linkTemplate.url_f) then
		output[1] = {
			langcode = string.find(linkTemplate.langcode, "SL.+") and "" or linkTemplate.langcode,
			category = {}
		}
		--mw.log("findSiteLinksLocal - linkTemplate="..dump(linkTemplate).." langcode="..output[#output].langcode .." wikidata_values="..dump(wikidata_values))
		local url = ''
		if linkTemplate.track and wikidata_sitelink then
			local local_value_in_wikidata = (wikidata_sitelink == local_value)
			output[1].category[1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, (local_value_in_wikidata and 'track-cat-local-wd-equal' or 'track-cat-local-wd-unequal')), linkTemplate.prop):plain()
		end
		if wikidata_sitelink then
			hasdatafromwikidata = true -- signal up the chain this article has a wikidata claim
		end
		-- Search for a url formatter
		if linkTemplate.url_f then
			-- we have a locally defined url-formatter function from the config, use it as first priority
			url = linkTemplate.url_f(local_value)
			if linkTemplate.track then 
				output[1].category[#output[1].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-local-local'), linkTemplate.prop):plain()
			end
		elseif linkTemplate.url then
			-- we have a locally defined url-formatter string from the config, use it as second priority
			url = mw.message.newRawMessage(linkTemplate.url, local_value):plain()
			if linkTemplate.track then 
				output[1].category[#output[1].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-local-local'), linkTemplate.prop):plain()
			end
		else -- we know wikidata_property exists
			url = local_value:gsub(' ','_')
			if linkTemplate.track then 
				output[1].category[#output[1].category+1] = mw.message.newRawMessage(cmodule:getMessage(contLangCode, 'track-cat-wd-local'), linkTemplate.prop):plain()
			end
		end
		local langlink = (output[1].langcode and output[1].langcode ~= '' and output[1].langcode ~= contLangCode) and mw.message.newRawMessage(conf:g('msg-langcode'), linkTemplate.langcode, mw.language.fetchLanguageName(linkTemplate.langcode, contLangCode)) or ""
		output[1].text =
			mw.message.newRawMessage(short_links and linkTemplate.short or linkTemplate.message,
				getLabel(nil, linkTemplate.genitive, pagetitle),
				url,
				langlink,
				local_value,
				mw.uri.encode(local_value, 'PATH'))
			:plain()
	end
	--mw.log("findSiteLinksLocal returning="..dump(output))
	return output
end


local function addLinkback(str, property)
	local id = mw.wikibase.getEntityIdForCurrentPage()
	if not id then
		return str
	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
	return args[argument]
end


local function removeEntry(conf_claims, identifier, property)
	for i, linkTemplate in ipairs(conf_claims) do
		if linkTemplate[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(contLangCode)
	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] = true
				-- cheat to make #req_prop indicate populated table
				req_prop[1] = true
			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] = true
				-- cheat to make #req_lang indicate populated table
				req_lang[1] = true
			end
		end
	end
	local short_links = getArgument(frame, conf:a('arg-short'))
	short_links = (short_links and short_links ~= '' or false)
	
	local showinline = getArgument(frame, conf:a('arg-inline'))
	showinline = (showinline and showinline ~= '' or false)
	
	if not links_shown or links_shown == '' then
		links_shown = limits['links-shown'] or 10
	else
		links_shown = tonumber(links_shown)
	end
	local somedataonwikidata = not short_links
	--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 _, linkTemplate 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[linkTemplate.prop]) and (#req_lang==0 or req_lang[linkTemplate.langcode] or string.find(linkTemplate.langcode, "[pP]%d+")) then
			--mw.log("checking claim="..dump(linkTemplate))
			local links = {}
			local checkedonwikidata = false
			-- get the any local overriding value from the call
			local wikivalue = getArgument(frame, linkTemplate.prop)
			--mw.log("wikivalue="..dump(wikivalue))
			if (not wikivalue or wikivalue == "") and string.find(linkTemplate.prop, "[pP]%d+") then
				-- the property is a Pnnn type, and therefore on Wikidata
				links = findMainLinksOnWikidata(linkTemplate, 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(linkTemplate.prop, "SL%l+") then
				-- this is a sitelink-type (SLspecieswiki)
				--mw.log("finding sitelinks..")
				links = findSiteLinksOnWikidata(linkTemplate, pagetitle, short_links)
				if links == nil then
					-- a nil-value indicated no wikidata-link
					haswikidatalink = false
					links = {}
				else
					checkedonwikidata = true
				end
			elseif string.find(linkTemplate.prop, "SL%l+") then -- we know that wikivalue is set if this is true
				-- this is a sitelink-type (SLspecieswiki)
				links = findSiteLinksLocal(linkTemplate, pagetitle, short_links, wikivalue)
			elseif wikivalue and wikivalue ~= '' then
				-- the property is of another annotation, and therefore a local construct
				links = findMainLinksLocal(linkTemplate, 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(contLangCode, 'with-data-cat')
					elseif not checkedonwikidata and not hasdatafromlocal then -- not checkonwikidata
						-- add a general tracking category for articles with data from template-calls in local articles
						hasdatafromlocal = true
						category[#category+1] = cmodule:getMessage(contLangCode, 'with-local-cat')
					end
					if short_links and linkTemplate.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[1] = v.text
						else
							output[#output] = output[#output] .. cmodule:getMessage(contLangCode,'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 ~= 0 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, linkTemplate.prop) or v.text)	-- if the link comes from wikidata, also output a linkback.
					elseif not short_links and v.text and v.text ~= '' then -- and showinline
						-- only if short links were not requested
						output[#output+1] = v.text
					end
					if linkTemplate.track 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(contLangCode,'short-list-separator')), nil)
			or table.concat(output,cmodule:getMessage(contLangCode,'short-list-separator')))
	elseif not showinline and #output>0 then -- and not shortlinks
		outtext = table.concat(output,"\n")
	elseif #output>0 then -- and not short_links and showinline
		outtext = table.concat(output,conf:g('msg-inline-separator'))
	end
	if not hasdatafromwikidata then
		category[#category+1] = cmodule:getMessage(contLangCode, 'no-data-cat')
		if not hasdatafromlocal and not short_links then
			outtext = cmodule:getMessage(contLangCode, 'no-data-text')
		end
	end
	if not haswikidatalink then
		category[#category+1] = cmodule:getMessage(contLangCode, 'no-wikilink-cat')
		if not hasdatafromlocal and not short_links then
			outtext = cmodule:getMessage(contLangCode, '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'))
	return getLanguageData(prop, nil, conf:a(mod-filter-separator))
end

return p