Open main menu

Difference between revisions of "Module:Authority control"

en>Elegie
(Use HTTPS for the CiNii and NKC (National Library of the Czech Republic) links in order to provide increased privacy and security for users. (From what it appears, accessing an HTTP version of a link generates a 301 redirect to an HTTPS version of the link.))
m (174 revisions imported)
 
(62 intermediate revisions by 7 users not shown)
Line 1: Line 1:
 
require('Module:No globals')
 
require('Module:No globals')
  
local function getCatForId( id )
+
local p = {}
local title = mw.title.getCurrentTitle()
+
local title = mw.title.getCurrentTitle()
local namespace = title.namespace
+
local namespace = title.namespace
 +
local talkspace = (mw.site.talkNamespaces[namespace] ~= nil)
 +
local testcases = (string.sub(title.subpageText,1,9) == 'testcases')
 +
 
 +
--[[==========================================================================]]
 +
--[[                            Category functions                            ]]
 +
--[[==========================================================================]]
 +
 
 +
function p.getCatForId( id )
 +
local catName = ''
 
if namespace == 0 then
 
if namespace == 0 then
return '[[Category:Wikipedia articles with ' .. id .. ' identifiers]]'
+
catName = 'Wikipedia articles with '..id..' identifiers'
 
elseif namespace == 2 and not title.isSubpage then
 
elseif namespace == 2 and not title.isSubpage then
return '[[Category:User pages with ' .. id .. ' identifiers]]'
+
catName = 'User pages with '..id..' identifiers'
 
else
 
else
return '[[Category:Miscellaneous pages with ' .. id .. ' identifiers]]'
+
catName = 'Miscellaneous pages with '..id..' identifiers'
 +
end
 +
return '[[Category:'..catName..']]'..p.redCatLink(catName)
 +
end
 +
 
 +
function p.redCatLink( catName ) --catName == 'Blah', not 'Category:Blah', not '[[Category:Blah]]'
 +
if catName and catName ~= '' and
 +
  testcases == false and
 +
  mw.title.new(catName, 14).exists == false
 +
then
 +
return '[[Category:Pages with red-linked authority control categories]]'
 
end
 
end
 +
return ''
 
end
 
end
  
local function viafLink( id )
+
--[[==========================================================================]]
 +
--[[                      Property formatting functions                      ]]
 +
--[[==========================================================================]]
 +
 
 +
function p.iaafLink( id )
 +
--P1146's format regex: [0-9][0-9]* (e.g. 012)
 
if not string.match( id, '^%d+$' ) then
 
if not string.match( id, '^%d+$' ) then
 
return false
 
return false
 
end
 
end
return '[https://viaf.org/viaf/' .. id .. ' ' .. id .. ']' .. getCatForId( 'VIAF' )
+
return '[https://www.iaaf.org/athletes/_/'..id..' '..id..']'..p.getCatForId( 'IAAF' )
 +
end
 +
 
 +
function p.viafLink( id )
 +
--P214's format regex: [1-9]\d(\d{0,7}|\d{17,20}) (e.g. 123456789, 1234567890123456789012)
 +
if not string.match( id, '^[1-9]%d%d?%d?%d?%d?%d?%d?%d?$' ) and
 +
  not string.match( id, '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d?%d?%d?$' ) then
 +
return false
 +
end
 +
return '[https://viaf.org/viaf/'..id..' '..id..']'..p.getCatForId( 'VIAF' )
 
end
 
end
  
local function kulturnavLink( id )
+
function p.kulturnavLink( id )
return '[http://kulturnav.org/' .. id .. ' ' .. id .. ']'
+
--P1248's format regex: [0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
 +
if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
 +
return false
 +
end
 +
return '[http://kulturnav.org/'..id..' '..id..']'..p.getCatForId( 'KULTURNAV' ) --no https yet (10/2018)
 
end
 
end
  
local function sikartLink( id )
+
function p.sikartLink( id )
return '[http://www.sikart.ch/KuenstlerInnen.aspx?id=' .. id .. '&lng=en ' .. id .. ']'
+
--P781's format regex: \d{7,9} (e.g. 123456789)
 +
if not string.match( id, '^%d%d%d%d%d%d%d%d?%d?$' ) then
 +
return false
 +
end
 +
return '[http://www.sikart.ch/KuenstlerInnen.aspx?id='..id..'&lng=en '..id..']'..p.getCatForId( 'SIKART' ) --no https yet (10/2018)
 
end
 
end
  
local function tlsLink( id )
+
function p.tlsLink( id )
 
local id2 = id:gsub(' +', '_')
 
local id2 = id:gsub(' +', '_')
return '[http://tls.theaterwissenschaft.ch/wiki/' .. id2 .. ' ' .. id .. ']'
+
--P1362's format regex: \p{Lu}[\p{L}\d_',\.\-\(\)\*/–]{3,59} (e.g. Abcd)
 +
local class = "[%a%d_',%.%-%(%)%*/–]"
 +
local regex = "^%u"..string.rep(class, 3)..string.rep(class.."?", 56).."$"
 +
if not mw.ustring.match( id2, regex ) then
 +
return false
 +
end
 +
return '[http://tls.theaterwissenschaft.ch/wiki/'..id2..' '..id..']'..p.getCatForId( 'TLS' ) --no https yet (10/2018)
 
end
 
end
  
local function ciniiLink( id )
+
function p.ciniiLink( id )
return '[https://ci.nii.ac.jp/author/' .. id .. '?l=en ' .. id .. ']'
+
--P271's format regex: DA\d{7}[\dX] (e.g. DA12345678)
 +
if not string.match( id, '^DA%d%d%d%d%d%d%d[%dX]$' ) then
 +
return false
 +
end
 +
return '[https://ci.nii.ac.jp/author/'..id..'?l=en '..id..']'..p.getCatForId( 'CINII' )
 
end
 
end
  
local function bneLink( id )
+
function p.daaoLink( id )
return '[http://catalogo.bne.es/uhtbin/authoritybrowse.cgi?action=display&authority_id=' .. id .. ' ' .. id .. ']'
+
--P1707's format regex: [a-z\-]+\d* (e.g. rolf-harris)
 +
if not string.match( id, '^[a-z%-]+%d*$' ) then
 +
return false
 +
end
 +
return '[https://www.daao.org.au/bio/'..id..' '..id..']'..p.getCatForId( 'DAAO' )
 
end
 
end
  
local function uscongressLink( id )
+
function p.bneLink( id )
return '[http://bioguide.congress.gov/scripts/biodisplay.pl?index=' .. id .. ' ' .. id .. ']'
+
--P950's format regex: (XX|FF|a)\d{4,7}|(bima|bimo|bica|bis[eo]|bivi|Mise|Mimo|Mima)\d{10} (e.g. XX1234567)
 +
if not string.match( id, '^[XF][XF]%d%d%d%d%d?%d?%d?$' ) and
 +
  not string.match( id, '^a%d%d%d%d%d?%d?%d?$' ) and
 +
  not string.match( id, '^bi[mcsv][aoei]%d%d%d%d%d%d%d%d%d%d$' ) and
 +
  not string.match( id, '^Mi[sm][eoa]%d%d%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[http://catalogo.bne.es/uhtbin/authoritybrowse.cgi?action=display&authority_id='..id..' '..id..']'..p.getCatForId( 'BNE' ) --no https yet (10/2018)
 
end
 
end
  
local function narapersonLink( id )
+
function p.uscongressLink( id )
return '[https://research.archives.gov/person/' .. id .. ' ' .. id .. ']'
+
--P1157's format regex: [A-Z]00[01]\d{3} (e.g. A000123)
 +
if not string.match( id, '^[A-Z]00[01]%d%d%d$' ) then
 +
return false
 +
end
 +
return '[http://bioguide.congress.gov/scripts/biodisplay.pl?index='..id..' '..id..']'..p.getCatForId( 'USCongress' ) --no https yet (10/2018)
 
end
 
end
  
local function naraorganizationLink( id )
+
function p.naraLink( id )
return '[https://research.archives.gov/organization/' .. id .. ' ' .. id .. ']'
+
--P1225's format regex: ^([1-9]\d{0,8})$ (e.g. 123456789)
 +
if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
 +
return false
 +
end
 +
return '[https://catalog.archives.gov/id/'..id..' '..id..']'..p.getCatForId( 'NARA' )
 
end
 
end
  
local function botanistLink( id )
+
function p.botanistLink( id )
 +
--P428's format regex: ('t )?(d')?(de )?(la )?(van (der )?)?(Ma?c)?(De)?(Di)?\p{Lu}?C?['\p{Ll}]*([-'. ]*(van )?(y )?(d[ae][nr]?[- ])?(Ma?c)?[\p{Lu}bht]?C?['\p{Ll}]*)*\.? ?f?\.? (e.g. L.)
 +
--not easily/meaningfully implementable in Lua's regex since "(this)?" is not allowed...
 +
if not mw.ustring.match( id, "^[%u%l%d%. '-]+$" ) then --better than nothing
 +
return false
 +
end
 
local id2 = id:gsub(' +', '%%20')
 
local id2 = id:gsub(' +', '%%20')
return '[http://www.ipni.org/ipni/advAuthorSearch.do?find_abbreviation=' .. id2 .. ' ' .. id .. ']'
+
return '[https://www.ipni.org/ipni/advAuthorSearch.do?find_abbreviation='..id2..' '..id..']'..p.getCatForId( 'Botanist' )
 
end
 
end
  
local function mgpLink( id )
+
function p.mgpLink( id )
-- TODO Implement some sanity checking regex
+
--P549's format regex: \d{1,6} (e.g. 123456)
return '[http://www.genealogy.ams.org/id.php?id=' .. id .. ' ' .. id .. ']'
+
if not string.match( id, '^%d%d?%d?%d?%d?%d?$' ) then
 +
return false
 +
end
 +
return '[https://genealogy.math.ndsu.nodak.edu/id.php?id='..id..' '..id..']'..p.getCatForId( 'MGP' )
 
end
 
end
  
