Vés al contingut

Mòdul:Authority control/proves

De la Viquipèdia, l'enciclopèdia lliure
local p = {}

-- ============ Format validation functions ============

local valid = {}

-- NCDA: NOID Check Digit Algorithm; see [[wikipedia:Check digit#NCDA]]
local ncda -- leave this as a local since NCDA is commonly used among ARK identifiers and could be useful for validating other identifiers later
do -- initialize these constants only once but scope them in a block so local namespace doesn't get cluttered with these
	local r29s = [[0123456789bcdfghjkmnpqrstvwxz]] -- radix 29 "betanumeric" digit string
	local r29n = r29s:len()
	local r29v2d, r29d2v = {}, {}
	for i = 1, r29n do
		local v, d = i-1, r29s:sub(i, i)
		r29v2d[v], r29d2v[d] = d, v
	end
	function ncda(sid)
		local n, sum = sid:len(), 0
		for i = 1, n do
			sum = sum + i * (r29d2v[sid:sub(i, i)] or 0)
		end
		return r29v2d[sum % r29n]
	end
end
function valid.validateBNF(id)
	--P268's format regex: \d{8}[0-9bcdfghjkmnpqrstvwxz] (e.g. 123456789)
	local FRBNF = id:sub(1, -2)
	return FRBNF:match('^%d%d%d%d%d%d%d%d$') ~= nil and ncda('cb'..FRBNF) == id:sub(-1) and id
end

--Validate ISNI (and ORCID) and returns it as a 16 characters string or returns false if it's invalid.
-- See http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
--P213 (ISNI) format regex: [0-9]{4} [0-9]{4} [0-9]{4} [0-9]{3}[0-9X] (e.g. 0000-0000-6653-4145)
function valid.validateIsni(id)
	id = id:gsub( '[ %-]', '' ):upper()
	if not id:match( '^%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d[%dX]$' ) then
		return false
	end
	local total = 0
	for i = 1, 15 do
		local digit = id:byte( i ) - 48 --Get integer value
		total = (total + digit) * 2
	end
	local remainder = total % 11
	local result = (12 - remainder) % 11
	local checkdigit
	if result == 10 then
		checkdigit = 'X'
	else
		checkdigit=tostring( result )
	end
	if checkdigit ~= string.char( id:byte( 16 ) ) then
		return false
	end
	return id
end

function valid.validateOrcid(id)
	--P496 (ORCID) format regex: 0000-000(1-[5-9]|2-[0-9]|3-[0-4])\d{3}-\d{3}[\dX] (e.g. 0000-0002-7398-5483)
	id = valid.validateIsni(id)
	if not id then
		return false
	end
	return id:sub( 1, 4 )..'-'..id:sub( 5, 8 )..'-'..id:sub( 9, 12 )..'-'..id:sub( 13, 16 )
end

function valid.validateEuropeana(id)
	--P7704's format regex: (place|agent|concept|organisation)/.* (e.g. agent/base/59832)
	local s1, s2, s3 = string.match(id, '([^/]+)([^%d]+)(%d+)')
	if not (s1 and s2 and s3) and not (s1 == 'place' or s1 == 'agent' or s1 == 'concept' or s1 == 'organisation') then
		return false
	end
	return s1 .. s2 .. s3
end

-- ============ Local functions ============

local function validValue(conf_id, id)
	if not id then
		return
	end
	local valid_value
	if conf_id.pattern then -- check pattern to determine validity
		valid_value = string.match(id, '^' .. conf_id.pattern .. '$')
	elseif conf_id.patterns then -- check multiple patterns to determine validity
		for _, pattern in ipairs(conf_id.patterns) do
			valid_value = id:match('^' .. pattern .. '$')
			if valid_value then
				break
			end
		end
	elseif conf_id.valid and type(valid[conf_id.valid]) == 'function' then -- use function to determine validity
		valid_value = valid[conf_id.valid](id)
	else -- no validation possible
		valid_value = id
	end
	return valid_value
end

local title = mw.title.getCurrentTitle()
local namespace = title.namespace

local function addCat(cat)
	if cat and namespace == 0 then
		return '[[Category:Articles amb identificador ' .. cat .. ']]'
	end
	return ''
end

local function getIdsFromWikidata(item, property)
	if item.claims[property] then
		local claims = mw.wikibase.getBestStatements(item.id, property)
		for _, statement in ipairs(claims) do
			if statement.mainsnak.datavalue and statement.mainsnak.datavalue.value then
				return statement.mainsnak.datavalue.value
			end
		end
	end
	return
end

local function getLinkFromWikidata(property)
	local claims = mw.wikibase.getBestStatements(property, 'P1630')
	for _, statement in ipairs(claims) do
		if statement.mainsnak.datavalue and statement.mainsnak.datavalue.value then
			return statement.mainsnak.datavalue.value
		end
	end
	return
end

local function createId(params, id)
	local arg_link = params.link
	if not arg_link then
		arg_link = getLinkFromWikidata('P' .. params.property)
	end
	if arg_link then
		local link = mw.ustring.gsub(arg_link, '$1', id)
		if params.prefix then
			return '* ' .. params.prefix .. ' ([' .. link .. ' 1])' .. addCat(params.category) .. '\n'
		else
			return '* [' .. link .. ' ' .. params.label .. ']' .. addCat(params.category) .. '\n'
		end
	end
	return ''
end

-- ============ Main ============

local config = mw.loadData('Module:Authority control/config')

function p.authorityControl(frame)
	local conf = config.config
	local parentArgs = frame:getParent().args
	local auxCats = ''
	local rct = false -- boolean to track if there are any links to be returned
	
	-- Create sections
	local numsections, sections = 0, {}
	for _, _ in ipairs(config.sections) do -- count number of regular sections
		numsections = numsections + 1
	end
	for _ = 1, numsections do
		table.insert(sections, {})
	end
	
	-- fetch data with Wikidata fallback
	local item_obj = mw.wikibase.getEntityObject(parentArgs.item)
	if item_obj ~= nil and item_obj.claims ~= nil then
		for _, params in ipairs(conf) do
			local args_id = parentArgs[params.label]
			if args_id and args_id == '' then
				-- paràmetre definit però buit, no el mostrarà
			elseif params.property ~= 0 then
				local wikidata_id = getIdsFromWikidata(item_obj, 'P' .. params.property)
				local valid_id = validValue(params, args_id or wikidata_id)
				if valid_id then
					rct = true
					if wikidata_id then
						if args_id and args_id ~= wikidata_id then
							table.insert(sections[params.section], createId(params, valid_id))
							auxCats = auxCats .. "[[Categoria:Pàgines amb paràmetres d'autoritat diferents de Wikidata]]"
						else
							table.insert(sections[params.section], createId(params, valid_id))
						end
					elseif args_id then
						table.insert(sections[params.section], createId(params, valid_id))
					end
				end
			end
		end
	end
	
	--configure Navbox
	if rct then -- there is at least one link to display
		local Navbox = require('Module:Navbox')
		local sect = 0
		local navboxArgs = {
			name  = 'Autoritat',
			navboxclass = 'authority-control',
			bodyclass = 'hlist',
		}
		for c = 1, numsections do
			if #sections[c] > 0 then -- section is non-empty
				sect = sect + 1
				navboxArgs['group' .. sect] = config.sections[c].name
				navboxArgs['list' .. sect] = table.concat(sections[c])
			end
		end
		return Navbox._navbox(navboxArgs) .. auxCats
	end
	return ''

end

return p