local function rslLink( id )
+
function p.rslLink( id )
-- TODO Implement some sanity checking regex
+
--P947's format regex: \d{1,9} (e.g. 123456789)
return '[http://aleph.rsl.ru/F?func=find-b&find_code=SYS&adjacent=Y&local_base=RSL11&request=' .. id .. '&CON_LNG=ENG ' .. id .. ']'
+
if not string.match( id, '^%d%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
 +
return false
 +
end
 +
return '[http://aleph.rsl.ru/F?func=find-b&find_code=SYS&adjacent=Y&local_base=RSL11&request='..id..'&CON_LNG=ENG '..id..']'..p.getCatForId( 'RSL' ) --no https yet (10/2018)
 
end
 
end
  
local function leonoreLink( id )
+
function p.leonoreLink( id )
-- Identifiants allant de LH/1/1 à LH/2794/54 (légionnaires)
+
--P640's format regex: LH/\d{1,4}/\d{1,3}|19800035/\d{1,4}/\d{1,5}(Bis)?|C/0/\d{1,2} (e.g. LH/2064/18)
-- Identifiants allant de C/0/1 à C/0/84 (84 légionnaires célèbres)
+
if not id:match( '^LH/%d%d?%d?%d?/%d%d?%d?$' ) and             --IDs from      LH/1/1 to        LH/2794/54 (legionaries)
-- Identifiants allant de 19800035/1/1 à 19800035/385/51670 (légionnaires décédés entre 1954 et 1977, et quelques dossiers de légionnaires décédés avant 1954)
+
  not id:match( '^19800035/%d%d?%d?%d?/%d%d?%d?%d?%d?$' ) and --IDs from 19800035/1/1 to 19800035/385/51670 (legionnaires who died 1954-1977 & some who died < 1954)
if not string.match( id, '^LH/%d%d?%d?%d?/%d%d?%d?$' ) and
+
  not id:match( '^C/0/%d%d?$' ) then                         --IDs from        C/0/1 to            C/0/84 (84 famous legionaries)
  not string.match( id, '^C/0/%d%d?$' ) and
 
  not string.match( id, '^19800035/%d%d?%d?%d?/%d%d?%d?%d?%d?$' ) then
 
 
return false
 
return false
 
end
 
end
return '[http://www.culture.gouv.fr/public/mistral/leonore_fr?ACTION=CHERCHER&FIELD_1=COTE&VALUE_1=' .. id .. ' ' .. id .. ']'
+
return '[http://www.culture.gouv.fr/public/mistral/leonore_fr?ACTION=CHERCHER&FIELD_1=COTE&VALUE_1='..id..' '..id..']'..p.getCatForId( 'Léonore' ) --no https yet (10/2018)
 
end
 
end
  
local function sbnLink( id )
+
function p.sbnLink( id )
if not string.match( id, '^IT\\ICCU\\%d%d%d%d%d%d%d%d%d%d$' ) and not string.match( id, '^IT\\ICCU\\%u%u[%d%u]%u\\%d%d%d%d%d%d$' ) then
+
--P396's format regex: IT\\ICCU\\(\d{10}|\D\D[\D\d]\D\\\d{6}) (e.g. IT\ICCU\CFIV\000163)
 +
if not string.match( id, '^IT\\ICCU\\%d%d%d%d%d%d%d%d%d%d$' ) and
 +
  not string.match( id, '^IT\\ICCU\\%u%u[%u%d]%u\\%d%d%d%d%d%d$' ) then --legacy: %u used here instead of %D (but the faulty ID cat is empty, out of ~12k uses)
 
return false
 
return false
 
end
 
end
return '[http://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid=' .. id .. ' ' .. id .. ']' .. getCatForId( 'SBN' )
+
return '[https://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid='..id..' '..id..']'..p.getCatForId( 'SBN' )
 
end
 
end
  
local function nkcLink( id )
+
function p.nkcLink( id )
return '[https://aleph.nkp.cz/F/?func=find-c&local_base=aut&ccl_term=ica=' .. id .. '&CON_LNG=ENG ' .. id .. ']'
+
--P691's format regex: [a-z]{2,4}[0-9]{2,14} (e.g. abcd12345678901234)
 +
if not string.match( id, '^[a-z][a-z][a-z]?[a-z]?%d%d%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
 +
return false
 +
end
 +
return '[https://aleph.nkp.cz/F/?func=find-c&local_base=aut&ccl_term=ica='..id..'&CON_LNG=ENG '..id..']'..p.getCatForId( 'NKC' )
 
end
 
end
  
local function nclLink( id )
+
function p.nclLink( id )
 +
--P1048's format regex: \d+ (e.g. 1081436)
 
if not string.match( id, '^%d+$' ) then
 
if not string.match( id, '^%d+$' ) then
 
return false
 
return false
 
end
 
end
return '[http://aleweb.ncl.edu.tw/F/?func=accref&acc_sequence=' .. id .. '&CON_LNG=ENG ' .. id .. ']'
+
return '[http://aleweb.ncl.edu.tw/F/?func=accref&acc_sequence='..id..'&CON_LNG=ENG '..id..']'..p.getCatForId( 'NCL' ) --no https yet (10/2018)
 
end
 
end
  
local function ndlLink( id )
+
function p.ndlLink( id )
return '[https://id.ndl.go.jp/auth/ndlna/' .. id .. ' ' .. id .. ']'
+
--P349's format regex: 0?\d{8} (e.g. 012345678)
 +
if not string.match( id, '^0?%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[https://id.ndl.go.jp/auth/ndlna/'..id..' '..id..']'..p.getCatForId( 'NDL' )
 
end
 
end
  
local function sudocLink( id )
+
function p.ngvLink( id )
if not string.match( id, '^%d%d%d%d%d%d%d%d[%dxX]$' ) then
+
--P2041's format regex: \d+ (e.g. 12354)
 +
if not string.match( id, '^%d+$' ) then
 
return false
 
return false
 
end
 
end
return '[https://www.idref.fr/' .. id .. ' ' .. id .. ']'
+
return '[https://www.ngv.vic.gov.au/explore/collection/artist/'..id..'/ '..id..']'..p.getCatForId( 'NGV' )
 
end
 
end
  
local function hlsLink( id )
+
function p.sudocLink( id )
if not string.match( id, '^%d+$' ) then
+
--P269's format regex: (\d{8}[\dX]|) (e.g. 026927608)
 +
if not string.match( id, '^%d%d%d%d%d%d%d%d[%dxX]$' ) then --legacy: allow lowercase 'x'
 +
return false
 +
end
 +
return '[https://www.idref.fr/'..id..' '..id..']'..p.getCatForId( 'SUDOC' )
 +
end
 +
 
 +
function p.hdsLink( id )
 +
--P902's format regex: 50\d{3}|[1-4]\d{4}|[1-9]\d{0,3}| (e.g. 50123)
 +
if not string.match( id, '^50%d%d%d$' ) and
 +
  not string.match( id, '^[1-4]%d%d%d%d$' ) and
 +
  not string.match( id, '^[1-9]%d?%d?%d?$' ) then
 
return false
 
return false
 
end
 
end
return '[http://www.hls-dhs-dss.ch/textes/f/F' .. id .. '.php ' .. id .. ']'
+
return '[http://www.hls-dhs-dss.ch/textes/f/F'..id..'.php '..id..']'..p.getCatForId( 'HDS' ) --no https yet (10/2018)
 
end
 
end
  
local function lirLink( id )
+
function p.lirLink( id )
 +
--P886's format regex: \d+ (e.g. 1)
 
if not string.match( id, '^%d+$' ) then
 
if not string.match( id, '^%d+$' ) then
 
return false
 
return false
 
end
 
end
return '[http://www.e-lir.ch/e-LIR___Lexicon.' .. id .. '.450.0.html ' .. id .. ']'
+
return '[http://www.e-lir.ch/e-LIR___Lexicon.'..id..'.450.0.html '..id..']'..p.getCatForId( 'LIR' ) --no https yet (10/2018)
 
end
 
end
  
local function splitLccn( id )
+
function p.splitLccn( id )
 +
--P244's format regex: (n|nb|nr|no|ns|sh)([4-9][0-9]|00|20[0-1][0-9])[0-9]{6} (e.g. n78039510)
 
if id:match( '^%l%l?%l?%d%d%d%d%d%d%d%d%d?%d?$' ) then
 
if id:match( '^%l%l?%l?%d%d%d%d%d%d%d%d%d?%d?$' ) then
 
id = id:gsub( '^(%l+)(%d+)(%d%d%d%d%d%d)$', '%1/%2/%3' )
 
id = id:gsub( '^(%l+)(%d+)(%d%d%d%d%d%d)$', '%1/%2/%3' )
Line 133: Line 238:
 
end
 
end
  
local function append(str, c, length)
+
function p.append(str, c, length)
 
while str:len() < length do
 
while str:len() < length do
 
str = c .. str
 
str = c .. str
Line 140: Line 245:
 
end
 
end
  
local function lccnLink( id )
+
function p.lccnLink( id )
local parts = splitLccn( id )
+
local parts = p.splitLccn( id ) --e.g. n78039510
 
if not parts then
 
if not parts then
 
return false
 
return false
 
end
 
end
 
local lccnType = parts[1] ~= 'sh' and 'names' or 'subjects'
 
local lccnType = parts[1] ~= 'sh' and 'names' or 'subjects'
id = parts[1] .. parts[2] .. append( parts[3], '0', 6 )
+
id = parts[1] .. parts[2] .. p.append( parts[3], '0', 6 )
return '[http://id.loc.gov/authorities/' .. lccnType .. '/' .. id .. ' ' .. id .. ']' .. getCatForId( 'LCCN' )
+
return '[https://id.loc.gov/authorities/'..lccnType..'/'..id..' '..id..']'..p.getCatForId( 'LCCN' )
 +
end
 +
 
 +
function p.mbaLink( id )
 +
--P434's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
 +
if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
 +
return false
 +
end
 +
return '[https://musicbrainz.org/artist/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz' ) --special category name
 +
end
 +
 
 +
function p.mbareaLink( id )
 +
--P982's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
 +
if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
 +
return false
 +
end
 +
return '[https://musicbrainz.org/area/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz area' ) --special category name
 +
end
 +
 
 +
function p.mbiLink( id )
 +
--P1330's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
 +
if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
 +
return false
 +
end
 +
return '[https://musicbrainz.org/instrument/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz instrument' ) --special category name
 +
end
 +
 
 +
function p.mblLink( id )
 +
--P966's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
 +
if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
 +
return false
 +
end
 +
return '[https://musicbrainz.org/label/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz label' ) --special category name
 +
end
 +
 
 +
function p.mbpLink( id )
 +
--P1004's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
 +
if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
 +
return false
 +
end
 +
return '[https://musicbrainz.org/place/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz place' ) --special category name
 +
end
 +
 
 +
function p.mbrgLink( id )
 +
--P436's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
 +
if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
 +
return false
 +
end
 +
return '[https://musicbrainz.org/release-group/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz release group' ) --special category name
 +
end
 +
 
 +
function p.mbsLink( id )
 +
--P1407's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
 +
if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
 +
return false
 +
end
 +
return '[https://musicbrainz.org/series/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz series' ) --special category name
 
end
 
end
  
local function mbLink( id )
+
function p.mbwLink( id )
-- TODO Implement some sanity checking regex
+
--P435's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
return '[https://musicbrainz.org/artist/' .. id .. ' ' .. id .. ']' .. getCatForId( 'MusicBrainz' )
+
if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
 +
return false
 +
end
 +
return '[https://musicbrainz.org/work/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz work' ) --special category name
 
end
 
end
  
--Returns the ISNI check digit isni must be a string where the 15 first elements are digits
+
--Returns the ISNI check digit isni must be a string where the 15 first elements are digits, e.g. 0000000066534145
local function getIsniCheckDigit( isni )
+
function p.getIsniCheckDigit( isni )
 
local total = 0
 
local total = 0
 
for i = 1, 15 do
 
for i = 1, 15 do
Line 172: Line 336:
 
--Validate ISNI (and ORCID) and retuns it as a 16 characters string or returns false if it's invalid
 
--Validate ISNI (and ORCID) and retuns 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
 
--See http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
local function validateIsni( id )
+
function p.validateIsni( id )
 +
--P213 (ISNI) format regex: [0-9]{4} [0-9]{4} [0-9]{4} [0-9]{3}[0-9X] (e.g. 0000-0000-6653-4145)
 +
--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 = id:gsub( '[ %-]', '' ):upper()
 
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
 
if not id:match( '^%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d[%dX]$' ) then
 
return false
 
return false
 
end
 
end
if getIsniCheckDigit( id ) ~= string.char( id:byte( 16 ) ) then
+
if p.getIsniCheckDigit( id ) ~= string.char( id:byte( 16 ) ) then
 
return false
 
return false
 
end
 
end
Line 183: Line 349:
 
end
 
end
  
local function isniLink( id )
+
function p.isniLink( id )
id = validateIsni( id )
+
id = p.validateIsni( id ) --e.g. 0000-0000-6653-4145
 
if not id then
 
if not id then
 
return false
 
return false
 
end
 
end
return '[http://isni.org/isni/' .. id .. ' ' .. id:sub( 1, 4 ) .. ' ' .. id:sub( 5, 8 ) .. ' ' .. id:sub( 9, 12 ) .. ' ' .. id:sub( 13, 16 ) .. ']' .. getCatForId( 'ISNI' )
+
return '[http://isni.org/isni/'..id..' '..id:sub( 1, 4 )..' '..id:sub( 5, 8 )..' '..id:sub( 9, 12 )..' '..id:sub( 13, 16 )..']'..p.getCatForId( 'ISNI' ) --no https yet (10/2018)
 
end
 
end
  
local function orcidLink( id )
+
function p.orcidLink( id )
id = validateIsni( id )
+
id = p.validateIsni( id ) --e.g. 0000-0002-7398-5483
 
if not id then
 
if not id then
 
return false
 
return false
 
end
 
end
id = id:sub( 1, 4 ) .. '-' .. id:sub( 5, 8 ) .. '-' .. id:sub( 9, 12 ) .. '-' .. id:sub( 13, 16 )
+
id = id:sub( 1, 4 )..'-'..id:sub( 5, 8 )..'-'..id:sub( 9, 12 )..'-'..id:sub( 13, 16 )
return '[https://orcid.org/' .. id .. ' ' .. id .. ']' .. getCatForId( 'ORCID' )
+
return '[https://orcid.org/'..id..' '..id..']'..p.getCatForId( 'ORCID' )
 
end
 
end
  
local function gndLink( id )
+
function p.gndLink( id )
return '[https://d-nb.info/gnd/' .. id .. ' ' .. id .. ']' .. getCatForId( 'GND' )
+
--P227's format regex: (1|1[01])\d{7}[0-9X]|[47]\d{6}-\d|[1-9]\d{0,7}-[0-9X]|3\d{7}[0-9X] (e.g. 4079154-3)
 +
if not string.match( id, '^1[01]?%d%d%d%d%d%d%d[0-9X]$' ) and
 +
  not string.match( id, '^[47]%d%d%d%d%d%d%-%d$' ) and
 +
  not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?%-[0-9X]$' ) and
 +
  not string.match( id, '^3%d%d%d%d%d%d%d[0-9X]$' ) then
 +
return false
 +
end
 +
return '[https://d-nb.info/gnd/'..id..' '..id..']'..p.getCatForId( 'GND' )
 
end
 
end
  
local function selibrLink( id )
+
function p.selibrLink( id )
if not string.match( id, '^%d+$' ) then
+
--P906's format regex: [1-9]\d{4,5} (e.g. 123456)
 +
if not string.match( id, '^[1-9]%d%d%d%d%d?$' ) then
 
return false
 
return false
 
end
 
end
return '[https://libris.kb.se/auth/' .. id .. ' ' .. id .. ']' .. getCatForId( 'SELIBR' )
+
return '[https://libris.kb.se/auth/'..id..' '..id..']'..p.getCatForId( 'SELIBR' )
 
end
 
end
  
local function bnfLink( id )
+
function p.bnfLink( id )
 +
--P268's format regex: \d{8}[0-9bcdfghjkmnpqrstvwxz] (e.g. 123456789)
 +
if not string.match( id, '^c?b?%d%d%d%d%d%d%d%d[0-9bcdfghjkmnpqrstvwxz]$' ) then
 +
return false
 +
end
 
--Add cb prefix if it has been removed
 
--Add cb prefix if it has been removed
 
if not string.match( id, '^cb.+$' ) then
 
if not string.match( id, '^cb.+$' ) then
id = 'cb' .. id
+
id = 'cb'..id
 +
end
 +
return '[https://catalogue.bnf.fr/ark:/12148/'..id..' '..id..'] [https://data.bnf.fr/ark:/12148/'..id..' (data)]'..p.getCatForId( 'BNF' )
 +
end
 +
 
 +
function p.bpnLink( id )
 +
--P651's format regex: \d{8} (e.g. 12345678)
 +
if not string.match( id, '^%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[http://www.biografischportaal.nl/en/persoon/'..id..' '..id..']'..p.getCatForId( 'BPN' ) --no https yet (10/2018)
 +
end
 +
 
 +
function p.ridLink( id )
 +
--P1053's format regex: [A-Z]-\d{4}-(19|20)\d\d (e.g. A-1234-1934)
 +
if not string.match( id, '^[A-Z]%-%d%d%d%d%-19%d%d$' ) and
 +
  not string.match( id, '^[A-Z]%-%d%d%d%d%-20%d%d$' ) then
 +
return false
 +
end
 +
return '[https://www.researcherid.com/rid/'..id..' '..id..']'..p.getCatForId( 'RID' )
 +
end
 +
 
 +
function p.bibsysLink( id )
 +
--P1015's format regex: [1-9]\d* or [1-9](\d{0,8}|\d{12}) (e.g. 1234567890123)
 +
--TODO: follow up @ [[d:Property talk:P1015#Discrepancy between the 2 regex constraints]] or escalate/investigate
 +
if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) and
 +
  not string.match( id, '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[https://authority.bibsys.no/authority/rest/authorities/html/'..id..' '..id..']'..p.getCatForId( 'BIBSYS' )
 +
end
 +
 
 +
function p.ulanLink( id )
 +
--P245's format regex: 500\d{6} (e.g. 500123456)
 +
if not string.match( id, '^500%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[https://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid='..id..' '..id..']'..p.getCatForId( 'ULAN' )
 +
end
 +
 
 +
function p.nlaLink( id )
 +
--P409's format regex: [1-9][0-9]{0,11} (e.g. 123456789012)
 +
if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
 +
return false
 +
end
 +
return '[https://nla.gov.au/anbd.aut-an'..id..' '..id..']'..p.getCatForId( 'NLA' )
 +
end
 +
 
 +
function p.nlapersonLink( id )
 +
--P1315's format regex: [1-9]\d{5,7} (e.g. 12345678)
 +
if not string.match( id, '^[1-9]%d%d%d%d%d%d?%d?$' ) then
 +
return false
 +
end
 +
return '[https://trove.nla.gov.au/people/'..id..' '..id..']'..p.getCatForId( 'NLA-person' )
 +
end
 +
 
 +
function p.rkdartistsLink( id )
 +
--P650's format regex: [1-9]\d{0,5} (e.g. 123456)
 +
if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?$' ) then
 +
return false
 +
end
 +
return '[https://rkd.nl/en/explore/artists/'..id..' '..id..']'..p.getCatForId( 'RKDartists' )
 +
end
 +
 
 +
function p.snacLink( id )
 +
--P3430's format regex: \d*[A-Za-z][0-9A-Za-z]* (e.g. A)
 +
if not string.match( id, '^%d*[A-Za-z][0-9A-Za-z]*$' ) then
 +
return false
 +
end
 +
return '[http://socialarchive.iath.virginia.edu/ark:/99166/'..id..' '..id..']'..p.getCatForId( 'SNAC-ID' ) --no https yet (10/2018)
 +
end
 +
 
 +
function p.dblpLink( id )
 +
--P2456's format regex: \d{2,3} /\d+(-\d+)?|[a-z] /[a-zA-Z][0-9A-Za-z]*(-\d+)? (e.g. 123/123)
 +
if not string.match( id, '^%d%d%d?/%d+$' ) and
 +
  not string.match( id, '^%d%d%d?/%d+%-%d+$' ) and
 +
  not string.match( id, '^[a-z]/[a-zA-Z][0-9A-Za-z]*$' ) and
 +
  not string.match( id, '^[a-z]/[a-zA-Z][0-9A-Za-z]*%-%d+$' ) then
 +
return false
 +
end
 +
return '[https://dblp.org/pid/'..id..' '..id..']'..p.getCatForId( 'DBLP' )
 +
end
 +
 
 +
function p.aagLink( id )
 +
--P3372's format regex: \d+ (e.g. 1)
 +
if not string.match( id, '^%d+$' ) then
 +
return false
 +
end
 +
return '[http://www.aucklandartgallery.com/explore-art-and-ideas/artist/'..id..'/ '..id..']'..p.getCatForId( 'AAG' )
 +
end
 +
 
 +
function p.acmLink( id )
 +
--P864's format regex: \d{11} (e.g. 12345678901)
 +
if not string.match( id, '^%d%d%d%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[https://dl.acm.org/author_page.cfm?id='..id..' '..id..']'..p.getCatForId( 'ACM-DL' )
 +
end
 +
 
 +
function p.adbLink( id )
 +
--P1907's format regex: [a-z][-a-z]+-([1-2]\d|[1-9])\d{0,3} (e.g. barton-sir-edmund-toby-71)
 +
if not string.match( id, '^[a-z][-a-z]+-[1-2]%d%d?%d?%d?$' ) and
 +
  not string.match( id, '^[a-z][-a-z]+-[1-9]%d?%d?%d?$' ) then
 +
return false
 +
end
 +
return '[http://adb.anu.edu.au/biography/'..id..' '..id..']'..p.getCatForId( 'ADB' )
 +
end
 +
 
 +
function p.agsaLink( id )
 +
--P6804's format regex: [1-9]\d* (e.g. 3625)
 +
if not string.match( id, '^[1-9]%d*$' ) then
 +
return false
 +
end
 +
return '[https://www.agsa.sa.gov.au/collection-publications/collection/creators/_/'..id..'/ '..id..']'..p.getCatForId( 'AGSA' )
 +
end
 +
 
 +
function p.autoresuyLink( id )
 +
--P2558's format regex: [1-9]\d{0,4} (e.g. 12345)
 +
if not string.match( id, '^[1-9]%d?%d?%d?%d?$' ) then
 +
return false
 
end
 
end
 +
return '[https://autores.uy/autor/'..id..' '..id..']'..p.getCatForId( 'autores.uy' )
 +
end
  
return '[http://catalogue.bnf.fr/ark:/12148/' .. id .. ' ' .. id .. '] [http://data.bnf.fr/ark:/12148/' .. id .. ' (data)]' .. getCatForId( 'BNF' )
+
function p.awrLink( id )
 +
--P4186's format regex: (([A-Z]{3}\d{4})|([A-Z]{2}\d{5}))[a-z] (e.g. PR00768b)
 +
if not string.match( id, '^[A-Z][A-Z][A-Z]%d%d%d%d[a-z]$' ) and
 +
  not string.match( id, '^[A-Z][A-Z]%d%d%d%d%d[a-z]$' ) then
 +
return false
 +
end
 +
return '[http://www.womenaustralia.info/biogs/'..id..'.htm '..id..']'..p.getCatForId( 'AWR' )
 
end
 
end
  
local function bpnLink( id )
+
function p.picLink( id )
 +
--P2750's format regex: [1-9]\d* (e.g. 1)
 +
if not string.match( id, '^[1-9]%d*$' ) then
 +
return false
 +
end
 +
return '[https://pic.nypl.org/constituents/'..id..' '..id..']'..p.getCatForId( 'PIC' )
 +
end
 +
 
 +
function p.bildLink( id )
 +
--P2092's format regex: \d+ (e.g. 1)
 
if not string.match( id, '^%d+$' ) then
 
if not string.match( id, '^%d+$' ) then
 
return false
 
return false
 
end
 
end
return '[http://www.biografischportaal.nl/en/persoon/' .. id .. ' ' .. id .. ']' .. getCatForId( 'BPN' )
+
return '[https://www.bildindex.de/document/obj'..id..' '..id..']'..p.getCatForId( 'Bildindex' )
 
end
 
end
  
local function ridLink( id )
+
function p.jocondeLink( id )
return '[https://www.researcherid.com/rid/' .. id .. ' ' .. id .. ']' .. getCatForId( 'RID' )
+
--P347's format regex: [\-0-9A-Za-z]{11} (e.g. 12345678901)
 +
local regex = '^'..string.rep('[%-0-9A-Za-z]', 11)..'$'
 +
if not string.match( id, regex ) then
 +
return false
 +
end
 +
return '[http://www2.culture.gouv.fr/public/mistral/joconde_fr?ACTION=CHERCHER&FIELD_1=REF&VALUE_1='..id..' '..id..']'..p.getCatForId( 'Joconde' ) --no https yet (10/2018)
 
end
 
end
  
local function bibsysLink( id )
+
function p.rkdidLink( id )
return '[https://authority.bibsys.no/authority/rest/authorities/html/' .. id .. ' ' .. id .. ']' .. getCatForId( 'BIBSYS' )
+
--P350's format regex: [1-9]\d{0,5} (e.g. 123456)
 +
if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?$' ) then
 +
return false
 +
end
 +
return '[https://rkd.nl/nl/explore/images/'..id..' '..id..']'..p.getCatForId( 'RKDID' )
 
end
 
end
  
local function ulanLink( id )
+
function p.balatLink( id )
return '[https://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid=' .. id .. ' ' .. id .. ']' .. getCatForId( 'ULAN' )
+
--P3293's format regex: \d+ (e.g. 1)
 +
if not string.match( id, '^%d+$' ) then
 +
return false
 +
end
 +
return '[http://balat.kikirpa.be/object/104257'..id..' '..id..']'..p.getCatForId( 'BALaT' ) --no https yet (10/2018)
 +
end
 +
 
 +
function p.lnbLink( id )
 +
--P1368's format regex: \d{9} (e.g. 123456789)
 +
if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[https://kopkatalogs.lv/F?func=direct&local_base=lnc10&doc_number='..id..'&P_CON_LNG=ENG '..id..']'..p.getCatForId( 'LNB' )
 +
end
 +
 
 +
function p.nskLink( id )
 +
--P1375's format regex: \d{9} (e.g. 123456789)
 +
if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[http://katalog.nsk.hr/F/?func=direct&doc_number='..id..'&local_base=nsk10 '..id..']'..p.getCatForId( 'NSK' ) --no https yet (10/2018)
 +
end
 +
 
 +
function p.iciaLink( id )
 +
--P1736's format regex: \d+ (e.g. 1)
 +
if not string.match( id, '^%d+$' ) then
 +
return false
 +
end
 +
return '[https://www.imj.org.il/artcenter/newsite/en/?artist='..id..' '..id..']'..p.getCatForId( 'ICIA' )
 +
end
 +
 
 +
function p.ta98Link( id )
 +
--P1323's format regex: A\d{2}\.\d\.\d{2}\.\d{3}[FM]? (e.g. A12.3.45.678)
 +
if not string.match( id, '^A%d%d%.%d%.%d%d%.%d%d%d[FM]?$' ) then
 +
return false
 +
end
 +
return '[http://tools.wmflabs.org/wikidata-externalid-url/?p=1323&url_prefix=https:%2F%2Fwww.unifr.ch%2Fifaa%2FPublic%2FEntryPage%2FTA98%20Tree%2FEntity%20TA98%20EN%2F&url_suffix=%20Entity%20TA98%20EN.htm&id='..id..' '..id..']'..p.getCatForId( 'TA98' )
 +
end
 +
 
 +
function p.teLink( id )
 +
--P1693's format regex: E[1-8]\.\d{1,2}\.\d{1,2}\.\d{1,2}\.\d{1}\.\d{1}\.\d{1,3} (e.g. E1.23.45.67.8.9.0)
 +
local e1, e2 = string.match( id, '^E([1-8])%.(%d%d?)%.%d%d?%.%d%d?%.%d%.%d%.%d%d?%d?$' )
 +
if not e1 then
 +
return false
 +
end
 +
local TEnum = 'TEe0'..e1 --no formatter URL in WD, probably due to this complexity
 +
if e1 == '5' or e1 == '7' then
 +
if #e2 == 1 then e2 = '0'..e2 end
 +
TEnum = TEnum..e2
 +
end
 +
return '[http://www.unifr.ch/ifaa/Public/EntryPage/ViewTE/'..TEnum..'.html '..id..']'..p.getCatForId( 'TE' )
 +
end
 +
 
 +
function p.tepapaLink( id )
 +
--P3544's format regex: \d+ (e.g. 1)
 +
if not string.match( id, '^%d+$' ) then
 +
return false
 +
end
 +
return '[https://collections.tepapa.govt.nz/agent/'..id..' '..id..']'..p.getCatForId( 'TePapa' )
 +
end
 +
 
 +
function p.thLink( id )
 +
--P1694's format regex: H\d\.\d{2}\.\d{2}\.\d\.\d{5} (e.g. H1.23.45.6.78901)
 +
local h1, h2 = string.match( id, '^H(%d)%.(%d%d)%.%d%d%.%d%.%d%d%d%d%d$' )
 +
if not h1 then
 +
return false
 +
end
 +
local THnum = 'THh'..h1..h2 --no formatter URL in WD, probably due to this complexity
 +
return '[http://www.unifr.ch/ifaa/Public/EntryPage/ViewTH/'..THnum..'.html '..id..']'..p.getCatForId( 'TH' )
 +
end
 +
 
 +
function p.dsiLink( id )
 +
--P2349's format regex: [1-9]\d* (e.g. 1538)
 +
if not string.match( id, '^[1-9]%d*$' ) then
 +
return false
 +
end
 +
return '[http://www.uni-stuttgart.de/hi/gnt/dsi2/index.php?table_name=dsi&function=details&where_field=id&where_value='..id..' '..id..']'..p.getCatForId( 'DSI' )
 
end
 
end
  
local function nlaLink( id )
+
function p.s2authoridLink( id )
return '[https://nla.gov.au/anbd.aut-an' .. id .. ' ' .. id .. ']' .. getCatForId( 'NLA' )
+
--P4012's format regex: [1-9]\d* (e.g. 1796130)
 +
if not string.match( id, '^[1-9]%d*$' ) then
 +
return false
 +
end
 +
return '[https://www.semanticscholar.org/author/'..id..' '..id..']'..p.getCatForId( 'Semantic Scholar author' ) --special category name
 
end
 
end
  
local function rkdartistsLink( id )
+
function p.nliLink( id )
return '[https://rkd.nl/en/explore/artists/' .. id .. ' ' .. id .. ']' .. getCatForId( 'RKDartists' )
+
--P949's format regex: \d{9} (e.g. 123456789)
 +
if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[http://aleph.nli.org.il/F/?func=find-b&local_base=NNL10&find_code=SYS&con_lng=eng&request='..id..' '..id..']'..p.getCatForId( 'NLI' )
 
end
 
end
  
local function snacLink( id )
+
function p.nlpLink( id )
return '[http://socialarchive.iath.virginia.edu/ark:/99166/' .. id .. ' ' .. id .. ']' .. getCatForId( 'SNAC-ID' )
+
--P1695's format regex: 9810[0-9]\d* or A[0-9]{7}[0-9X] (e.g. 9810123456789012345 or A10414836)
 +
if not string.match( id, '^9810%d+$' ) and
 +
  not string.match( id, '^A%d%d%d%d%d%d%d[%dX]$' ) then
 +
return false
 +
end
 +
return '[https://tools.wmflabs.org/wikidata-externalid-url?p=1695&id='..id..' '..id..']'..p.getCatForId( 'NLP' )
 
end
 
end
  
local function dblpLink( id )
+
function p.reroLink( id )
return '[https://dblp.org/pid/' .. id .. ' ' .. id .. ']' .. getCatForId( 'DBLP' )
+
--P3065's format regex: 0[1-2]-[A-Z0-9]{1,10} (e.g. 02-A012345678)
 +
if not string.match( id, '^0[1-2]%-[A-Z%d][A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?$' ) then
 +
return false
 +
end
 +
return '[http://data.rero.ch/'..id..' '..id..']'..p.getCatForId( 'RERO' )
 
end
 
end
  
local function acmLink( id )
+
function p.nlrLink( id )
return '[https://dl.acm.org/author_page.cfm?id=' .. id .. ' ' .. id .. ']' .. getCatForId( 'ACM-DL' )
+
--P1003's format regex: \d{9} (e.g. 123456789)
 +
if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[http://alephnew.bibnat.ro:8991/F?func=find-b&request='..id..'&find_code=SYS&adjacent=Y&local_base=NLR10 '..id..']'..p.getCatForId( 'NLR' )
 
end
 
end
  
local function autoresuyLink( id )
+
function p.ntaLink( id )
return '[http://autores.uy/autor/' .. id .. ' ' .. id .. ']' .. getCatForId( 'autores.uy' )
+
--P1006's format regex: \d{8}[\dX] (e.g. 12345678X)
 +
if not string.match( id, '^%d%d%d%d%d%d%d%d[%dX]$' ) then
 +
return false
 +
end
 +
return '[http://data.bibliotheken.nl/id/thes/p'..id..' '..id..']'..p.getCatForId( 'NTA' )
 
end
 
end
  
local function picLink( id )
+
function p.bncLink( id )
return '[https://pic.nypl.org/constituents/' .. id .. ' ' .. id .. ']' .. getCatForId( 'PIC' )
+
--P1890's format regex: \d{9} (e.g. 123456789)
 +
if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[http://www.bncatalogo.cl/F?func=direct&local_base=red10&doc_number='..id..' '..id..']'..p.getCatForId( 'BNC' )
 
end
 
end
  
local function getIdsFromWikidata( itemId, property )
+
--[[==========================================================================]]
 +
--[[          Wikidata, navigation bar, and documentation functions          ]]
 +
--[[==========================================================================]]
 +
 
 +
function p.getIdsFromWikidata( itemId, property )
 
local ids = {}
 
local ids = {}
 
local statements = mw.wikibase.getBestStatements( itemId, property )
 
local statements = mw.wikibase.getBestStatements( itemId, property )
Line 280: Line 712:
 
end
 
end
  
local function matchesWikidataRequirements( itemId, reqs )
+
function p.matchesWikidataRequirements( itemId, reqs )
 
for _, group in ipairs( reqs ) do
 
for _, group in ipairs( reqs ) do
local property = 'P' .. group[1]
+
local property = 'P'..group[1]
 
local qid = group[2]
 
local qid = group[2]
 
local statements = mw.wikibase.getBestStatements( itemId, property )
 
local statements = mw.wikibase.getBestStatements( itemId, property )
if statements ~= nil then
+
if statements then
 
for _, statement in ipairs( statements ) do
 
for _, statement in ipairs( statements ) do
if statement.mainsnak.datavalue ~= nil then
+
if statement.mainsnak.datavalue then
 
if statement.mainsnak.datavalue.value['numeric-id'] == qid then
 
if statement.mainsnak.datavalue.value['numeric-id'] == qid then
 
return true
 
return true
end
+
end end end end end
end
 
end
 
end
 
end
 
 
return false
 
return false
 
end
 
end
  
local function createRow( id, label, rawValue, link, withUid )
+
function p.createRow( id, label, rawValue, link, withUid, specialCat )
 
if link then
 
if link then
 
if withUid then
 
if withUid then
return '*<span class="nowrap">' .. label .. ' <span class="uid">' .. link .. '</span></span>\n'
+
return '*<span class="nowrap">'..label..' <span class="uid">'..link..'</span></span>\n'
else
 
return '*<span class="nowrap">' .. label .. ' ' .. link .. '</span>\n'
 
 
end
 
end
else
+
return '*<span class="nowrap">'..label..' '..link..'</span>\n'
return '* <span class="error">The ' .. id .. ' id ' .. rawValue .. ' is not valid.</span>[[Category:Wikipedia articles with faulty authority control identifiers (' .. id .. ')]]\n'
+
end
 +
 
 +
local catName = 'Wikipedia articles with faulty '..(specialCat or id)..' identifiers'
 +
return '* <span class="error">The '..id..' id '..rawValue..' is not valid.</span>[[Category:'..catName..']]'..p.redCatLink(catName)..'\n'
 +
end
 +
 
 +
-- Creates a human-readable standalone wikitable version of p.conf, and tracking categories with page counts, for use in the documentation
 +
function p.docConfTable( frame )
 +
local wikiTable = '{| class="wikitable sortable"\n'..
 +
  '! rowspan=2 | Parameter\n'..
 +
  '! rowspan=2 | Label\n'..
 +
  '! rowspan=2; data-sort-type=number | Wikidata property\n'..
 +
  '! colspan=4 | Tracking categories and page counts\n'..
 +
  '|-\n'..
 +
  '! Articles\n'..
 +
  '! User pages\n'..
 +
  '! Misc. pages\n'..
 +
  '! Faulty IDs\n'..
 +
  '|-\n'
 +
 +
local lang = mw.getContentLanguage()
 +
for _, conf in pairs( p.conf ) do
 +
local param, link, pid = conf[1], conf[2], conf[3]
 +
local category = conf.category or param
 +
local args = { id = 'f', pid }
 +
local wpl = frame:expandTemplate{ title = 'Wikidata property link', args = args }
 +
--cats
 +
local articleCat = 'Wikipedia articles with '..category..' identifiers'
 +
local userCat =    'User pages with '..category..' identifiers'
 +
local miscCat =    'Miscellaneous pages with '..category..' identifiers'
 +
local faultyCat =  'Wikipedia articles with faulty '..category..' identifiers'
 +
--counts
 +
local articleCount = lang:formatNum( mw.site.stats.pagesInCategory(articleCat, 'pages') )
 +
local userCount =    lang:formatNum( mw.site.stats.pagesInCategory(userCat, 'pages') )
 +
local miscCount =    lang:formatNum( mw.site.stats.pagesInCategory(miscCat, 'pages') )
 +
local faultyCount =  lang:formatNum( mw.site.stats.pagesInCategory(faultyCat, 'pages') )
 +
--concat
 +
wikiTable = wikiTable..'\n'..
 +
'|-\n'..
 +
'||'..param..
 +
'||'..link..
 +
'||data-sort-value='..pid..'|'..wpl..
 +
'||style="text-align: right;"|[[:Category:'..articleCat..'|'..articleCount..']]'..
 +
'||style="text-align: right;"|[[:Category:'..  userCat..'|'..  userCount..']]'..
 +
'||style="text-align: right;"|[[:Category:'..  miscCat..'|'..  miscCount..']]'..
 +
'||style="text-align: right;"|[[:Category:'.. faultyCat..'|'.. faultyCount..']]'
 
end
 
end
 +
return wikiTable..'\n|}'
 
end
 
end
  
--In this order: name of the parameter, label, propertyId in Wikidata, formatting function
+
--[[==========================================================================]]
local conf = {
+
--[[                             Configuration                              ]]
{ 'VIAF', '[[Virtual International Authority File|VIAF]]', 214, viafLink },
+
--[[==========================================================================]]
{ 'LCCN', '[[Library of Congress Control Number|LCCN]]', 244, lccnLink },
 
{ 'ISNI', '[[International Standard Name Identifier|ISNI]]', 213, isniLink },
 
{ 'ORCID', '[[ORCID]]', 496, orcidLink },
 
{ 'GND', '[[Integrated Authority File|GND]]', 227, gndLink },
 
{ 'SELIBR', '[[LIBRIS|SELIBR]]', 906, selibrLink },
 
{ 'SUDOC', '[[Système universitaire de documentation|SUDOC]]', 269, sudocLink },
 
{ 'BNF', '[[Bibliothèque nationale de France|BNF]]', 268, bnfLink },
 
{ 'BPN', '[[Biografisch Portaal|BPN]]', 651, bpnLink },
 
{ 'RID', '[[ResearcherID]]', 1053, ridLink },
 
{ 'BIBSYS', '[[BIBSYS]]', 1015, bibsysLink },
 
{ 'ULAN', '[[Union List of Artist Names|ULAN]]', 245, ulanLink },
 
{ 'HDS', '[[Historical Dictionary of Switzerland|HDS]]', 902, hlsLink },
 
{ 'LIR', '[[Historical Dictionary of Switzerland#Lexicon_Istoric_Retic|LIR]]', 886, lirLink },
 
{ 'MBA', '[[MusicBrainz]]', 434, mbLink },
 
{ 'MGP', '[[Mathematics Genealogy Project|MGP]]', 549, mgpLink },
 
{ 'NLA', '[[National Library of Australia|NLA]]', 409, nlaLink },
 
{ 'NDL', '[[National Diet Library|NDL]]', 349, ndlLink },
 
{ 'NCL', '[[National Central Library|NCL]]', 1048, nclLink },
 
{ 'NKC', '[[National Library of the Czech Republic|NKC]]', 691, nkcLink },
 
{ 'Léonore', '[[Base Léonore|Léonore]]', 640, leonoreLink },
 
{ 'SBN', '[[Istituto Centrale per il Catalogo Unico|ICCU]]', 396, sbnLink },
 
{ 'RLS', '[[Russian State Library|RLS]]', 947, rslLink },
 
{ 'Botanist', '[[Author citation (botany)|Botanist]]', 428, botanistLink },
 
{ 'NARA-person', '[[National Archives and Records Administration|NARA]]', 1222, narapersonLink },
 
{ 'NARA-organization', '[[National Archives and Records Administration|NARA]]', 1223, naraorganizationLink },
 
{ 'USCongress', '[[Biographical Directory of the United States Congress|US Congress]]', 1157, uscongressLink },
 
{ 'BNE', '[[Biblioteca Nacional de España|BNE]]', 950, bneLink },
 
{ 'CINII', '[[CiNii]]', 271, ciniiLink },
 
{ 'TLS', '[[Theaterlexikon der Schweiz|TLS]]', 1362, tlsLink },
 
{ 'SIKART', '[[SIKART]]', 781, sikartLink },
 
{ 'KULTURNAV', '[[KulturNav]]', 1248, kulturnavLink },
 
{ 'RKDartists', '[[Netherlands Institute for Art History#Online artist pages|RKD]]', 650, rkdartistsLink },
 
{ 'SNAC-ID', '[[SNAC]]', 3430, snacLink },
 
{ 'DBLP', '[[DBLP]]', 2456, dblpLink },
 
{ 'ACM-DL', '[[ACM Digital Library|ACM DL]]', 864, acmLink },
 
{ 'autores.uy', '[[autores.uy]]', 2558, autoresuyLink },
 
{ 'PIC', '[[:d:Q23892012|PIC]]', 2750, picLink },
 
}
 
  
 
-- Check that the Wikidata item has this property-->value before adding it
 
-- Check that the Wikidata item has this property-->value before adding it
 
local reqs = {}
 
local reqs = {}
  
local p = {}
+
-- Parameter format: { parameter name, label, propertyId # in Wikidata, formatting/validation function }
 +
p.conf = {
 +
{ 'AAG', '[[Auckland Art Gallery Toi o Tāmaki|AAG]]', 3372, p.aagLink },
 +
{ 'ACM-DL', '[[ACM Digital Library|ACM DL]]', 864, p.acmLink },
 +
{ 'ADB', '[[Australian Dictionary of Biography|ADB]]', 1907, p.adbLink },
 +
{ 'AGSA', '[[Art Gallery of South Australia|AGSA]]', 6804, p.agsaLink },
 +
{ 'autores.uy', '[[autores.uy]]', 2558, p.autoresuyLink },
 +
{ 'AWR', '[[Australian Women\'s Register|AWR]]', 4186, p.awrLink },
 +
{ 'BALaT', '[[Royal Institute for Cultural Heritage#Online artworks pages|BALaT]]', 3293, p.balatLink },
 +
{ 'BIBSYS', '[[Bibsys|BIBSYS]]', 1015, p.bibsysLink },
 +
{ 'Bildindex', '[[Marburg Picture Index|Bildindex]]', 2092, p.bildLink },
 +
{ 'BNC', '[[Biblioteca Nacional de Chile|BNC]]', 1890, p.bncLink }, --initially commented due to excessive WD ID errors (many bad IDs since removed)
 +
{ 'BNE', '[[Biblioteca Nacional de España|BNE]]', 950, p.bneLink },
 +
{ 'BNF', '[[Bibliothèque nationale de France|BNF]]', 268, p.bnfLink },
 +
{ 'Botanist', '[[Author citation (botany)|Botanist]]', 428, p.botanistLink },
 +
{ 'BPN', '[[Biografisch Portaal|BPN]]', 651, p.bpnLink },
 +
{ 'CINII', '[[CiNii]]', 271, p.ciniiLink },
 +
{ 'DAAO', '[[Dictionary of Australian Artists|DAAO]]', 1707, p.daaoLink },
 +
{ 'DBLP', '[[DBLP]]', 2456, p.dblpLink },
 +
{ 'DSI', '[[Stuttgart Database of Scientific Illustrators 1450–1950|DSI]]', 2349, p.dsiLink },
 +
{ 'GND', '[[Integrated Authority File|GND]]', 227, p.gndLink },
 +
{ 'HDS', '[[Historical Dictionary of Switzerland|HDS]]', 902, p.hdsLink },
 +
{ 'IAAF', '[[International Association of Athletics Federations|IAAF]]', 1146, p.iaafLink },
 +
{ 'ICIA', '[[Information Center for Israeli Art|ICIA]]', 1736, p.iciaLink },
 +
{ 'ISNI', '[[International Standard Name Identifier|ISNI]]', 213, p.isniLink },
 +
{ 'Joconde', '[[Joconde]]' , 347, p.jocondeLink },
 +
{ 'KULTURNAV', '[[KulturNav]]', 1248, p.kulturnavLink },
 +
{ 'LCCN', '[[Library of Congress Control Number|LCCN]]', 244, p.lccnLink },
 +
{ 'LIR', '[[Historical Dictionary of Switzerland#Lexicon_Istoric_Retic|LIR]]', 886, p.lirLink },
 +
{ 'LNB', '[[National Library of Latvia|LNB]]', 1368, p.lnbLink },
 +
{ 'Léonore', '[[Base Léonore|Léonore]]', 640, p.leonoreLink },
 +
{ 'MBA', '[[MusicBrainz]]', 434, p.mbaLink, category = 'MusicBrainz' }, --special category name
 +
{ 'MBAREA', '[[MusicBrainz]]', 982, p.mbareaLink, category = 'MusicBrainz area' }, --special category name
 +
{ 'MBI', '[[MusicBrainz]]', 1330, p.mbiLink, category = 'MusicBrainz instrument' }, --special category name
 +
{ 'MBL', '[[MusicBrainz]]', 966, p.mblLink, category = 'MusicBrainz label' }, --special category name
 +
{ 'MBP', '[[MusicBrainz]]', 1004, p.mbpLink, category = 'MusicBrainz place' }, --special category name
 +
{ 'MBRG', '[[MusicBrainz]] release group', 436, p.mbrgLink, category = 'MusicBrainz release group' }, --special category name
 +
{ 'MBS', '[[MusicBrainz]]', 1407, p.mbsLink, category = 'MusicBrainz series' }, --special category name
 +
{ 'MBW', '[[MusicBrainz]] work', 435, p.mbwLink, category = 'MusicBrainz work' }, --special category name
 +
{ 'MGP', '[[Mathematics Genealogy Project|MGP]]', 549, p.mgpLink },
 +
{ 'NARA', '[[National Archives and Records Administration|NARA]]', 1225, p.naraLink },
 +
{ 'NCL', '[[National Central Library|NCL]]', 1048, p.nclLink },
 +
{ 'NDL', '[[National Diet Library|NDL]]', 349, p.ndlLink },
 +
{ 'NGV', '[[National Gallery of Victoria|NGV]]', 2041, p.ngvLink },
 +
{ 'NKC', '[[National Library of the Czech Republic|NKC]]', 691, p.nkcLink },
 +
{ 'NLA', '[[National Library of Australia|NLA]]', 409, p.nlaLink },
 +
{ 'NLA-person', '[[National Library of Australia|NLA-person]]', 1315, p.nlapersonLink },
 +
{ 'NLI', '[[National Library of Israel|NLI]]', 949, p.nliLink },
 +
{ 'NLP', '[[National Library of Poland|NLP]]', 1695, p.nlpLink },
 +
{ 'NLR', '[[National Library of Romania|NLR]]', 1003, p.nlrLink }, --initially commented due to excessive WD ID errors (conflated with National Library of Russia IDs)
 +
{ 'NSK', '[[National and University Library in Zagreb|NSK]]', 1375, p.nskLink },
 +
{ 'NTA', '[[Royal Library of the Netherlands|NTA]]', 1006, p.ntaLink },
 +
{ 'ORCID', '[[ORCID]]', 496, p.orcidLink },
 +
{ 'PIC', '[[:d:Q23892012|PIC]]', 2750, p.picLink },
 +
{ 'RID', '[[ResearcherID]]', 1053, p.ridLink },
 +
{ 'RERO', '[[RERO (Library Network of Western Switzerland)|RERO]]', 3065, p.reroLink }, --initially commented due to excessive WD ID errors (regex fixed/relaxed)
 +
{ 'RKDartists', '[[Netherlands Institute for Art History#Online artist pages|RKD]]', 650, p.rkdartistsLink },
 +
{ 'RKDID', '[[:d:Q17299580|RKDimages ID]]', 350, p.rkdidLink },
 +
{ 'RSL', '[[Russian State Library|RSL]]', 947, p.rslLink },
 +
{ 'SBN', '[[Istituto Centrale per il Catalogo Unico|ICCU]]', 396, p.sbnLink },
 +
{ 'SELIBR', '[[LIBRIS|SELIBR]]', 906, p.selibrLink },
 +
{ 'SIKART', '[[SIKART]]', 781, p.sikartLink },
 +
{ 'SNAC-ID', '[[SNAC]]', 3430, p.snacLink },
 +
{ 'SUDOC', '[[Système universitaire de documentation|SUDOC]]', 269, p.sudocLink },
 +
{ 'S2AuthorId', '[[Semantic Scholar|S2AuthorId]]', 4012, p.s2authoridLink, category = 'Semantic Scholar author' }, --special category name
 +
{ 'TA98', '[[Terminologia Anatomica|TA98]]', 1323, p.ta98Link },
 +
{ 'TE', '[[Terminologia Embryologica|TE]]', 1693, p.teLink },
 +
{ 'TePapa', '[[Museum of New Zealand Te Papa Tongarewa|TePapa]]', 3544, p.tepapaLink },
 +
{ 'TH', '[[Terminologia Histologica|TH]]', 1694, p.thLink },
 +
{ 'TLS', '[[Theaterlexikon der Schweiz|TLS]]', 1362, p.tlsLink },
 +
{ 'ULAN', '[[Union List of Artist Names|ULAN]]', 245, p.ulanLink },
 +
{ 'USCongress', '[[Biographical Directory of the United States Congress|US Congress]]', 1157, p.uscongressLink },
 +
{ 'VIAF', '[[Virtual International Authority File|VIAF]]', 214, p.viafLink },
 +
}
 +
 
 +
-- Legitimate aliases to p.conf, for convenience
 +
-- Format: { alias, parameter name in p.conf }
 +
p.aliases = {
 +
{ 'RLS', 'RSL' },
 +
{ 'MusicBrainz', 'MBA' },
 +
{ 'MusicBrainz artist', 'MBA' },
 +
{ 'MusicBrainz label', 'MBL' },
 +
{ 'MusicBrainz release group', 'MBRG' },
 +
{ 'MusicBrainz work', 'MBW' },
 +
{ 'Leonore', 'Léonore' },
 +
}
 +
 
 +
-- Deprecated aliases to p.conf, which also get assigned to a tracking cat
 +
-- Format: { deprecated parameter name, replacement parameter name in p.conf }
 +
p.deprecated = {
 +
{ 'GKD', 'GND' },
 +
{ 'PND', 'GND' },
 +
{ 'SWD', 'GND' },
 +
{ 'NARA-organization', 'NARA' },
 +
{ 'NARA-person', 'NARA' },
 +
}
 +
 
 +
--[[==========================================================================]]
 +
--[[                                  Main                                  ]]
 +
--[[==========================================================================]]
  
 
function p.authorityControl( frame )
 
function p.authorityControl( frame )
 +
local resolveEntity = require( "Module:ResolveEntityId" )
 
local parentArgs = frame:getParent().args
 
local parentArgs = frame:getParent().args
--Create rows
+
local elements = {} --create/insert rows later
local elements = {}
+
local worldcatCat = ''
 
+
local suppressedIdCat = ''
--redirect PND to GND
+
local deprecatedIdCat = ''
if (parentArgs.GND == nil or parentArgs.GND == '') and parentArgs.PND ~= nil and parentArgs.PND ~= '' then
+
parentArgs.GND = parentArgs.PND
+
--Redirect aliases to proper parameter names
 +
for _, a in pairs( p.aliases ) do
 +
local alias, param = a[1], a[2]
 +
if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[alias] then
 +
parentArgs[param] = parentArgs[alias]
 +
end
 +
end
 +
 +
--Redirect deprecated parameters to proper parameter names, and assign tracking cat
 +
for _, d in pairs( p.deprecated ) do
 +
local dep, param = d[1], d[2]
 +
if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[dep] then
 +
parentArgs[param] = parentArgs[dep]
 +
if namespace == 0 then
 +
deprecatedIdCat = '[[Category:Wikipedia articles with deprecated authority control identifiers|'..dep..']]'
 +
end
 +
end
 +
end
 +
 +
--Use QID= parameter for testing/example purposes only
 +
local itemId = nil
 +
if namespace ~= 0 then
 +
local qid = parentArgs['qid'] or parentArgs['QID']
 +
if qid then
 +
itemId = 'Q'..mw.ustring.gsub(qid, '^[Qq]', '')
 +
itemId = resolveEntity._id(itemId) --nil if unresolvable
 +
end
 +
else
 +
itemId = mw.wikibase.getEntityIdForCurrentPage()
 
end
 
end
 
+
 
--Wikidata fallback if requested
 
--Wikidata fallback if requested
local itemId = mw.wikibase.getEntityIdForCurrentPage()
+
if itemId then
if itemId ~= nil then
+
for _, params in ipairs( p.conf ) do
for _, params in ipairs( conf ) do
+
if params[3] > 0 then
if params[3] ~= 0 then
 
 
local val = parentArgs[params[1]]
 
local val = parentArgs[params[1]]
if not val or val == '' then
+
if val == nil or val == '' then
 
local canUseWikidata = nil
 
local canUseWikidata = nil
if reqs[params[1]] ~= nil then
+
if reqs[params[1]] then
canUseWikidata = matchesWikidataRequirements( itemId, reqs[params[1]] )
+
canUseWikidata = p.matchesWikidataRequirements( itemId, reqs[params[1]] )
 
else
 
else
 
canUseWikidata = true
 
canUseWikidata = true
 
end
 
end
 
if canUseWikidata then
 
if canUseWikidata then
local wikidataIds = getIdsFromWikidata( itemId, 'P' .. params[3] )
+
local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
 
if wikidataIds[1] then
 
if wikidataIds[1] then
parentArgs[params[1]] = wikidataIds[1]
+
if val == '' and (namespace == 0 or testcases) then
end
+
suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|'..params[1]..']]'
end
+
else
end
+
parentArgs[params[1]] = wikidataIds[1]
end
+
end end end end end end end
end
+
end
 
 
 
--Worldcat
 
if parentArgs['WORLDCATID'] and parentArgs['WORLDCATID'] ~= '' then
 
table.insert( elements, createRow( 'WORLDCATID', '', parentArgs['WORLDCATID'], '[https://www.worldcat.org/identities/' .. parentArgs['WORLDCATID'] .. ' WorldCat Identities]', false ) ) --Validation?
 
elseif parentArgs['VIAF'] and string.match( parentArgs['VIAF'], '^%d+$' ) then -- Hackishly copy the validation code; this should go away when we move to using P1793 and P1630
 
table.insert( elements, createRow( 'VIAF', '', parentArgs['VIAF'], '[https://www.worldcat.org/identities/containsVIAFID/' .. parentArgs['VIAF'] .. ' WorldCat Identities]', false ) )
 
elseif parentArgs['LCCN'] and parentArgs['LCCN'] ~= '' then
 
local lccnParts = splitLccn( parentArgs['LCCN'] )
 
if lccnParts and lccnParts[1] ~= 'sh' then
 
table.insert( elements, createRow( 'LCCN', '', parentArgs['LCCN'], '[https://www.worldcat.org/identities/lccn-' .. lccnParts[1] .. lccnParts[2] .. '-' .. lccnParts[3] .. ' WorldCat Identities]', false ) )
 
end
 
end
 
 
 
 
--Configured rows
 
--Configured rows
 
local rct = 0
 
local rct = 0
for _, params in ipairs( conf ) do
+
for _, params in ipairs( p.conf ) do
 
local val = parentArgs[params[1]]
 
local val = parentArgs[params[1]]
 
if val and val ~= '' then
 
if val and val ~= '' then
table.insert( elements, createRow( params[1], params[2] .. ':', val, params[4]( val ), true ) )
+
table.insert( elements, p.createRow( params[1], params[2]..':', val, params[4]( val ), true, params.category ) )
 
rct = rct + 1
 
rct = rct + 1
 
end
 
end
 
end
 
end
 +
 +
--WorldCat
 +
local worldcatId = parentArgs['WORLDCATID']
 +
if worldcatId and worldcatId ~= '' then --if present & unsuppressed
 +
table.insert( elements, p.createRow( 'WORLDCATID', '', worldcatId, '[[WorldCat Identities]]: [https://www.worldcat.org/identities/'..worldcatId..' '..worldcatId..']', false ) ) --Validation?
 +
worldcatCat = '[[Category:Wikipedia articles with WorldCat identifiers]]'
 +
elseif worldcatId == nil then --if absent & unsuppressed
 +
local viafId = parentArgs['VIAF']
 +
local lccnId = parentArgs['LCCN']
 +
if viafId and viafId ~= '' and p.viafLink( viafId ) then --VIAF must be present, unsuppressed, & validated
 +
table.insert( elements, p.createRow( 'VIAF', '', viafId, '[[WorldCat Identities]] (via VIAF): [https://www.worldcat.org/identities/containsVIAFID/'..viafId..' '..viafId..']', false ) )
 +
if namespace == 0 then
 +
worldcatCat = '[[Category:Wikipedia articles with WorldCat-VIAF identifiers]]'
 +
end
 +
elseif lccnId and lccnId ~= '' and p.lccnLink( lccnId ) then --LCCN must be present, unsuppressed, & validated
 +
local lccnParts = p.splitLccn( lccnId )
 +
if lccnParts and lccnParts[1] ~= 'sh' then
 +
local lccnIdFmtd = lccnParts[1]..lccnParts[2]..'-'..lccnParts[3]
 +
table.insert( elements, p.createRow( 'LCCN', '', lccnId, '[[WorldCat Identities]] (via LCCN): [https://www.worldcat.org/identities/lccn-'..lccnIdFmtd..' '..lccnIdFmtd..']', false ) )
 +
if namespace == 0 then
 +
worldcatCat = '[[Category:Wikipedia articles with WorldCat-LCCN identifiers]]'
 +
end
 +
end
 +
end
 +
elseif worldcatId == '' then --if suppressed
 +
suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|WORLDCATID]]'
 +
end
 +
 
local Navbox = require('Module:Navbox')
 
local Navbox = require('Module:Navbox')
local elementscats = ''
+
local elementsCat = ''
if rct > 13 then
+
if rct >= 20 then
elementscats = '[[Category:AC with ' .. rct .. ' elements]]'
+
local catName = 'AC with '..rct..' elements'
 +
elementsCat = '[[Category:'..catName..']]'..p.redCatLink(catName)
 
end
 
end
 
+
if #elements ~= 0 then
+
local outString = ''
return Navbox._navbox( {
+
if #elements > 0 then
 +
local args = {}
 +
if testcases and itemId then args = { qid = itemId } end --expensive
 +
local pencil = frame:expandTemplate{ title = 'EditAtWikidata', args = args}
 +
outString = Navbox._navbox( {
 
name  = 'Authority control',
 
name  = 'Authority control',
 +
navboxclass = 'authority-control',
 
bodyclass = 'hlist',
 
bodyclass = 'hlist',
group1 = '[[Help:Authority control|Authority control]]' .. elementscats,
+
group1 = '[[Help:Authority control|Authority control]]'..pencil,
 
list1 = table.concat( elements )
 
list1 = table.concat( elements )
 
} )
 
} )
else
+
local auxCats = worldcatCat .. elementsCat .. suppressedIdCat .. deprecatedIdCat
return ""
+
if testcases then
 +
auxCats = mw.ustring.gsub(auxCats, '(%[%[)(Category)', '%1:%2') --for easier checking
 +
end
 +
outString = outString .. auxCats
 +
if namespace ~= 0 then
 +
outString = mw.ustring.gsub(outString, '(%[%[)(Category:Wikipedia articles)', '%1:%2') --by definition
 +
end
 
end
 
end
 +
 +
return outString
 
end
 
end
  
 
return p
 
return p

Latest revision as of 01:34, 10 September 2019

Documentation for this module may be created at Module:Authority control/doc

require('Module:No globals')

local p = {}
local title = mw.title.getCurrentTitle()
local namespace = title.namespace
local talkspace = (mw.site.talkNamespaces[namespace] ~= nil)
local testcases = (string.sub(title.subpageText,1,9) == 'testcases')

--[[==========================================================================]]
--[[                            Category functions                            ]]
--[[==========================================================================]]

function p.getCatForId( id )
	local catName = ''
	if namespace == 0 then
		catName = 'Wikipedia articles with '..id..' identifiers'
	elseif namespace == 2 and not title.isSubpage then
		catName = 'User pages with '..id..' identifiers'
	else
		catName = 'Miscellaneous pages with '..id..' identifiers'
	end
	return '[[Category:'..catName..']]'..p.redCatLink(catName)
end

function p.redCatLink( catName ) --catName == 'Blah', not 'Category:Blah', not '[[Category:Blah]]'
	if catName and catName ~= '' and
	   testcases == false and
	   mw.title.new(catName, 14).exists == false
	then
		return '[[Category:Pages with red-linked authority control categories]]'
	end
	return ''
end

--[[==========================================================================]]
--[[                      Property formatting functions                       ]]
--[[==========================================================================]]

function p.iaafLink( id )
	--P1146's format regex: [0-9][0-9]* (e.g. 012)
	if not string.match( id, '^%d+$' ) then
		return false
	end
	return '[https://www.iaaf.org/athletes/_/'..id..' '..id..']'..p.getCatForId( 'IAAF' )
end

function p.viafLink( id )
	--P214's format regex: [1-9]\d(\d{0,7}|\d{17,20}) (e.g. 123456789, 1234567890123456789012)
	if not string.match( id, '^[1-9]%d%d?%d?%d?%d?%d?%d?%d?$' ) and
	   not string.match( id, '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d?%d?%d?$' ) then
		return false
	end
	return '[https://viaf.org/viaf/'..id..' '..id..']'..p.getCatForId( 'VIAF' )
end

function p.kulturnavLink( id )
	--P1248's format regex: [0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
	if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
		return false
	end
	return '[http://kulturnav.org/'..id..' '..id..']'..p.getCatForId( 'KULTURNAV' ) --no https yet (10/2018)
end

function p.sikartLink( id )
	--P781's format regex: \d{7,9} (e.g. 123456789)
	if not string.match( id, '^%d%d%d%d%d%d%d%d?%d?$' ) then
		return false
	end
	return '[http://www.sikart.ch/KuenstlerInnen.aspx?id='..id..'&lng=en '..id..']'..p.getCatForId( 'SIKART' ) --no https yet (10/2018)
end

function p.tlsLink( id )
	local id2 = id:gsub(' +', '_')
	--P1362's format regex: \p{Lu}[\p{L}\d_',\.\-\(\)\*/–]{3,59} (e.g. Abcd)
	local class = "[%a%d_',%.%-%(%)%*/–]"
	local regex = "^%u"..string.rep(class, 3)..string.rep(class.."?", 56).."$"
	if not mw.ustring.match( id2, regex ) then
		return false
	end
	return '[http://tls.theaterwissenschaft.ch/wiki/'..id2..' '..id..']'..p.getCatForId( 'TLS' ) --no https yet (10/2018)
end

function p.ciniiLink( id )
	--P271's format regex: DA\d{7}[\dX] (e.g. DA12345678)
	if not string.match( id, '^DA%d%d%d%d%d%d%d[%dX]$' ) then
		return false
	end
	return '[https://ci.nii.ac.jp/author/'..id..'?l=en '..id..']'..p.getCatForId( 'CINII' )
end

function p.daaoLink( id )
	--P1707's format regex: [a-z\-]+\d* (e.g. rolf-harris)
	if not string.match( id, '^[a-z%-]+%d*$' ) then
		return false
	end
	return '[https://www.daao.org.au/bio/'..id..' '..id..']'..p.getCatForId( 'DAAO' )
end

function p.bneLink( id )
	--P950's format regex: (XX|FF|a)\d{4,7}|(bima|bimo|bica|bis[eo]|bivi|Mise|Mimo|Mima)\d{10} (e.g. XX1234567)
	if not string.match( id, '^[XF][XF]%d%d%d%d%d?%d?%d?$' ) and
	   not string.match( id, '^a%d%d%d%d%d?%d?%d?$' ) and
	   not string.match( id, '^bi[mcsv][aoei]%d%d%d%d%d%d%d%d%d%d$' ) and
	   not string.match( id, '^Mi[sm][eoa]%d%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[http://catalogo.bne.es/uhtbin/authoritybrowse.cgi?action=display&authority_id='..id..' '..id..']'..p.getCatForId( 'BNE' ) --no https yet (10/2018)
end

function p.uscongressLink( id )
	--P1157's format regex: [A-Z]00[01]\d{3} (e.g. A000123)
	if not string.match( id, '^[A-Z]00[01]%d%d%d$' ) then
		return false
	end
	return '[http://bioguide.congress.gov/scripts/biodisplay.pl?index='..id..' '..id..']'..p.getCatForId( 'USCongress' ) --no https yet (10/2018)
end

function p.naraLink( id )
	--P1225's format regex: ^([1-9]\d{0,8})$ (e.g. 123456789)
	if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
		return false
	end
	return '[https://catalog.archives.gov/id/'..id..' '..id..']'..p.getCatForId( 'NARA' )
end

function p.botanistLink( id )
	--P428's format regex: ('t )?(d')?(de )?(la )?(van (der )?)?(Ma?c)?(De)?(Di)?\p{Lu}?C?['\p{Ll}]*([-'. ]*(van )?(y )?(d[ae][nr]?[- ])?(Ma?c)?[\p{Lu}bht]?C?['\p{Ll}]*)*\.? ?f?\.? (e.g. L.)
	--not easily/meaningfully implementable in Lua's regex since "(this)?" is not allowed...
	if not mw.ustring.match( id, "^[%u%l%d%. '-]+$" ) then --better than nothing
		return false
	end
	local id2 = id:gsub(' +', '%%20')
	return '[https://www.ipni.org/ipni/advAuthorSearch.do?find_abbreviation='..id2..' '..id..']'..p.getCatForId( 'Botanist' )
end

function p.mgpLink( id )
	--P549's format regex: \d{1,6} (e.g. 123456)
	if not string.match( id, '^%d%d?%d?%d?%d?%d?$' ) then
		return false
	end
	return '[https://genealogy.math.ndsu.nodak.edu/id.php?id='..id..' '..id..']'..p.getCatForId( 'MGP' )
end

function p.rslLink( id )
	--P947's format regex: \d{1,9} (e.g. 123456789)
	if not string.match( id, '^%d%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
		return false
	end
	return '[http://aleph.rsl.ru/F?func=find-b&find_code=SYS&adjacent=Y&local_base=RSL11&request='..id..'&CON_LNG=ENG '..id..']'..p.getCatForId( 'RSL' ) --no https yet (10/2018)
end

function p.leonoreLink( id )
	--P640's format regex: LH/\d{1,4}/\d{1,3}|19800035/\d{1,4}/\d{1,5}(Bis)?|C/0/\d{1,2} (e.g. LH/2064/18)
	if not id:match( '^LH/%d%d?%d?%d?/%d%d?%d?$' ) and             --IDs from       LH/1/1 to         LH/2794/54 (legionaries)
	   not id:match( '^19800035/%d%d?%d?%d?/%d%d?%d?%d?%d?$' ) and --IDs from 19800035/1/1 to 19800035/385/51670 (legionnaires who died 1954-1977 & some who died < 1954)
	   not id:match( '^C/0/%d%d?$' ) then                          --IDs from        C/0/1 to             C/0/84 (84 famous legionaries)
		return false
	end
	return '[http://www.culture.gouv.fr/public/mistral/leonore_fr?ACTION=CHERCHER&FIELD_1=COTE&VALUE_1='..id..' '..id..']'..p.getCatForId( 'Léonore' ) --no https yet (10/2018)
end

function p.sbnLink( id )
	--P396's format regex: IT\\ICCU\\(\d{10}|\D\D[\D\d]\D\\\d{6}) (e.g. IT\ICCU\CFIV\000163)
	if not string.match( id, '^IT\\ICCU\\%d%d%d%d%d%d%d%d%d%d$' ) and
	   not string.match( id, '^IT\\ICCU\\%u%u[%u%d]%u\\%d%d%d%d%d%d$' ) then --legacy: %u used here instead of %D (but the faulty ID cat is empty, out of ~12k uses)
		return false
	end
	return '[https://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid='..id..' '..id..']'..p.getCatForId( 'SBN' )
end

function p.nkcLink( id )
	--P691's format regex: [a-z]{2,4}[0-9]{2,14} (e.g. abcd12345678901234)
	if not string.match( id, '^[a-z][a-z][a-z]?[a-z]?%d%d%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
		return false
	end
	return '[https://aleph.nkp.cz/F/?func=find-c&local_base=aut&ccl_term=ica='..id..'&CON_LNG=ENG '..id..']'..p.getCatForId( 'NKC' )
end

function p.nclLink( id )
	--P1048's format regex: \d+ (e.g. 1081436)
	if not string.match( id, '^%d+$' ) then
		return false
	end
	return '[http://aleweb.ncl.edu.tw/F/?func=accref&acc_sequence='..id..'&CON_LNG=ENG '..id..']'..p.getCatForId( 'NCL' ) --no https yet (10/2018)
end

function p.ndlLink( id )
	--P349's format regex: 0?\d{8} (e.g. 012345678)
	if not string.match( id, '^0?%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[https://id.ndl.go.jp/auth/ndlna/'..id..' '..id..']'..p.getCatForId( 'NDL' )
end

function p.ngvLink( id )
	--P2041's format regex: \d+ (e.g. 12354)
	if not string.match( id, '^%d+$' ) then
		return false
	end
	return '[https://www.ngv.vic.gov.au/explore/collection/artist/'..id..'/ '..id..']'..p.getCatForId( 'NGV' )
end

function p.sudocLink( id )
	--P269's format regex: (\d{8}[\dX]|) (e.g. 026927608)
	if not string.match( id, '^%d%d%d%d%d%d%d%d[%dxX]$' ) then --legacy: allow lowercase 'x'
		return false
	end
	return '[https://www.idref.fr/'..id..' '..id..']'..p.getCatForId( 'SUDOC' )
end

function p.hdsLink( id )
	--P902's format regex: 50\d{3}|[1-4]\d{4}|[1-9]\d{0,3}| (e.g. 50123)
	if not string.match( id, '^50%d%d%d$' ) and
	   not string.match( id, '^[1-4]%d%d%d%d$' ) and
	   not string.match( id, '^[1-9]%d?%d?%d?$' ) then
		return false
	end
	return '[http://www.hls-dhs-dss.ch/textes/f/F'..id..'.php '..id..']'..p.getCatForId( 'HDS' ) --no https yet (10/2018)
end

function p.lirLink( id )
	--P886's format regex: \d+ (e.g. 1)
	if not string.match( id, '^%d+$' ) then
		return false
	end
	return '[http://www.e-lir.ch/e-LIR___Lexicon.'..id..'.450.0.html '..id..']'..p.getCatForId( 'LIR' ) --no https yet (10/2018)
end

function p.splitLccn( id )
	--P244's format regex: (n|nb|nr|no|ns|sh)([4-9][0-9]|00|20[0-1][0-9])[0-9]{6} (e.g. n78039510)
	if id:match( '^%l%l?%l?%d%d%d%d%d%d%d%d%d?%d?$' ) then
		id = id:gsub( '^(%l+)(%d+)(%d%d%d%d%d%d)$', '%1/%2/%3' )
	end
	if id:match( '^%l%l?%l?/%d%d%d?%d?/%d+$' ) then
		return mw.text.split( id, '/' )
	end
	return false
end

function p.append(str, c, length)
	while str:len() < length do
		str = c .. str
	end
	return str
end

function p.lccnLink( id )
	local parts = p.splitLccn( id ) --e.g. n78039510
	if not parts then
		return false
	end
	local lccnType = parts[1] ~= 'sh' and 'names' or 'subjects'
	id = parts[1] .. parts[2] .. p.append( parts[3], '0', 6 )
	return '[https://id.loc.gov/authorities/'..lccnType..'/'..id..' '..id..']'..p.getCatForId( 'LCCN' )
end

function p.mbaLink( id )
	--P434's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
	if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
		return false
	end
	return '[https://musicbrainz.org/artist/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz' ) --special category name
end

function p.mbareaLink( id )
	--P982's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
	if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
		return false
	end
	return '[https://musicbrainz.org/area/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz area' ) --special category name
end

function p.mbiLink( id )
	--P1330's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
	if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
		return false
	end
	return '[https://musicbrainz.org/instrument/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz instrument' ) --special category name
end

function p.mblLink( id )
	--P966's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
	if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
		return false
	end
	return '[https://musicbrainz.org/label/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz label' ) --special category name
end

function p.mbpLink( id )
	--P1004's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
	if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
		return false
	end
	return '[https://musicbrainz.org/place/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz place' ) --special category name
end

function p.mbrgLink( id )
	--P436's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
	if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
		return false
	end
	return '[https://musicbrainz.org/release-group/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz release group' ) --special category name
end

function p.mbsLink( id )
	--P1407's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
	if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
		return false
	end
	return '[https://musicbrainz.org/series/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz series' ) --special category name
end

function p.mbwLink( id )
	--P435's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
	if not string.match( id, '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
		return false
	end
	return '[https://musicbrainz.org/work/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz work' ) --special category name
end

--Returns the ISNI check digit isni must be a string where the 15 first elements are digits, e.g. 0000000066534145
function p.getIsniCheckDigit( isni )
	local total = 0
	for i = 1, 15 do
		local digit = isni:byte( i ) - 48 --Get integer value
		total = (total + digit) * 2
	end
	local remainder = total % 11
	local result = (12 - remainder) % 11
	if result == 10 then
		return "X"
	end
	return tostring( result )
end

--Validate ISNI (and ORCID) and retuns 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
function p.validateIsni( id )
	--P213 (ISNI) format regex: [0-9]{4} [0-9]{4} [0-9]{4} [0-9]{3}[0-9X] (e.g. 0000-0000-6653-4145)
	--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 = 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
	if p.getIsniCheckDigit( id ) ~= string.char( id:byte( 16 ) ) then
		return false
	end
	return id
end

function p.isniLink( id )
	id = p.validateIsni( id ) --e.g. 0000-0000-6653-4145
	if not id then
		return false
	end
	return '[http://isni.org/isni/'..id..' '..id:sub( 1, 4 )..' '..id:sub( 5, 8 )..' '..id:sub( 9, 12 )..' '..id:sub( 13, 16 )..']'..p.getCatForId( 'ISNI' ) --no https yet (10/2018)
end

function p.orcidLink( id )
	id = p.validateIsni( id ) --e.g. 0000-0002-7398-5483
	if not id then
		return false
	end
	id = id:sub( 1, 4 )..'-'..id:sub( 5, 8 )..'-'..id:sub( 9, 12 )..'-'..id:sub( 13, 16 )
	return '[https://orcid.org/'..id..' '..id..']'..p.getCatForId( 'ORCID' )
end

function p.gndLink( id )
	--P227's format regex: (1|1[01])\d{7}[0-9X]|[47]\d{6}-\d|[1-9]\d{0,7}-[0-9X]|3\d{7}[0-9X] (e.g. 4079154-3)
	if not string.match( id, '^1[01]?%d%d%d%d%d%d%d[0-9X]$' ) and
	   not string.match( id, '^[47]%d%d%d%d%d%d%-%d$' ) and
	   not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?%-[0-9X]$' ) and
	   not string.match( id, '^3%d%d%d%d%d%d%d[0-9X]$' ) then
		return false
	end
	return '[https://d-nb.info/gnd/'..id..' '..id..']'..p.getCatForId( 'GND' )
end

function p.selibrLink( id )
	--P906's format regex: [1-9]\d{4,5} (e.g. 123456)
	if not string.match( id, '^[1-9]%d%d%d%d%d?$' ) then
		return false
	end
	return '[https://libris.kb.se/auth/'..id..' '..id..']'..p.getCatForId( 'SELIBR' )
end

function p.bnfLink( id )
	--P268's format regex: \d{8}[0-9bcdfghjkmnpqrstvwxz] (e.g. 123456789)
	if not string.match( id, '^c?b?%d%d%d%d%d%d%d%d[0-9bcdfghjkmnpqrstvwxz]$' ) then
		return false
	end
	--Add cb prefix if it has been removed
	if not string.match( id, '^cb.+$' ) then
		id = 'cb'..id
	end
	return '[https://catalogue.bnf.fr/ark:/12148/'..id..' '..id..'] [https://data.bnf.fr/ark:/12148/'..id..' (data)]'..p.getCatForId( 'BNF' )
end

function p.bpnLink( id )
	--P651's format regex: \d{8} (e.g. 12345678)
	if not string.match( id, '^%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[http://www.biografischportaal.nl/en/persoon/'..id..' '..id..']'..p.getCatForId( 'BPN' ) --no https yet (10/2018)
end

function p.ridLink( id )
	--P1053's format regex: [A-Z]-\d{4}-(19|20)\d\d (e.g. A-1234-1934)
	if not string.match( id, '^[A-Z]%-%d%d%d%d%-19%d%d$' ) and
	   not string.match( id, '^[A-Z]%-%d%d%d%d%-20%d%d$' ) then
		return false
	end
	return '[https://www.researcherid.com/rid/'..id..' '..id..']'..p.getCatForId( 'RID' )
end

function p.bibsysLink( id )
	--P1015's format regex: [1-9]\d* or [1-9](\d{0,8}|\d{12}) (e.g. 1234567890123)
	--TODO: follow up @ [[d:Property talk:P1015#Discrepancy between the 2 regex constraints]] or escalate/investigate
	if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) and
	   not string.match( id, '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[https://authority.bibsys.no/authority/rest/authorities/html/'..id..' '..id..']'..p.getCatForId( 'BIBSYS' )
end

function p.ulanLink( id )
	--P245's format regex: 500\d{6} (e.g. 500123456)
	if not string.match( id, '^500%d%d%d%d%d%d$' ) then
		return false
	end
	return '[https://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid='..id..' '..id..']'..p.getCatForId( 'ULAN' )
end

function p.nlaLink( id )
	--P409's format regex: [1-9][0-9]{0,11} (e.g. 123456789012)
	if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
		return false
	end
	return '[https://nla.gov.au/anbd.aut-an'..id..' '..id..']'..p.getCatForId( 'NLA' )
end

function p.nlapersonLink( id )
	--P1315's format regex: [1-9]\d{5,7} (e.g. 12345678)
	if not string.match( id, '^[1-9]%d%d%d%d%d%d?%d?$' ) then
		return false
	end
	return '[https://trove.nla.gov.au/people/'..id..' '..id..']'..p.getCatForId( 'NLA-person' )
end

function p.rkdartistsLink( id )
	--P650's format regex: [1-9]\d{0,5} (e.g. 123456)
	if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?$' ) then
		return false
	end
	return '[https://rkd.nl/en/explore/artists/'..id..' '..id..']'..p.getCatForId( 'RKDartists' )
end

function p.snacLink( id )
	--P3430's format regex: \d*[A-Za-z][0-9A-Za-z]* (e.g. A)
	if not string.match( id, '^%d*[A-Za-z][0-9A-Za-z]*$' ) then
		return false
	end
	return '[http://socialarchive.iath.virginia.edu/ark:/99166/'..id..' '..id..']'..p.getCatForId( 'SNAC-ID' ) --no https yet (10/2018)
end

function p.dblpLink( id )
	--P2456's format regex: \d{2,3} /\d+(-\d+)?|[a-z] /[a-zA-Z][0-9A-Za-z]*(-\d+)? (e.g. 123/123)
	if not string.match( id, '^%d%d%d?/%d+$' ) and
	   not string.match( id, '^%d%d%d?/%d+%-%d+$' ) and
	   not string.match( id, '^[a-z]/[a-zA-Z][0-9A-Za-z]*$' ) and
	   not string.match( id, '^[a-z]/[a-zA-Z][0-9A-Za-z]*%-%d+$' ) then
		return false
	end
	return '[https://dblp.org/pid/'..id..' '..id..']'..p.getCatForId( 'DBLP' )
end

function p.aagLink( id )
	--P3372's format regex: \d+ (e.g. 1)
	if not string.match( id, '^%d+$' ) then
		return false
	end
	return '[http://www.aucklandartgallery.com/explore-art-and-ideas/artist/'..id..'/ '..id..']'..p.getCatForId( 'AAG' )
end

function p.acmLink( id )
	--P864's format regex: \d{11} (e.g. 12345678901)
	if not string.match( id, '^%d%d%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[https://dl.acm.org/author_page.cfm?id='..id..' '..id..']'..p.getCatForId( 'ACM-DL' )
end

function p.adbLink( id )
	--P1907's format regex: [a-z][-a-z]+-([1-2]\d|[1-9])\d{0,3} (e.g. barton-sir-edmund-toby-71)
	if not string.match( id, '^[a-z][-a-z]+-[1-2]%d%d?%d?%d?$' ) and
	   not string.match( id, '^[a-z][-a-z]+-[1-9]%d?%d?%d?$' ) then
		return false
	end
	return '[http://adb.anu.edu.au/biography/'..id..' '..id..']'..p.getCatForId( 'ADB' )
end

function p.agsaLink( id )
	--P6804's format regex: [1-9]\d* (e.g. 3625)
	if not string.match( id, '^[1-9]%d*$' ) then
		return false
	end
	return '[https://www.agsa.sa.gov.au/collection-publications/collection/creators/_/'..id..'/ '..id..']'..p.getCatForId( 'AGSA' )
end

function p.autoresuyLink( id )
	--P2558's format regex: [1-9]\d{0,4} (e.g. 12345)
	if not string.match( id, '^[1-9]%d?%d?%d?%d?$' ) then
		return false
	end
	return '[https://autores.uy/autor/'..id..' '..id..']'..p.getCatForId( 'autores.uy' )
end

function p.awrLink( id )
	--P4186's format regex: (([A-Z]{3}\d{4})|([A-Z]{2}\d{5}))[a-z] (e.g. PR00768b)
	if not string.match( id, '^[A-Z][A-Z][A-Z]%d%d%d%d[a-z]$' ) and
	   not string.match( id, '^[A-Z][A-Z]%d%d%d%d%d[a-z]$' ) then
		return false
	end
	return '[http://www.womenaustralia.info/biogs/'..id..'.htm '..id..']'..p.getCatForId( 'AWR' )
end

function p.picLink( id )
	--P2750's format regex: [1-9]\d* (e.g. 1)
	if not string.match( id, '^[1-9]%d*$' ) then
		return false
	end
	return '[https://pic.nypl.org/constituents/'..id..' '..id..']'..p.getCatForId( 'PIC' )
end

function p.bildLink( id )
	--P2092's format regex: \d+ (e.g. 1)
	if not string.match( id, '^%d+$' ) then
		return false
	end
	return '[https://www.bildindex.de/document/obj'..id..' '..id..']'..p.getCatForId( 'Bildindex' )
end

function p.jocondeLink( id )
	--P347's format regex: [\-0-9A-Za-z]{11} (e.g. 12345678901)
	local regex = '^'..string.rep('[%-0-9A-Za-z]', 11)..'$'
	if not string.match( id, regex ) then
		return false
	end
	return '[http://www2.culture.gouv.fr/public/mistral/joconde_fr?ACTION=CHERCHER&FIELD_1=REF&VALUE_1='..id..' '..id..']'..p.getCatForId( 'Joconde' ) --no https yet (10/2018)
end

function p.rkdidLink( id )
	--P350's format regex: [1-9]\d{0,5} (e.g. 123456)
	if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?$' ) then
		return false
	end
	return '[https://rkd.nl/nl/explore/images/'..id..' '..id..']'..p.getCatForId( 'RKDID' )
end

function p.balatLink( id )
	--P3293's format regex: \d+ (e.g. 1)
	if not string.match( id, '^%d+$' ) then
		return false
	end
	return '[http://balat.kikirpa.be/object/104257'..id..' '..id..']'..p.getCatForId( 'BALaT' ) --no https yet (10/2018)
end

function p.lnbLink( id )
	--P1368's format regex: \d{9} (e.g. 123456789)
	if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[https://kopkatalogs.lv/F?func=direct&local_base=lnc10&doc_number='..id..'&P_CON_LNG=ENG '..id..']'..p.getCatForId( 'LNB' )
end

function p.nskLink( id )
	--P1375's format regex: \d{9} (e.g. 123456789)
	if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[http://katalog.nsk.hr/F/?func=direct&doc_number='..id..'&local_base=nsk10 '..id..']'..p.getCatForId( 'NSK' ) --no https yet (10/2018)
end

function p.iciaLink( id )
	--P1736's format regex: \d+ (e.g. 1)
	if not string.match( id, '^%d+$' ) then
		return false
	end
	return '[https://www.imj.org.il/artcenter/newsite/en/?artist='..id..' '..id..']'..p.getCatForId( 'ICIA' )
end

function p.ta98Link( id )
	--P1323's format regex: A\d{2}\.\d\.\d{2}\.\d{3}[FM]? (e.g. A12.3.45.678)
	if not string.match( id, '^A%d%d%.%d%.%d%d%.%d%d%d[FM]?$' ) then
		return false
	end
	return '[http://tools.wmflabs.org/wikidata-externalid-url/?p=1323&url_prefix=https:%2F%2Fwww.unifr.ch%2Fifaa%2FPublic%2FEntryPage%2FTA98%20Tree%2FEntity%20TA98%20EN%2F&url_suffix=%20Entity%20TA98%20EN.htm&id='..id..' '..id..']'..p.getCatForId( 'TA98' )
end

function p.teLink( id )
	--P1693's format regex: E[1-8]\.\d{1,2}\.\d{1,2}\.\d{1,2}\.\d{1}\.\d{1}\.\d{1,3} (e.g. E1.23.45.67.8.9.0)
	local e1, e2 = string.match( id, '^E([1-8])%.(%d%d?)%.%d%d?%.%d%d?%.%d%.%d%.%d%d?%d?$' )
	if not e1 then
		return false
	end
	local TEnum = 'TEe0'..e1 --no formatter URL in WD, probably due to this complexity
	if e1 == '5' or e1 == '7' then
		if #e2 == 1 then e2 = '0'..e2 end
		TEnum = TEnum..e2
	end
	return '[http://www.unifr.ch/ifaa/Public/EntryPage/ViewTE/'..TEnum..'.html '..id..']'..p.getCatForId( 'TE' )
end

function p.tepapaLink( id )
	--P3544's format regex: \d+ (e.g. 1)
	if not string.match( id, '^%d+$' ) then
		return false
	end
	return '[https://collections.tepapa.govt.nz/agent/'..id..' '..id..']'..p.getCatForId( 'TePapa' )
end

function p.thLink( id )
	--P1694's format regex: H\d\.\d{2}\.\d{2}\.\d\.\d{5} (e.g. H1.23.45.6.78901)
	local h1, h2 = string.match( id, '^H(%d)%.(%d%d)%.%d%d%.%d%.%d%d%d%d%d$' )
	if not h1 then
		return false
	end
	local THnum = 'THh'..h1..h2 --no formatter URL in WD, probably due to this complexity
	return '[http://www.unifr.ch/ifaa/Public/EntryPage/ViewTH/'..THnum..'.html '..id..']'..p.getCatForId( 'TH' )
end

function p.dsiLink( id )
	--P2349's format regex: [1-9]\d* (e.g. 1538)
	if not string.match( id, '^[1-9]%d*$' ) then
		return false
	end
	return '[http://www.uni-stuttgart.de/hi/gnt/dsi2/index.php?table_name=dsi&function=details&where_field=id&where_value='..id..' '..id..']'..p.getCatForId( 'DSI' )
end

function p.s2authoridLink( id )
	--P4012's format regex: [1-9]\d* (e.g. 1796130)
	if not string.match( id, '^[1-9]%d*$' ) then
		return false
	end
	return '[https://www.semanticscholar.org/author/'..id..' '..id..']'..p.getCatForId( 'Semantic Scholar author' ) --special category name
end

function p.nliLink( id )
	--P949's format regex: \d{9} (e.g. 123456789)
	if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[http://aleph.nli.org.il/F/?func=find-b&local_base=NNL10&find_code=SYS&con_lng=eng&request='..id..' '..id..']'..p.getCatForId( 'NLI' )
end

function p.nlpLink( id )
	--P1695's format regex: 9810[0-9]\d* or A[0-9]{7}[0-9X] (e.g. 9810123456789012345 or A10414836)
	if not string.match( id, '^9810%d+$' ) and
	   not string.match( id, '^A%d%d%d%d%d%d%d[%dX]$' ) then
		return false
	end
	return '[https://tools.wmflabs.org/wikidata-externalid-url?p=1695&id='..id..' '..id..']'..p.getCatForId( 'NLP' )
end

function p.reroLink( id )
	--P3065's format regex: 0[1-2]-[A-Z0-9]{1,10} (e.g. 02-A012345678)
	if not string.match( id, '^0[1-2]%-[A-Z%d][A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?$' ) then
		return false
	end
	return '[http://data.rero.ch/'..id..' '..id..']'..p.getCatForId( 'RERO' )
end

function p.nlrLink( id )
	--P1003's format regex: \d{9} (e.g. 123456789)
	if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[http://alephnew.bibnat.ro:8991/F?func=find-b&request='..id..'&find_code=SYS&adjacent=Y&local_base=NLR10 '..id..']'..p.getCatForId( 'NLR' )
end

function p.ntaLink( id )
	--P1006's format regex: \d{8}[\dX] (e.g. 12345678X)
	if not string.match( id, '^%d%d%d%d%d%d%d%d[%dX]$' ) then
		return false
	end
	return '[http://data.bibliotheken.nl/id/thes/p'..id..' '..id..']'..p.getCatForId( 'NTA' )
end

function p.bncLink( id )
	--P1890's format regex: \d{9} (e.g. 123456789)
	if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[http://www.bncatalogo.cl/F?func=direct&local_base=red10&doc_number='..id..' '..id..']'..p.getCatForId( 'BNC' )
end

--[[==========================================================================]]
--[[          Wikidata, navigation bar, and documentation functions           ]]
--[[==========================================================================]]

function p.getIdsFromWikidata( itemId, property )
	local ids = {}
	local statements = mw.wikibase.getBestStatements( itemId, property )
	if statements then
		for _, statement in ipairs( statements ) do
			if statement.mainsnak.datavalue then
				table.insert( ids, statement.mainsnak.datavalue.value )
			end
		end
	end
	return ids
end

function p.matchesWikidataRequirements( itemId, reqs )
	for _, group in ipairs( reqs ) do
		local property = 'P'..group[1]
		local qid = group[2]
		local statements = mw.wikibase.getBestStatements( itemId, property )
		if statements then
			for _, statement in ipairs( statements ) do
				if statement.mainsnak.datavalue then
					if statement.mainsnak.datavalue.value['numeric-id'] == qid then
						return true
	end	end	end	end	end
	return false
end

function p.createRow( id, label, rawValue, link, withUid, specialCat )
	if link then
		if withUid then
			return '*<span class="nowrap">'..label..' <span class="uid">'..link..'</span></span>\n'
		end
		return '*<span class="nowrap">'..label..' '..link..'</span>\n'
	end

	local catName = 'Wikipedia articles with faulty '..(specialCat or id)..' identifiers'
	return '* <span class="error">The '..id..' id '..rawValue..' is not valid.</span>[[Category:'..catName..']]'..p.redCatLink(catName)..'\n'
end

-- Creates a human-readable standalone wikitable version of p.conf, and tracking categories with page counts, for use in the documentation
function p.docConfTable( frame )
	local wikiTable = '{| class="wikitable sortable"\n'..
					  '! rowspan=2 | Parameter\n'..
					  '! rowspan=2 | Label\n'..
					  '! rowspan=2; data-sort-type=number | Wikidata property\n'..
					  '! colspan=4 | Tracking categories and page counts\n'..
					  '|-\n'..
					  '! Articles\n'..
					  '! User pages\n'..
					  '! Misc. pages\n'..
					  '! Faulty IDs\n'..
					  '|-\n'
	
	local lang = mw.getContentLanguage()
	for _, conf in pairs( p.conf ) do
		local param, link, pid = conf[1], conf[2], conf[3]
		local category = conf.category or param
		local args = { id = 'f', pid }
		local wpl = frame:expandTemplate{ title = 'Wikidata property link', args = args }
		--cats
		local articleCat = 'Wikipedia articles with '..category..' identifiers'
		local userCat =    'User pages with '..category..' identifiers'
		local miscCat =    'Miscellaneous pages with '..category..' identifiers'
		local faultyCat =  'Wikipedia articles with faulty '..category..' identifiers'
		--counts
		local articleCount = lang:formatNum( mw.site.stats.pagesInCategory(articleCat, 'pages') )
		local userCount =    lang:formatNum( mw.site.stats.pagesInCategory(userCat, 'pages') )
		local miscCount =    lang:formatNum( mw.site.stats.pagesInCategory(miscCat, 'pages') )
		local faultyCount =  lang:formatNum( mw.site.stats.pagesInCategory(faultyCat, 'pages') )
		--concat
		wikiTable = wikiTable..'\n'..
					'|-\n'..
					'||'..param..
					'||'..link..
					'||data-sort-value='..pid..'|'..wpl..
					'||style="text-align: right;"|[[:Category:'..articleCat..'|'..articleCount..']]'..
					'||style="text-align: right;"|[[:Category:'..   userCat..'|'..   userCount..']]'..
					'||style="text-align: right;"|[[:Category:'..   miscCat..'|'..   miscCount..']]'..
					'||style="text-align: right;"|[[:Category:'.. faultyCat..'|'.. faultyCount..']]'
	end
	return wikiTable..'\n|}'
end

--[[==========================================================================]]
--[[                              Configuration                               ]]
--[[==========================================================================]]

-- Check that the Wikidata item has this property-->value before adding it
local reqs = {}

-- Parameter format: { parameter name, label, propertyId # in Wikidata, formatting/validation function }
p.conf = {
	{ 'AAG', '[[Auckland Art Gallery Toi o Tāmaki|AAG]]', 3372, p.aagLink },
	{ 'ACM-DL', '[[ACM Digital Library|ACM DL]]', 864, p.acmLink },
	{ 'ADB', '[[Australian Dictionary of Biography|ADB]]', 1907, p.adbLink },
	{ 'AGSA', '[[Art Gallery of South Australia|AGSA]]', 6804, p.agsaLink },
	{ 'autores.uy', '[[autores.uy]]', 2558, p.autoresuyLink },
	{ 'AWR', '[[Australian Women\'s Register|AWR]]', 4186, p.awrLink },
	{ 'BALaT', '[[Royal Institute for Cultural Heritage#Online artworks pages|BALaT]]', 3293, p.balatLink },
	{ 'BIBSYS', '[[Bibsys|BIBSYS]]', 1015, p.bibsysLink },
	{ 'Bildindex', '[[Marburg Picture Index|Bildindex]]', 2092, p.bildLink },
	{ 'BNC', '[[Biblioteca Nacional de Chile|BNC]]', 1890, p.bncLink }, --initially commented due to excessive WD ID errors (many bad IDs since removed)
	{ 'BNE', '[[Biblioteca Nacional de España|BNE]]', 950, p.bneLink },
	{ 'BNF', '[[Bibliothèque nationale de France|BNF]]', 268, p.bnfLink },
	{ 'Botanist', '[[Author citation (botany)|Botanist]]', 428, p.botanistLink },
	{ 'BPN', '[[Biografisch Portaal|BPN]]', 651, p.bpnLink },
	{ 'CINII', '[[CiNii]]', 271, p.ciniiLink },
	{ 'DAAO', '[[Dictionary of Australian Artists|DAAO]]', 1707, p.daaoLink },
	{ 'DBLP', '[[DBLP]]', 2456, p.dblpLink },
	{ 'DSI', '[[Stuttgart Database of Scientific Illustrators 1450–1950|DSI]]', 2349, p.dsiLink },
	{ 'GND', '[[Integrated Authority File|GND]]', 227, p.gndLink },
	{ 'HDS', '[[Historical Dictionary of Switzerland|HDS]]', 902, p.hdsLink },
	{ 'IAAF', '[[International Association of Athletics Federations|IAAF]]', 1146, p.iaafLink },
	{ 'ICIA', '[[Information Center for Israeli Art|ICIA]]', 1736, p.iciaLink },
	{ 'ISNI', '[[International Standard Name Identifier|ISNI]]', 213, p.isniLink },
	{ 'Joconde', '[[Joconde]]' , 347, p.jocondeLink },
	{ 'KULTURNAV', '[[KulturNav]]', 1248, p.kulturnavLink },
	{ 'LCCN', '[[Library of Congress Control Number|LCCN]]', 244, p.lccnLink },
	{ 'LIR', '[[Historical Dictionary of Switzerland#Lexicon_Istoric_Retic|LIR]]', 886, p.lirLink },
	{ 'LNB', '[[National Library of Latvia|LNB]]', 1368, p.lnbLink },
	{ 'Léonore', '[[Base Léonore|Léonore]]', 640, p.leonoreLink },
	{ 'MBA', '[[MusicBrainz]]', 434, p.mbaLink, category = 'MusicBrainz' }, --special category name
	{ 'MBAREA', '[[MusicBrainz]]', 982, p.mbareaLink, category = 'MusicBrainz area' }, --special category name
	{ 'MBI', '[[MusicBrainz]]', 1330, p.mbiLink, category = 'MusicBrainz instrument' }, --special category name
	{ 'MBL', '[[MusicBrainz]]', 966, p.mblLink, category = 'MusicBrainz label' }, --special category name
	{ 'MBP', '[[MusicBrainz]]', 1004, p.mbpLink, category = 'MusicBrainz place' }, --special category name
	{ 'MBRG', '[[MusicBrainz]] release group', 436, p.mbrgLink, category = 'MusicBrainz release group' }, --special category name
	{ 'MBS', '[[MusicBrainz]]', 1407, p.mbsLink, category = 'MusicBrainz series' }, --special category name
	{ 'MBW', '[[MusicBrainz]] work', 435, p.mbwLink, category = 'MusicBrainz work' }, --special category name
	{ 'MGP', '[[Mathematics Genealogy Project|MGP]]', 549, p.mgpLink },
	{ 'NARA', '[[National Archives and Records Administration|NARA]]', 1225, p.naraLink },
	{ 'NCL', '[[National Central Library|NCL]]', 1048, p.nclLink },
	{ 'NDL', '[[National Diet Library|NDL]]', 349, p.ndlLink },
	{ 'NGV', '[[National Gallery of Victoria|NGV]]', 2041, p.ngvLink },
	{ 'NKC', '[[National Library of the Czech Republic|NKC]]', 691, p.nkcLink },
	{ 'NLA', '[[National Library of Australia|NLA]]', 409, p.nlaLink },
	{ 'NLA-person', '[[National Library of Australia|NLA-person]]', 1315, p.nlapersonLink },
	{ 'NLI', '[[National Library of Israel|NLI]]', 949, p.nliLink },
	{ 'NLP', '[[National Library of Poland|NLP]]', 1695, p.nlpLink },
	{ 'NLR', '[[National Library of Romania|NLR]]', 1003, p.nlrLink }, --initially commented due to excessive WD ID errors (conflated with National Library of Russia IDs)
	{ 'NSK', '[[National and University Library in Zagreb|NSK]]', 1375, p.nskLink },
	{ 'NTA', '[[Royal Library of the Netherlands|NTA]]', 1006, p.ntaLink },
	{ 'ORCID', '[[ORCID]]', 496, p.orcidLink },
	{ 'PIC', '[[:d:Q23892012|PIC]]', 2750, p.picLink },
	{ 'RID', '[[ResearcherID]]', 1053, p.ridLink },
	{ 'RERO', '[[RERO (Library Network of Western Switzerland)|RERO]]', 3065, p.reroLink }, --initially commented due to excessive WD ID errors (regex fixed/relaxed)
	{ 'RKDartists', '[[Netherlands Institute for Art History#Online artist pages|RKD]]', 650, p.rkdartistsLink },
	{ 'RKDID', '[[:d:Q17299580|RKDimages ID]]', 350, p.rkdidLink },
	{ 'RSL', '[[Russian State Library|RSL]]', 947, p.rslLink },
	{ 'SBN', '[[Istituto Centrale per il Catalogo Unico|ICCU]]', 396, p.sbnLink },
	{ 'SELIBR', '[[LIBRIS|SELIBR]]', 906, p.selibrLink },
	{ 'SIKART', '[[SIKART]]', 781, p.sikartLink },
	{ 'SNAC-ID', '[[SNAC]]', 3430, p.snacLink },
	{ 'SUDOC', '[[Système universitaire de documentation|SUDOC]]', 269, p.sudocLink },
	{ 'S2AuthorId', '[[Semantic Scholar|S2AuthorId]]', 4012, p.s2authoridLink, category = 'Semantic Scholar author' }, --special category name
	{ 'TA98', '[[Terminologia Anatomica|TA98]]', 1323, p.ta98Link },
	{ 'TE', '[[Terminologia Embryologica|TE]]', 1693, p.teLink },
	{ 'TePapa', '[[Museum of New Zealand Te Papa Tongarewa|TePapa]]', 3544, p.tepapaLink },
	{ 'TH', '[[Terminologia Histologica|TH]]', 1694, p.thLink },
	{ 'TLS', '[[Theaterlexikon der Schweiz|TLS]]', 1362, p.tlsLink },
	{ 'ULAN', '[[Union List of Artist Names|ULAN]]', 245, p.ulanLink },
	{ 'USCongress', '[[Biographical Directory of the United States Congress|US Congress]]', 1157, p.uscongressLink },
	{ 'VIAF', '[[Virtual International Authority File|VIAF]]', 214, p.viafLink },
}

-- Legitimate aliases to p.conf, for convenience
-- Format: { alias, parameter name in p.conf }
p.aliases = {
	{ 'RLS', 'RSL' },
	{ 'MusicBrainz', 'MBA' },
	{ 'MusicBrainz artist', 'MBA' },
	{ 'MusicBrainz label', 'MBL' },
	{ 'MusicBrainz release group', 'MBRG' },
	{ 'MusicBrainz work', 'MBW' },
	{ 'Leonore', 'Léonore' },
}

-- Deprecated aliases to p.conf, which also get assigned to a tracking cat
-- Format: { deprecated parameter name, replacement parameter name in p.conf }
p.deprecated = {
	{ 'GKD', 'GND' },
	{ 'PND', 'GND' },
	{ 'SWD', 'GND' },
	{ 'NARA-organization', 'NARA' },
	{ 'NARA-person', 'NARA' },
}

--[[==========================================================================]]
--[[                                   Main                                   ]]
--[[==========================================================================]]

function p.authorityControl( frame )
	local resolveEntity = require( "Module:ResolveEntityId" )
	local parentArgs = frame:getParent().args
	local elements = {} --create/insert rows later
	local worldcatCat = ''
	local suppressedIdCat = ''
	local deprecatedIdCat = ''
	
	--Redirect aliases to proper parameter names
	for _, a in pairs( p.aliases ) do
		local alias, param = a[1], a[2]
		if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[alias] then
			parentArgs[param] = parentArgs[alias]
		end
	end
	
	--Redirect deprecated parameters to proper parameter names, and assign tracking cat
	for _, d in pairs( p.deprecated ) do
		local dep, param = d[1], d[2]
		if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[dep] then
			parentArgs[param] = parentArgs[dep]
			if namespace == 0 then
				deprecatedIdCat = '[[Category:Wikipedia articles with deprecated authority control identifiers|'..dep..']]'
			end
		end
	end
	
	--Use QID= parameter for testing/example purposes only
	local itemId = nil
	if namespace ~= 0 then
		local qid = parentArgs['qid'] or parentArgs['QID']
		if qid then
			itemId = 'Q'..mw.ustring.gsub(qid, '^[Qq]', '')
			itemId = resolveEntity._id(itemId) --nil if unresolvable
		end
	else
		itemId = mw.wikibase.getEntityIdForCurrentPage()
	end
	
	--Wikidata fallback if requested
	if itemId then
		for _, params in ipairs( p.conf ) do
			if params[3] > 0 then
				local val = parentArgs[params[1]]
				if val == nil or val == '' then
					local canUseWikidata = nil
					if reqs[params[1]] then
						canUseWikidata = p.matchesWikidataRequirements( itemId, reqs[params[1]] )
					else
						canUseWikidata = true
					end
					if canUseWikidata then
						local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
						if wikidataIds[1] then
							if val == '' and (namespace == 0 or testcases) then
								suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|'..params[1]..']]'
							else
								parentArgs[params[1]] = wikidataIds[1]
	end	end	end	end	end	end	end
	
	--Configured rows
	local rct = 0
	for _, params in ipairs( p.conf ) do
		local val = parentArgs[params[1]]
		if val and val ~= '' then
			table.insert( elements, p.createRow( params[1], params[2]..':', val, params[4]( val ), true, params.category ) )
			rct = rct + 1
		end
	end
	
	--WorldCat
	local worldcatId = parentArgs['WORLDCATID']
	if worldcatId and worldcatId ~= '' then --if present & unsuppressed
		table.insert( elements, p.createRow( 'WORLDCATID', '', worldcatId, '[[WorldCat Identities]]: [https://www.worldcat.org/identities/'..worldcatId..' '..worldcatId..']', false ) ) --Validation?
		worldcatCat = '[[Category:Wikipedia articles with WorldCat identifiers]]'
	elseif worldcatId == nil then --if absent & unsuppressed
		local viafId = parentArgs['VIAF']
		local lccnId = parentArgs['LCCN']
		if viafId and viafId ~= '' and p.viafLink( viafId ) then --VIAF must be present, unsuppressed, & validated
			table.insert( elements, p.createRow( 'VIAF', '', viafId, '[[WorldCat Identities]] (via VIAF): [https://www.worldcat.org/identities/containsVIAFID/'..viafId..' '..viafId..']', false ) )
			if namespace == 0 then 
				worldcatCat = '[[Category:Wikipedia articles with WorldCat-VIAF identifiers]]'
			end
		elseif lccnId and lccnId ~= '' and p.lccnLink( lccnId ) then --LCCN must be present, unsuppressed, & validated
			local lccnParts = p.splitLccn( lccnId )
			if lccnParts and lccnParts[1] ~= 'sh' then
				local lccnIdFmtd = lccnParts[1]..lccnParts[2]..'-'..lccnParts[3]
				table.insert( elements, p.createRow( 'LCCN', '', lccnId, '[[WorldCat Identities]] (via LCCN): [https://www.worldcat.org/identities/lccn-'..lccnIdFmtd..' '..lccnIdFmtd..']', false ) )
				if namespace == 0 then 
					worldcatCat = '[[Category:Wikipedia articles with WorldCat-LCCN identifiers]]'
				end
			end
		end
	elseif worldcatId == '' then --if suppressed
		suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|WORLDCATID]]'
	end
	
	local Navbox = require('Module:Navbox')
	local elementsCat = ''
	if rct >= 20 then
		local catName = 'AC with '..rct..' elements'
		elementsCat  = '[[Category:'..catName..']]'..p.redCatLink(catName)
	end
	
	local outString = ''
	if #elements > 0 then
		local args = {}
		if testcases and itemId then args = { qid = itemId } end --expensive
		local pencil = frame:expandTemplate{ title = 'EditAtWikidata', args = args}
		outString = Navbox._navbox( {
			name  = 'Authority control',
			navboxclass = 'authority-control',
			bodyclass = 'hlist',
			group1 = '[[Help:Authority control|Authority control]]'..pencil,
			list1 = table.concat( elements )
			} )
		local auxCats = worldcatCat .. elementsCat .. suppressedIdCat .. deprecatedIdCat
		if testcases then
			auxCats = mw.ustring.gsub(auxCats, '(%[%[)(Category)', '%1:%2') --for easier checking
		end
		outString = outString .. auxCats
		if namespace ~= 0 then
			outString = mw.ustring.gsub(outString, '(%[%[)(Category:Wikipedia articles)', '%1:%2') --by definition
		end
	end
	
	return outString
end

return p