Difference between revisions of "Module:Authority control"

From All About Ayrshire
Jump to: navigation, search
m (1 revision)
en>Matthiaspaul
(added comment regarding usage of custom inter-wiki prefixes instead of external links per m:Interwiki_map (example :VIAF:))
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
    if namespace == 0 then
+
local testcases = (string.sub(title.subpageText,1,9) == 'testcases')
        return '[[Category:Wikipedia articles with ' .. id .. ' identifiers]]'
+
 
    elseif namespace == 2 and not title.isSubpage then
+
--[[==========================================================================]]
        return '[[Category:User pages with ' .. id .. ' identifiers]]'
+
--[[                            Category functions                            ]]
    else
+
--[[==========================================================================]]
         return '[[Category:Miscellaneous pages with ' .. id .. ' identifiers]]'
+
 
    end
+
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
 +
 
 +
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
 +
 
 +
--[[==========================================================================]]
 +
--[[                      Property formatting functions                      ]]
 +
--[[==========================================================================]]
 +
 
 +
-- If a link has a suitable entry in the global inter-wiki prefix table at [[:m:Interwiki_map]], please consider routing through this prefix rather than as external link URL. This will ease future maintenance as necessary updates to the link can be centrally carried out there rather than by updating this module. The "external link" icon would disappear for such entries.
 +
 
 +
function p.aagLink( id )
 +
--P3372's format regex: \d+ (e.g. 1)
 +
if not id:match( '^%d+$' ) then
 +
return false
 +
end
 +
return '[https://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 id:match( '^%d%d%d%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[https://dl.acm.org/profile/'..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 id:match( '^[a-z][-a-z]+-[1-2]%d%d?%d?%d?$' ) and
 +
  not id:match( '^[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 id:match( '^[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 id:match( '^[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 id:match( '^[A-Z][A-Z][A-Z]%d%d%d%d[a-z]$' ) and
 +
  not id:match( '^[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.balatLink( id )
 +
--P3293's format regex: \d+ (e.g. 1)
 +
if not id:match( '^%d+$' ) then
 +
return false
 +
end
 +
return '[http://balat.kikirpa.be/object/104257'..id..' '..id..']'..p.getCatForId( 'BALaT' ) --no https as of 9/2019
 +
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 id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) and
 +
  not id:match( '^[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.bildLink( id )
 +
--P2092's format regex: \d+ (e.g. 1)
 +
if not id:match( '^%d+$' ) then
 +
return false
 +
end
 +
return '[https://www.bildindex.de/document/obj'..id..' '..id..']'..p.getCatForId( 'Bildindex' )
 +
end
 +
 
 +
function p.bncLink( id )
 +
--P1890's format regex: \d{9} (e.g. 123456789)
 +
if not id:match( '^%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
 +
 
 +
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 id:match( '^[XF][XF]%d%d%d%d%d?%d?%d?$' ) and
 +
  not id:match( '^a%d%d%d%d%d?%d?%d?$' ) and
 +
  not id:match( '^bi[mcsv][aoei]%d%d%d%d%d%d%d%d%d%d$' ) and
 +
  not id:match( '^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 as of 9/2019
 +
end
 +
 
 +
function p.bnfLink( id )
 +
--P268's format regex: \d{8}[0-9bcdfghjkmnpqrstvwxz] (e.g. 123456789)
 +
if not id:match( '^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 id:match( '^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.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.bpnLink( id )
 +
--P651's format regex: \d{6,8} (e.g. 00123456)
 +
if not id:match( '^%d%d%d%d%d%d%d%d$' ) and --original format regex, changed 8/2019 to
 +
  not id:match( '^0?%d%d%d%d%d%d%d$' ) and --allow 1-2 leading 0s, allowed by the website
 +
  not id:match( '^0?0?%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[http://www.biografischportaal.nl/en/persoon/'..id..' '..id..']'..p.getCatForId( 'BPN' ) --no https as of 9/2019
 +
end
 +
 
 +
function p.canticLink( id )
 +
--P1273's format regex: a\d{7}[0-9x] (e.g. a10640745)
 +
if not id:match( '^a%d%d%d%d%d%d%d[%dx]$' ) then
 +
return false
 +
end
 +
return '[http://cantic.bnc.cat/registres/CUCId/'..id..' '..id..']'..p.getCatForId( 'CANTIC' ) --no https as of 10/2019
 +
end
 +
 
 +
function p.ciniiLink( id )
 +
--P271's format regex: DA\d{7}[\dX] (e.g. DA12345678)
 +
if not id:match( '^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.cwgcLink( id )
 +
--P1908's format regex: [1-9]\d* (e.g. 75228351)
 +
if not id:match( '^[1-9]%d*$' ) then
 +
return false
 +
end
 +
return '[https://www.cwgc.org/find-war-dead/casualty/'..id..'/ '..id..']'..p.getCatForId( 'CWGC' )
 +
end
 +
 
 +
function p.daaoLink( id )
 +
--P1707's format regex: [a-z\-]+\d* (e.g. rolf-harris)
 +
if not id:match( '^[a-z%-]+%d*$' ) then
 +
return false
 +
end
 +
return '[https://www.daao.org.au/bio/'..id..' '..id..']'..p.getCatForId( 'DAAO' )
 +
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 id:match( '^%d%d%d?/%d+$' ) and
 +
  not id:match( '^%d%d%d?/%d+%-%d+$' ) and
 +
  not id:match( '^[a-z]/[a-zA-Z][0-9A-Za-z]*$' ) and
 +
  not id:match( '^[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.dsiLink( id )
 +
--P2349's format regex: [1-9]\d* (e.g. 1538)
 +
if not id:match( '^[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.fnzaLink( id )
 +
--P6792's format regex: [1-9]\d* (e.g. 9785)
 +
if not id:match( '^[1-9]%d*$' ) then
 +
return false
 +
end
 +
return '[https://findnzartists.org.nz/artist/'..id..'/ '..id..']'..p.getCatForId( 'FNZA' )
 +
end
 +
 
 +
function p.gndLink( id )
 +
--P227's format regex: 1[012]?\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 id:match( '^1[012]?%d%d%d%d%d%d%d[0-9X]$' ) and
 +
  not id:match( '^[47]%d%d%d%d%d%d%-%d$' ) and
 +
  not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%-[0-9X]$' ) and
 +
  not id:match( '^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.hdsLink( id )
 +
--P902's format regex: \d{6} (e.g. 050123)
 +
if not id:match( '^%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[https://hls-dhs-dss.ch/fr/articles/'..id..' '..id..']'..p.getCatForId( 'HDS' )
 +
end
 +
 
 +
function p.iaafLink( id )
 +
--P1146's format regex: [0-9][0-9]* (e.g. 012)
 +
if not id:match( '^%d+$' ) then
 +
return false
 +
end
 +
return '[https://www.iaaf.org/athletes/_/'..id..' '..id..']'..p.getCatForId( 'IAAF' )
 +
end
 +
 
 +
function p.iciaLink( id )
 +
--P1736's format regex: \d+ (e.g. 1)
 +
if not id:match( '^%d+$' ) then
 +
return false
 +
end
 +
return '[https://www.imj.org.il/artcenter/newsite/en/?artist='..id..' '..id..']'..p.getCatForId( 'ICIA' )
 +
end
 +
 
 +
function p.isniLink( id )
 +
id = p.validateIsni( id ) --e.g. 0000-0000-6653-4145
 +
if not id then
 +
return false
 +
end
 +
return '[https://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 as of 9/2019
 +
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 id:match( regex ) then
 +
return false
 +
end
 +
return '[https://www.pop.culture.gouv.fr/notice/joconde/'..id..' '..id..']'..p.getCatForId( 'Joconde' )
 +
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 id:match( '^%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 as of 9/2019
 +
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.lirLink( id )
 +
--P886's format regex: \d+ (e.g. 1)
 +
if not id:match( '^%d+$' ) then
 +
return false
 +
end
 +
return '[http://www.e-lir.ch/e-LIR___Lexicon.'..id..'.450.0.html '..id..']'..p.getCatForId( 'LIR' ) --no https as of 9/2019
 +
end
 +
 
 +
function p.lnbLink( id )
 +
--P1368's format regex: \d{9} (e.g. 123456789)
 +
if not id:match( '^%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.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 as of 9/2019
 +
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 id:match( '^%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 id:match( '^%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 id:match( '^%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
 
end
  
local function viafLink( id )
+
function p.mblLink( id )
    if not string.match( id, '^%d+$' ) then
+
--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)
        return false
+
if not id:match( '^%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
    end
+
return false
    return '[https://viaf.org/viaf/' .. id .. ' ' .. id .. ']' .. getCatForId( 'VIAF' )
+
end
 +
return '[https://musicbrainz.org/label/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz label' ) --special category name
 
end
 
end
  
local function kulturnavLink( id )
+
function p.mbpLink( id )
    return '[http://kulturnav.org/language/en/' .. id .. ' 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 id:match( '^%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
 
end
  
local function sikartLink( id )
+
function p.mbrgLink( id )
    return '[http://www.sikart.ch/KuenstlerInnen.aspx?id=' .. id .. '&lng=en ' .. 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 id:match( '^%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 id:match( '^%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 id:match( '^%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
 +
 
 +
function p.mgpLink( id )
 +
--P549's format regex: \d{1,6} (e.g. 123456)
 +
if not id:match( '^%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.naraLink( id )
 +
--P1225's format regex: ^([1-9]\d{0,8})$ (e.g. 123456789)
 +
if not id:match( '^[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.nclLink( id )
 +
--P1048's format regex: \d+ (e.g. 1081436)
 +
if not id:match( '^%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 as of 9/2019
 +
end
 +
 
 +
function p.ndlLink( id )
 +
--P349's format regex: 0?\d{8} (e.g. 012345678)
 +
if not id:match( '^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 id:match( '^%d+$' ) then
 +
return false
 +
end
 +
return '[https://www.ngv.vic.gov.au/explore/collection/artist/'..id..'/ '..id..']'..p.getCatForId( 'NGV' )
 
end
 
end
  
local function tlsLink( id )
+
function p.nkcLink( id )
local id2 = mw.ustring.gsub(id, '%s', function(s) return mw.uri.encode(s, 'WIKI') end)
+
--P691's format regex: [a-z]{2,4}[0-9]{2,14} (e.g. abcd12345678901234)
    return '[http://tls.theaterwissenschaft.ch/wiki/' .. id2 .. ' ' .. id .. ']'  
+
if not id:match( '^[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
  
 +
function p.nlaLink( id )
 +
--P409's format regex: [1-9][0-9]{0,11} (e.g. 123456789012)
 +
if not id:match( '^[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
  
local function ciniiLink( id )
+
function p.nlgLink( id )
    return '[http://ci.nii.ac.jp/author/' .. id .. '?l=en ' .. id .. ']'  
+
--P3348's format regex: [1-9]\d* (e.g. 1)
 +
if not id:match( '^[1-9]%d*$' ) then
 +
return false
 +
end
 +
return '[https://data.nlg.gr/resource/authority/record'..id..' '..id..']'..p.getCatForId( 'NLG' )
 
end
 
end
  
local function bneLink( id )
+
function p.nliLink( id )
    return '[http://catalogo.bne.es/uhtbin/authoritybrowse.cgi?action=display&authority_id=' .. id .. ' ' .. id .. ']'  
+
--P949's format regex: \d{9} (e.g. 123456789)
 +
if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[http://uli.nli.org.il/F/?func=direct&doc_number='..id..'&local_base=nlx10'..' '..id..']'..p.getCatForId( 'NLI' )
 
end
 
end
  
 +
function p.nlkLink( id )
 +
--P5034's format regex: KA.(19|20).{7} (e.g. KAC201501465)
 +
if not id:match( '^KA.19.......$' ) and
 +
  not id:match( '^KA.20.......$' ) then
 +
return false
 +
end
 +
return '[https://nl.go.kr/authorities/resource/'..id..' '..id..']'..p.getCatForId( 'NLK' )
 +
end
  
local function uscongressLink( id )
+
function p.nlpLink( id )
    return '[http://bioguide.congress.gov/scripts/biodisplay.pl?index=' .. id .. ' ' .. id .. ']'  
+
--P1695's format regex: 9810[0-9]\d* or A[0-9]{7}[0-9X] (e.g. 9810123456789012345 or A10414836)
 +
if not id:match( '^9810%d+$' ) and
 +
  not id:match( '^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 narapersonLink( id )
+
function p.nlrLink( id )
    return '[https://research.archives.gov/person/' .. id .. ' ' .. id .. ']'  
+
--P1003's format regex: \d{9} (e.g. 123456789)
 +
if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
 +
return false
 +
end
 +
return '[http://aleph.bibnat.ro:8991/F/?func=direct&local_base=NLR10&doc_number='..id..']'..p.getCatForId( 'NLR' )
 
end
 
end
  
local function naraorganizationLink( id )
+
function p.nskLink( id )
    return '[https://research.archives.gov/organization/' .. id .. ' ' .. id .. ']'  
+
--P1375's format regex: \d{9} (e.g. 123456789)
 +
if not id:match( '^%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 as of 9/2019
 
end
 
end
  
local function botanistLink( id )
+
function p.ntaLink( id )
local id2 = mw.ustring.gsub(id, '%s', function(s) return mw.uri.encode(s, 'PATH') end)
+
--P1006's format regex: \d{8}[\dX] (e.g. 12345678X)
    return '[http://www.ipni.org/ipni/advAuthorSearch.do?find_abbreviation=' .. id2 .. ' ' .. id .. ']'  
+
if not id:match( '^%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 mgpLink( id )
+
function p.orcidLink( id )
    -- TODO Implement some sanity checking regex
+
id = p.validateIsni( id ) --e.g. 0000-0002-7398-5483
    return '[http://www.genealogy.ams.org/id.php?id=' .. id .. ' ' .. id .. ']'  
+
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
 
end
  
local function rslLink( id )
+
function p.picLink( id )
    -- TODO Implement some sanity checking regex
+
--P2750's format regex: [1-9]\d* (e.g. 1)
    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 id:match( '^[1-9]%d*$' ) then
 +
return false
 +
end
 +
return '[https://pic.nypl.org/constituents/'..id..' '..id..']'..p.getCatForId( 'PIC' )
 
end
 
end
  
local function leonoreLink( id )
+
function p.ridLink( id )
-- Identifiants allant de LH/1/1 à LH/2794/54 (légionnaires)
+
--P1053's format regex: [A-Z]{1,3}-\d{4}-20[0-2]\d  (e.g. AAS-5150-2020)
-- Identifiants allant de C/0/1 à C/0/84 (84 légionnaires célèbres)
+
if not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-20[0-2]%d$' ) then
-- 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)
+
return false
    if not string.match( id, '^LH/%d%d?%d?%d?/%d%d?%d?$' ) and
+
end
      not string.match( id, '^C/0/%d%d?$' ) and
+
return '[https://www.researcherid.com/rid/'..id..' '..id..']'..p.getCatForId( 'RID' )
  not string.match( id, '^19800035/%d%d?%d?%d?/%d%d?%d?%d?%d?$' ) then
 
        return false
 
    end
 
    return '[//www.culture.gouv.fr/public/mistral/leonore_fr?ACTION=CHERCHER&FIELD_1=COTE&VALUE_1=' .. id .. ' ' .. id .. ']'  
 
 
end
 
end
  
local function sbnLink( id )
+
function p.reroLink( 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
+
--P3065's format regex: 0[1-2]-[A-Z0-9]{1,10} (e.g. 02-A012345678)
        return false
+
if not id:match( '^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
    end
+
return false
    return '[http://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid=' .. id .. ' ' .. id .. ']' .. getCatForId( 'SBN' )
+
end
 +
return '[http://data.rero.ch/'..id..' '..id..']'..p.getCatForId( 'RERO' )
 
end
 
end
  
local function nkcLink( id )
+
function p.rkdartistsLink( id )
return '[http://aleph.nkp.cz/F/?func=find-c&local_base=aut&ccl_term=ica=' .. id .. '&CON_LNG=ENG ' .. id .. ']'  
+
--P650's format regex: [1-9]\d{0,5} (e.g. 123456)
 +
if not id:match( '^[1-9]%d?%d?%d?%d?%d?$' ) then
 +
return false
 +
end
 +
return '[https://rkd.nl/en/explore/artists/'..id..' '..id..']'..p.getCatForId( 'RKDartists' )
 
end
 
end
  
local function nclLink( id )
+
function p.rkdidLink( id )
    if not string.match( id, '^%d+$' ) then
+
--P350's format regex: [1-9]\d{0,5} (e.g. 123456)
        return false
+
if not id:match( '^[1-9]%d?%d?%d?%d?%d?$' ) then
    end
+
return false
    return '[http://aleweb.ncl.edu.tw/F/?func=accref&acc_sequence=' .. id .. '&CON_LNG=ENG ' .. id .. ']'  
+
end
 +
return '[https://rkd.nl/nl/explore/images/'..id..' '..id..']'..p.getCatForId( 'RKDID' )
 
end
 
end
  
local function ndlLink( id )
+
function p.rslLink( id )
return '[http://id.ndl.go.jp/auth/ndlna/' .. id .. ' ' .. id .. ']'  
+
--P947's format regex: \d{1,9} (e.g. 123456789)
 +
if not id:match( '^%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 as of 9/2019
 
end
 
end
  
local function sudocLink( id )
+
function p.iccuLink( id )
    if not string.match( id, '^%d%d%d%d%d%d%d%d[%dxX]$' ) then
+
--P396's format regex: IT\\ICCU\\(\d{10}|\D\D[\D\d]\D\\\d{6}) (e.g. IT\ICCU\CFIV\000163)
        return false
+
if not id:match( '^IT\\ICCU\\%d%d%d%d%d%d%d%d%d%d$' ) and
    end
+
  not id:match( '^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 '[http://www.idref.fr/' .. id .. ' ' .. id .. ']'  
+
return false
 +
end
 +
return '[https://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid='..id..' '..id..']'..p.getCatForId( 'ICCU' ) end
 +
 
 +
function p.selibrLink( id )
 +
--P906's format regex: [1-9]\d{4,5} (e.g. 123456)
 +
if not id:match( '^[1-9]%d%d%d%d%d?$' ) then
 +
return false
 +
end
 +
return '[https://libris.kb.se/auth/'..id..' '..id..']'..p.getCatForId( 'SELIBR' )
 
end
 
end
  
local function hlsLink( id )
+
function p.sikartLink( id )
    if not string.match( id, '^%d+$' ) then
+
--P781's format regex: \d{7,9} (e.g. 123456789)
        return false
+
if not id:match( '^%d%d%d%d%d%d%d%d?%d?$' ) then
    end
+
return false
    return '[http://www.hls-dhs-dss.ch/textes/f/F' .. id .. '.php ' .. id .. ']'
+
end
 +
return '[http://www.sikart.ch/KuenstlerInnen.aspx?id='..id..'&lng=en '..id..']'..p.getCatForId( 'SIKART' ) --no https as of 9/2019
 
end
 
end
  
local function lirLink( id )
+
function p.snacLink( id )
    if not string.match( id, '^%d+$' ) then
+
--P3430's format regex: \d*[A-Za-z][0-9A-Za-z]* (e.g. A)
        return false
+
if not id:match( '^%d*[A-Za-z][0-9A-Za-z]*$' ) then
    end
+
return false
    return '[http://www.e-lir.ch/e-LIR___Lexicon.' .. id .. '.450.0.html ' .. id .. ']'
+
end
 +
return '[https://snaccooperative.org/ark:/99166/'..id..' '..id..']'..p.getCatForId( 'SNAC-ID' )
 
end
 
end
  
local function splitLccn( id )
+
function p.sudocLink( id )
    if id:match( '^%l%l?%l?%d%d%d%d%d%d%d%d%d?%d?$' ) then
+
--P269's format regex: (\d{8}[\dX]|) (e.g. 026927608)
        id = id:gsub( '^(%l+)(%d+)(%d%d%d%d%d%d)$', '%1/%2/%3' )
+
if not id:match( '^%d%d%d%d%d%d%d%d[%dxX]$' ) then --legacy: allow lowercase 'x'
    end
+
return false
    if id:match( '^%l%l?%l?/%d%d%d?%d?/%d+$' ) then
+
end
        return mw.text.split( id, '/' )
+
return '[https://www.idref.fr/'..id..' '..id..']'..p.getCatForId( 'SUDOC' )
    end
 
    return false
 
 
end
 
end
  
local function append(str, c, length)
+
function p.s2authoridLink( id )
    while str:len() < length do
+
--P4012's format regex: [1-9]\d* (e.g. 1796130)
        str = c .. str
+
if not id:match( '^[1-9]%d*$' ) then
    end
+
return false
    return str
+
end
 +
return '[https://www.semanticscholar.org/author/'..id..' '..id..']'..p.getCatForId( 'Semantic Scholar author' ) --special category name
 
end
 
end
  
local function lccnLink( id )
+
function p.ta98Link( id )
    local parts = splitLccn( id )
+
--P1323's format regex: A\d{2}\.\d\.\d{2}\.\d{3}[FM]? (e.g. A12.3.45.678)
    if not parts then
+
if not id:match( '^A%d%d%.%d%.%d%d%.%d%d%d[FM]?$' ) then
        return false
+
return false
    end
+
end
    local lccnType = parts[1] ~= 'sh' and 'names' or 'subjects'
+
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' )
    id = parts[1] .. parts[2] .. append( parts[3], '0', 6 )
 
    return '[http://id.loc.gov/authorities/' .. lccnType .. '/' .. id .. ' ' .. id .. ']' .. getCatForId( 'LCCN' )
 
 
end
 
end
  
local function mbLink( id )
+
function p.tdviaLink( id )
    -- TODO Implement some sanity checking regex
+
--P7314's format regex: [a-z/-]+] (e.g. barkan-omer-lutfi)
    return '[//musicbrainz.org/artist/' .. id .. ' ' .. id .. ']' .. getCatForId( 'MusicBrainz' )
+
if not id:match( '^[a-z/-]+$' ) then
 +
return false
 +
end
 +
return '[https://islamansiklopedisi.org.tr/'..id..' '..id..']'..p.getCatForId( 'TDVİA' )
 
end
 
end
  
--Returns the ISNI check digit isni must be a string where the 15 first elements are digits
+
function p.teLink( id )
local function getIsniCheckDigit( isni )
+
--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 total = 0
+
local e1, e2 = id:match( '^E([1-8])%.(%d%d?)%.%d%d?%.%d%d?%.%d%.%d%.%d%d?%d?$' )
    for i = 1, 15 do
+
if not e1 then
        local digit = isni:byte( i ) - 48 --Get integer value
+
return false
        total = (total + digit) * 2
+
end
    end
+
local TEnum = 'TEe0'..e1 --no formatter URL in WD, probably due to this complexity
    local remainder = total % 11
+
if e1 == '5' or e1 == '7' then
    local result = (12 - remainder) % 11
+
if #e2 == 1 then e2 = '0'..e2 end
    if result == 10 then
+
TEnum = TEnum..e2
        return "X"
+
end
    end
+
return '[http://www.unifr.ch/ifaa/Public/EntryPage/ViewTE/'..TEnum..'.html '..id..']'..p.getCatForId( 'TE' )
    return tostring( result )
 
 
end
 
end
  
--Validate ISNI (and ORCID) and retuns it as a 16 characters string or returns false if it's invalid
+
function p.tepapaLink( id )
--See http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
+
--P3544's format regex: \d+ (e.g. 1)
local function validateIsni( id )
+
if not id:match( '^%d+$' ) then
    id = id:gsub( '[ %-]', '' ):upper()
+
return false
    if not id:match( '^%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d[%dX]$' ) then
+
end
        return false
+
return '[https://collections.tepapa.govt.nz/agent/'..id..' '..id..']'..p.getCatForId( 'TePapa' )
    end
 
    if getIsniCheckDigit( id ) ~= string.char( id:byte( 16 ) ) then
 
        return false
 
    end
 
    return id
 
 
end
 
end
  
local function isniLink( id )
+
function p.thLink( id )
    id = validateIsni( id )
+
--P1694's format regex: H\d\.\d{2}\.\d{2}\.\d\.\d{5} (e.g. H1.23.45.6.78901)
    if not id then
+
local h1, h2 = id:match( '^H(%d)%.(%d%d)%.%d%d%.%d%.%d%d%d%d%d$' )
        return false
+
if not h1 then
    end
+
return false
    return '[http://isni-url.oclc.nl/isni/' .. id .. ' ' .. id:sub( 1, 4 ) .. ' ' .. id:sub( 5, 8 ) .. ' '  .. id:sub( 9, 12 ) .. ' .. id:sub( 13, 16 ) .. ']' .. getCatForId( 'ISNI' )
+
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
 
end
  
local function orcidLink( id )
+
function p.tlsLink( id )
    id = validateIsni( id )
+
local id2 = id:gsub(' +', '_')
    if not id then
+
--P1362's format regex: \p{Lu}[\p{L}\d_',\.\-\(\)\*/–]{3,59} (e.g. Abcd)
        return false
+
local class = "[%a%d_',%.%-%(%)%*/–]"
    end
+
local regex = "^%u"..string.rep(class, 3)..string.rep(class.."?", 56).."$"
    id = id:sub( 1, 4 ) .. '-' .. id:sub( 5, 8 ) .. '-'  .. id:sub( 9, 12 ) .. '-'  .. id:sub( 13, 16 )
+
if not mw.ustring.match( id2, regex ) then
    return '[http://orcid.org/' .. id .. ' ' .. id .. ']' .. getCatForId( 'ORCID' )
+
return false
 +
end
 +
return '[http://tls.theaterwissenschaft.ch/wiki/'..id2..' '..id..']'..p.getCatForId( 'TLS' ) --no https as of 9/2019
 
end
 
end
  
local function gndLink( id )
+
function p.troveLink( id )
    return '[http://d-nb.info/gnd/' .. id .. ' ' .. id .. ']' .. getCatForId( 'GND' )
+
--P1315's format regex: [1-9]\d{5,7} (e.g. 12345678)
 +
if not id:match( '^[1-9]%d%d%d%d%d%d?%d?$' ) then
 +
return false
 +
end
 +
return '[https://trove.nla.gov.au/people/'..id..' '..id..']'..p.getCatForId( 'Trove' )
 
end
 
end
  
local function selibrLink( id )
+
function p.ukparlLink( id )
if not string.match( id, '^%d+$' ) then
+
--P6213's format regex: [a-zA-Z\d]{8} (e.g. AQUupyiR)
        return false
+
if not id:match( '^[a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d]$' ) then
    end
+
return false
    return '[//libris.kb.se/auth/' .. id .. ' ' .. id .. ']' .. getCatForId( 'SELIBR' )
+
end
 +
return '[https://id.parliament.uk/'..id..' '..id..']'..p.getCatForId( 'UKPARL' )
 
end
 
end
  
local function bnfLink( id )
+
function p.ulanLink( id )
    --Add cb prefix if it has been removed
+
--P245's format regex: 500\d{6} (e.g. 500123456)
    if not string.match( id, '^cb.+$' ) then
+
if not id:match( '^500%d%d%d%d%d%d$' ) then
        id = 'cb' .. id
+
return false
    end
+
end
 +
return '[https://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid='..id..' '..id..']'..p.getCatForId( 'ULAN' )
 +
end
  
    return '[http://catalogue.bnf.fr/ark:/12148/' .. id .. ' ' .. id .. '] [http://data.bnf.fr/ark:/12148/' .. id .. ' (data)]' .. getCatForId( 'BNF' )
+
function p.uscongressLink( id )
 +
--P1157's format regex: [A-Z]00[01]\d{3} (e.g. A000123)
 +
if not id:match( '^[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 as of 9/2019
 
end
 
end
  
local function bpnLink( id )
+
function p.vcbaLink( id )
    if not string.match( id, '^%d+$' ) then
+
--P8034's format regex: \d{3}\/[1-9]\d{0,5} (e.g. 494/9793)
        return false
+
if not id:match( '^%d%d%d\/[1-9]%d?%d?%d?%d?%d?$' ) then
    end
+
return false
    return '[http://www.biografischportaal.nl/en/persoon/' .. id .. ' ' .. id .. ']' .. getCatForId( 'BPN' )
+
end
 +
local id2 = id:gsub('\/', '_')
 +
return '[https://opac.vatlib.it/auth/detail/'..id2..' '..id..']'..p.getCatForId( 'VcBA' )
 
end
 
end
  
local function ridLink( id )
+
function p.viafLink( id )
    return '[http://www.researcherid.com/rid/' .. id .. ' ' .. id .. ']' .. getCatForId( 'RID' )
+
--P214's format regex: [1-9]\d(\d{0,7}|\d{17,20}) (e.g. 123456789, 1234567890123456789012)
 +
if not id:match( '^[1-9]%d%d?%d?%d?%d?%d?%d?%d?$' ) and
 +
  not id:match( '^[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
 +
-- If the "VIAF" entry at [[:m:Interwiki map]] would resolve to "https://viaf.org/viaf/$1" (rather than "http://viaf.org/viaf/$1", as it currently still does), the code below could change from '[https://viaf.org/viaf/'..id..' '..id..']' to '[[:VIAF:'..id..'|'..id..']]'.
 +
return '[https://viaf.org/viaf/'..id..' '..id..']'..p.getCatForId( 'VIAF' )
 
end
 
end
  
local function bibsysLink( id )
+
--[[=========================== Helper functions =============================]]
    return '[http://ask.bibsys.no/ask/action/result?cmd=&kilde=biblio&cql=bs.autid+%3D+' .. id .. '&feltselect=bs.autid ' .. id .. ']' .. getCatForId( 'BIBSYS' )
+
 
 +
function p.append(str, c, length)
 +
while str:len() < length do
 +
str = c .. str
 +
end
 +
return str
 
end
 
end
  
local function ulanLink( id )
+
--Returns the ISNI check digit isni must be a string where the 15 first elements are digits, e.g. 0000000066534145
    return '[//www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid=' .. id .. ' ' .. id .. ']' .. getCatForId( 'ULAN' )
+
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
 
end
  
local function nlaLink( id )
+
--Validate ISNI (and ORCID) and retuns it as a 16 characters string or returns false if it's invalid
return '[//nla.gov.au/anbd.aut-an' .. id .. ' ' .. id .. ']' .. getCatForId( 'NLA' )
+
--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
 
end
  
local function rkdartistsLink( id )
+
function p.splitLccn( id )
return '[https://rkd.nl/en/explore/artists/' .. id .. ' ' .. id .. ']' .. getCatForId( 'RKDartists' )
+
--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
 
end
  
local function getIdsFromWikidata( item, property )
+
--[[==========================================================================]]
    local ids = {}
+
--[[                    Wikidata & documentation functions                    ]]
    if not item.claims[property] then
+
--[[==========================================================================]]
        return ids
+
 
    end
+
function p.getIdsFromWikidata( itemId, property )
    for _, statement in pairs( item.claims[property] ) do
+
local ids = {}
if statement.mainsnak.datavalue then
+
local statements = mw.wikibase.getBestStatements( itemId, property )
table.insert( ids, statement.mainsnak.datavalue.value )
+
if statements then
 +
for _, statement in ipairs( statements ) do
 +
if statement.mainsnak.datavalue then
 +
table.insert( ids, statement.mainsnak.datavalue.value )
 +
end
 
end
 
end
    end
+
end
    return ids
+
return ids
 
end
 
end
  
local function matchesWikidataRequirements( item, reqs )
+
function p.matchesWikidataRequirements( itemId, reqs )
    for _, group in pairs( 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]
        if item.claims[property] ~= nil then
+
local statements = mw.wikibase.getBestStatements( itemId, property )
            for _, statement in pairs ( item.claims[property] ) do
+
if statements then
            if statement.mainsnak.datavalue ~= nil then
+
for _, statement in ipairs( statements ) do
                if statement.mainsnak.datavalue.value['numeric-id'] == qid then
+
if statement.mainsnak.datavalue then
                    return true
+
if statement.mainsnak.datavalue.value['numeric-id'] == qid then
                end
+
return true
            end
+
end end end end end
            end
+
return false
        end
 
    end
 
    return false
 
 
end
 
end
  
local function createRow( id, label, rawValue, link, withUid )
+
-- Creates a human-readable standalone wikitable version of p.conf, and tracking categories with page counts, for use in the documentation
    if link then
+
function p.docConfTable( frame )
        if withUid then
+
local wikiTable = '{| class="wikitable sortable"\n'..
            return '* ' .. label .. ' <span class="uid">' .. link .. '</span>\n'
+
  '! rowspan=2 | Parameter\n'..
        else
+
  '! rowspan=2 | Label\n'..
            return '* ' .. label .. ' ' .. link .. '\n'
+
  '! rowspan=2; data-sort-type=number | Wikidata property\n'..
        end
+
  '! colspan=4 | Tracking categories and page counts\n'..
    else
+
  '|-\n'..
        return '* <span class="error">The ' .. id .. ' id ' .. rawValue .. ' is not valid.</span>[[Category:Wikipedia articles with faulty authority control identifiers (' .. id .. ')]]\n'
+
  '! [[:Category:Wikipedia articles with authority control information|'..      'Articles]]\n'..
    end
+
  '! [[:Category:User pages with authority control information|'..              'User pages]]\n'..
 +
  '! [[:Category:Miscellaneous pages with authority control information|'..      'Misc. pages]]\n'..
 +
  '! [[:Category:Wikipedia articles with faulty authority control information|'..'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
 
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 },
+
-- If a specific "(identifier) redirect" exists for an identifier, please route through this particular redirect rather than linking directly to the target page. This reduces clutter in "What links here" and improves reverse lookup of articles where a manifestation of this particular identifier is used.
    { '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', '[[:fr: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 },
 
}
 
  
 
-- 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', '[[AAG (identifier)|AAG]]', 3372, p.aagLink },
 +
{ 'ACM-DL', '[[ACM DL (identifier)|ACM DL]]', 864, p.acmLink },
 +
{ 'ADB', '[[ADB (identifier)|ADB]]', 1907, p.adbLink },
 +
{ 'AGSA', '[[AGSA (identifier)|AGSA]]', 6804, p.agsaLink },
 +
{ 'autores.uy', '[[autores.uy (identifier)|autores.uy]]', 2558, p.autoresuyLink },
 +
{ 'AWR', '[[AWR (identifier)|AWR]]', 4186, p.awrLink },
 +
{ 'BALaT', '[[BALaT (identifier)|BALaT]]', 3293, p.balatLink },
 +
{ 'BIBSYS', '[[BIBSYS (identifier)|BIBSYS]]', 1015, p.bibsysLink },
 +
{ 'Bildindex', '[[Bildindex (identifier)|Bildindex]]', 2092, p.bildLink },
 +
{ 'BNC', '[[BNC (identifier)|BNC]]', 1890, p.bncLink },
 +
{ 'BNE', '[[BNE (identifier)|BNE]]', 950, p.bneLink },
 +
{ 'BNF', '[[BNF (identifier)|BNF]]', 268, p.bnfLink },
 +
{ 'Botanist', '[[Botanist (identifier)|Botanist]]', 428, p.botanistLink },
 +
{ 'BPN', '[[BPN (identifier)|BPN]]', 651, p.bpnLink },
 +
{ 'CANTIC', '[[CANTIC (identifier)|CANTIC]]', 1273, p.canticLink },
 +
{ 'CINII', '[[CiNii (identifier)|CiNii]]', 271, p.ciniiLink },
 +
{ 'CWGC', '[[CWGC (identifier)|CWGC]]', 1908, p.cwgcLink },
 +
{ 'DAAO', '[[DAAO (identifier)|DAAO]]', 1707, p.daaoLink },
 +
{ 'DBLP', '[[DBLP (identifier)|DBLP]]', 2456, p.dblpLink },
 +
{ 'DSI', '[[DSI (identifier)|DSI]]', 2349, p.dsiLink },
 +
{ 'FNZA', '[[:d:Property:P6792|FNZA]]', 6792, p.fnzaLink },
 +
{ 'GND', '[[GND (identifier)|GND]]', 227, p.gndLink },
 +
{ 'HDS', '[[HDS (identifier)|HDS]]', 902, p.hdsLink },
 +
{ 'IAAF', '[[IAAF (identifier)|IAAF]]', 1146, p.iaafLink },
 +
{ 'ICCU', '[[ICCU (identifier)|ICCU]]', 396, p.iccuLink }, --formerly SBN
 +
{ 'ICIA', '[[ICIA (identifier)|ICIA]]', 1736, p.iciaLink },
 +
{ 'ISNI', '[[ISNI (identifier)|ISNI]]', 213, p.isniLink },
 +
{ 'Joconde', '[[Joconde (identifier)|Joconde]]' , 347, p.jocondeLink },
 +
{ 'KULTURNAV', '[[KulturNav (identifier)|KulturNav]]', 1248, p.kulturnavLink },
 +
{ 'LCCN', '[[LCCN (identifier)|LCCN]]', 244, p.lccnLink },
 +
{ 'LIR', '[[LIR (identifier)|LIR]]', 886, p.lirLink },
 +
{ 'LNB', '[[LNB (identifier)|LNB]]', 1368, p.lnbLink },
 +
{ 'Léonore', '[[Léonore (identifier)|Léonore]]', 640, p.leonoreLink },
 +
{ 'MBA', '[[MBA (identifier)|MBA]]', 434, p.mbaLink, category = 'MusicBrainz' }, --special category name
 +
{ 'MBAREA', '[[MBAREA (identifier)|MBAREA]]', 982, p.mbareaLink, category = 'MusicBrainz area' }, --special category name
 +
{ 'MBI', '[[MBI (identifier)|MBI]]', 1330, p.mbiLink, category = 'MusicBrainz instrument' }, --special category name
 +
{ 'MBL', '[[MBL (identifier)|MBL]]', 966, p.mblLink, category = 'MusicBrainz label' }, --special category name
 +
{ 'MBP', '[[MBP (identifier)|MBP]]', 1004, p.mbpLink, category = 'MusicBrainz place' }, --special category name
 +
{ 'MBRG', '[[MBRG (identifier)|MBRG]]', 436, p.mbrgLink, category = 'MusicBrainz release group' }, --special category name
 +
{ 'MBS', '[[MBS (identifier)|MBS]]', 1407, p.mbsLink, category = 'MusicBrainz series' }, --special category name
 +
{ 'MBW', '[[MBW (identifier)|MBW]] work', 435, p.mbwLink, category = 'MusicBrainz work' }, --special category name
 +
{ 'MGP', '[[MGP (identifier)|MGP]]', 549, p.mgpLink },
 +
{ 'NARA', '[[NARA (identifier)|NARA]]', 1225, p.naraLink },
 +
{ 'NCL', '[[NCL (identifier)|NBL]]', 1048, p.nclLink },
 +
{ 'NDL', '[[NDL (identifier)|NDL]]', 349, p.ndlLink },
 +
{ 'NGV', '[[NGV (identifier)|NGV]]', 2041, p.ngvLink },
 +
{ 'NKC', '[[NKC (identifier)|NKC]]', 691, p.nkcLink },
 +
{ 'NLA', '[[NLA (identifier)|NLA]]', 409, p.nlaLink },
 +
{ 'NLG', '[[NLG (identifier)|NLG]]', 3348, p.nlgLink },
 +
{ 'NLI', '[[NLI (identifier)|NLI]]', 949, p.nliLink },
 +
{ 'NLK', '[[NLK (identifier)|NLK]]', 5034, p.nlkLink },
 +
{ 'NLP', '[[NLP (identifier)|NLP]]', 1695, p.nlpLink },
 +
{ 'NLR', '[[NLR (identifier)|NLR]]', 1003, p.nlrLink },
 +
{ 'NSK', '[[NSK (identifier)|NSK]]', 1375, p.nskLink },
 +
{ 'NTA', '[[NTA (identifier)|NTA]]', 1006, p.ntaLink },
 +
{ 'ORCID', '[[ORCID (identifier)|ORCID]]', 496, p.orcidLink },
 +
{ 'PIC', '[[:d:Q23892012|PIC]]', 2750, p.picLink },
 +
{ 'RID', '[[RID (identifier)|ResearcherID]]', 1053, p.ridLink },
 +
{ 'RERO', '[[RERO (identifier)|RERO]]', 3065, p.reroLink },
 +
{ 'RKDartists', '[[RKDartists (identifier)|RKD]]', 650, p.rkdartistsLink },
 +
{ 'RKDID', '[[RKDID (identifier)|RKDimages ID]]', 350, p.rkdidLink },
 +
{ 'RSL', '[[RSL (identifier)|RSL]]', 947, p.rslLink },
 +
{ 'SELIBR', '[[SELIBR (identifier)|SELIBR]]', 906, p.selibrLink },
 +
{ 'SIKART', '[[SIKART (identifier)|SIKART]]', 781, p.sikartLink },
 +
{ 'SNAC-ID', '[[SNAC-ID (identifier)|SNAC]]', 3430, p.snacLink },
 +
{ 'SUDOC', '[[SUDOC (identifier)|SUDOC]]', 269, p.sudocLink },
 +
{ 'S2AuthorId', '[[S2AuthorId (identifier)|S2AuthorId]]', 4012, p.s2authoridLink, category = 'Semantic Scholar author' }, --special category name
 +
{ 'TA98', '[[TA98 (identifier)|TA98]]', 1323, p.ta98Link },
 +
{ 'TDVİA', '[[TDVİA (identifier)|TDVİA]]', 7314, p.tdviaLink },
 +
{ 'TE', '[[TE (identifier)|TE]]', 1693, p.teLink },
 +
{ 'TePapa', '[[TePapa (identifier)|TePapa]]', 3544, p.tepapaLink },
 +
{ 'TH', '[[TH (identifier)|TH]]', 1694, p.thLink },
 +
{ 'TLS', '[[TLS (identifier)|TLS]]', 1362, p.tlsLink },
 +
{ 'Trove', '[[Trove (identifier)|Trove]]', 1315, p.troveLink }, --formerly NLA-person
 +
{ 'UKPARL', '[[UKPARL (identifier)|UKPARL]]', 6213, p.ukparlLink },
 +
{ 'ULAN', '[[ULAN (identifier)|ULAN]]', 245, p.ulanLink },
 +
{ 'USCongress', '[[US Congress (identifier)|US Congress]]', 1157, p.uscongressLink },
 +
{ 'VcBA', '[[VcBA (identifier)|VcBA]]', 8034, p.vcbaLink },
 +
{ 'VIAF', '[[VIAF (identifier)|VIAF]]', 214, p.viafLink },
 +
{ 'WORLDCATID', '[[WorldCat Identities (identifier)|WorldCat Identities]]', 7859, nil },
 +
}
  
function p.authorityControl( frame )
+
-- Legitimate aliases to p.conf, for convenience
    local parentArgs = frame:getParent().args
+
-- Format: { 'alias', 'parameter name in p.conf' }
    --Create rows
+
p.aliases = {
    local elements = {}
+
{ 'Leonore', 'Léonore' }, --alias name without diacritics
 +
{ 'leonore', 'Léonore' }, --lowercase variant without diacritics
 +
{ 'MusicBrainz', 'MBA' },
 +
{ 'MusicBrainz artist', 'MBA' },
 +
{ 'MusicBrainz label', 'MBL' },
 +
{ 'MusicBrainz release group', 'MBRG' },
 +
{ 'MusicBrainz work', 'MBW' },
 +
{ 'SBN', 'ICCU' }, --SBN alias to be deprecated at a later stage
 +
{ 'TDVIA', 'TDVİA' }, --alias name without diacritics
 +
{ 'tdvia', 'TDVİA' }, --lowercase variant without diacritics
 +
}
  
    --redirect PND to GND
+
-- Deprecated aliases to p.conf; tracked in [[Category:Wikipedia articles with deprecated authority control identifiers]]
    if (parentArgs.GND == nil or parentArgs.GND == '') and parentArgs.PND ~= nil and parentArgs.PND ~= '' then
+
-- Format: { 'deprecated parameter name', 'replacement parameter name in p.conf' }
        parentArgs.GND = parentArgs.PND
+
p.deprecated = {
    end
+
{ 'GKD', 'GND' },
 +
{ 'PND', 'GND' },
 +
{ 'RLS', 'RSL' },
 +
{ 'SWD', 'GND' },
 +
{ 'NARA-organization', 'NARA' },
 +
{ 'NARA-person', 'NARA' },
 +
}
  
    --Wikidata fallback if requested
+
--[[==========================================================================]]
    local item = mw.wikibase.getEntityObject()
+
--[[                                   Main                                  ]]
    if item ~= nil and item.claims ~= nil then
+
--[[==========================================================================]]
        for _, params in pairs( conf ) do
 
            if params[3] ~= 0 then
 
                local val = parentArgs[params[1]]
 
                if not val or val == '' then
 
                local canUseWikidata = nil
 
                    if reqs[params[1]] ~= nil then
 
                        canUseWikidata = matchesWikidataRequirements( item, reqs[params[1]] )
 
                    else
 
                        canUseWikidata = true
 
                    end
 
                    if canUseWikidata then
 
                        local wikidataIds = getIdsFromWikidata( item, 'P' .. params[3] )
 
                        if wikidataIds[1] then
 
                            parentArgs[params[1]] = wikidataIds[1]
 
                        end
 
                    end
 
                end
 
            end
 
        end
 
    end
 
  
    --Worldcat
+
function p.authorityControl( frame )
    if parentArgs['WORLDCATID'] and parentArgs['WORLDCATID'] ~= '' then
+
local resolveEntity = require( "Module:ResolveEntityId" )
        table.insert( elements, createRow( 'WORLDCATID', '', parentArgs['WORLDCATID'], '[//www.worldcat.org/identities/' .. parentArgs['WORLDCATID'] .. ' WorldCat]', false ) ) --Validation?
+
local parentArgs = frame:getParent().args
    elseif parentArgs['LCCN'] and parentArgs['LCCN'] ~= '' then
+
local elements = {} --create/insert rows later
        local lccnParts = splitLccn( parentArgs['LCCN'] )
+
local worldcatCat = ''
        if lccnParts and lccnParts[1] ~= 'sh' then
+
local suppressedIdCat = ''
            table.insert( elements, createRow( 'LCCN', '', parentArgs['LCCN'], '[//www.worldcat.org/identities/lccn-' .. lccnParts[1] .. lccnParts[2] .. '-' .. lccnParts[3] .. ' WorldCat]', false ) )
+
local deprecatedIdCat = ''
        end
+
    end
+
--Redirect aliases to proper parameter names
 
+
for _, a in pairs( p.aliases ) do
    --Configured rows
+
local alias, param = a[1], a[2]
    local rct = 0
+
if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[alias] then
    for k, params in pairs( conf ) do
+
parentArgs[param] = parentArgs[alias]
        local val = parentArgs[params[1]]
+
end
        if val and val ~= '' then
+
end
            table.insert( elements, createRow( params[1], params[2] .. ':', val, params[4]( val ), true ) )
+
            rct = rct + 1
+
--Redirect deprecated parameters to proper parameter names, and assign tracking cat
        end
+
for _, d in pairs( p.deprecated ) do
    end
+
local dep, param = d[1], d[2]
    local Navbox = require('Module:Navbox')
+
if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[dep] then
    local elementscats = ''
+
parentArgs[param] = parentArgs[dep]
    if rct > 13 then
+
if namespace == 0 then
    elementscats = '[[Category:AC with ' .. rct .. ' elements]]'
+
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[mw.ustring.lower(params[1])] or 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[mw.ustring.lower(params[1])] or parentArgs[params[1]]
 +
if val and val ~= '' and type(params[4]) == 'function' 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'] or parentArgs['WORLDCATID']
 +
if worldcatId and worldcatId ~= '' then --if WORLDCATID present & unsuppressed
 +
table.insert( elements, p.createRow( 'WORLDCATID', '', worldcatId, '[[WorldCat Identities (identifier)|WorldCat Identities]]: [https://www.worldcat.org/identities/'..mw.uri.encode(worldcatId, 'PATH')..' '..worldcatId..']', false ) ) --Validation?
 +
worldcatCat = p.getCatForId( 'WORLDCATID' )
 +
elseif worldcatId == nil then --if WORLDCATID absent but unsuppressed
 +
local viafId = parentArgs['viaf'] or parentArgs['VIAF']
 +
local lccnId = parentArgs['lccn'] or 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 (identifier)|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 (identifier)|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 WORLDCATID suppressed
 +
suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|WORLDCATID]]'
 +
end
 +
 +
local Navbox = require('Module:Navbox')
 +
local elementsCat = ''
 +
if rct == 0 or rct >= 25 then
 +
local eCat = 'AC with '..rct..' elements'
 +
elementsCat = '[[Category:'..eCat..']]'..p.redCatLink(eCat)
 
end
 
end
 
 
if #elements ~= 0 then
+
local outString = ''
return Navbox._navbox( {
+
if #elements > 0 then
 +
local args = { pid = 'identifiers' } -- #target the list of identifiers
 +
if testcases and itemId then args = { pid = 'identifiers', 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
 
return ""
 
 
end
 
end
 +
 +
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
 +
 +
return outString
 
end
 
end
  
 
return p
 
return p

Revision as of 09:58, 8 November 2020

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 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

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

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

-- If a link has a suitable entry in the global inter-wiki prefix table at [[:m:Interwiki_map]], please consider routing through this prefix rather than as external link URL. This will ease future maintenance as necessary updates to the link can be centrally carried out there rather than by updating this module. The "external link" icon would disappear for such entries.

function p.aagLink( id )
	--P3372's format regex: \d+ (e.g. 1)
	if not id:match( '^%d+$' ) then
		return false
	end
	return '[https://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 id:match( '^%d%d%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[https://dl.acm.org/profile/'..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 id:match( '^[a-z][-a-z]+-[1-2]%d%d?%d?%d?$' ) and
	   not id:match( '^[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 id:match( '^[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 id:match( '^[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 id:match( '^[A-Z][A-Z][A-Z]%d%d%d%d[a-z]$' ) and
	   not id:match( '^[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.balatLink( id )
	--P3293's format regex: \d+ (e.g. 1)
	if not id:match( '^%d+$' ) then
		return false
	end
	return '[http://balat.kikirpa.be/object/104257'..id..' '..id..']'..p.getCatForId( 'BALaT' ) --no https as of 9/2019
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 id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) and
	   not id:match( '^[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.bildLink( id )
	--P2092's format regex: \d+ (e.g. 1)
	if not id:match( '^%d+$' ) then
		return false
	end
	return '[https://www.bildindex.de/document/obj'..id..' '..id..']'..p.getCatForId( 'Bildindex' )
end

function p.bncLink( id )
	--P1890's format regex: \d{9} (e.g. 123456789)
	if not id:match( '^%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

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 id:match( '^[XF][XF]%d%d%d%d%d?%d?%d?$' ) and
	   not id:match( '^a%d%d%d%d%d?%d?%d?$' ) and
	   not id:match( '^bi[mcsv][aoei]%d%d%d%d%d%d%d%d%d%d$' ) and
	   not id:match( '^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 as of 9/2019
end

function p.bnfLink( id )
	--P268's format regex: \d{8}[0-9bcdfghjkmnpqrstvwxz] (e.g. 123456789)
	if not id:match( '^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 id:match( '^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.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.bpnLink( id )
	--P651's format regex: \d{6,8} (e.g. 00123456)
	if not id:match( '^%d%d%d%d%d%d%d%d$' ) and --original format regex, changed 8/2019 to
	   not id:match( '^0?%d%d%d%d%d%d%d$' ) and --allow 1-2 leading 0s, allowed by the website
	   not id:match( '^0?0?%d%d%d%d%d%d$' ) then
		return false
	end
	return '[http://www.biografischportaal.nl/en/persoon/'..id..' '..id..']'..p.getCatForId( 'BPN' ) --no https as of 9/2019
end

function p.canticLink( id )
	--P1273's format regex: a\d{7}[0-9x] (e.g. a10640745)
	if not id:match( '^a%d%d%d%d%d%d%d[%dx]$' ) then
		return false
	end
	return '[http://cantic.bnc.cat/registres/CUCId/'..id..' '..id..']'..p.getCatForId( 'CANTIC' ) --no https as of 10/2019
end

function p.ciniiLink( id )
	--P271's format regex: DA\d{7}[\dX] (e.g. DA12345678)
	if not id:match( '^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.cwgcLink( id )
	--P1908's format regex: [1-9]\d* (e.g. 75228351)
	if not id:match( '^[1-9]%d*$' ) then
		return false
	end
	return '[https://www.cwgc.org/find-war-dead/casualty/'..id..'/ '..id..']'..p.getCatForId( 'CWGC' )
end

function p.daaoLink( id )
	--P1707's format regex: [a-z\-]+\d* (e.g. rolf-harris)
	if not id:match( '^[a-z%-]+%d*$' ) then
		return false
	end
	return '[https://www.daao.org.au/bio/'..id..' '..id..']'..p.getCatForId( 'DAAO' )
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 id:match( '^%d%d%d?/%d+$' ) and
	   not id:match( '^%d%d%d?/%d+%-%d+$' ) and
	   not id:match( '^[a-z]/[a-zA-Z][0-9A-Za-z]*$' ) and
	   not id:match( '^[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.dsiLink( id )
	--P2349's format regex: [1-9]\d* (e.g. 1538)
	if not id:match( '^[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.fnzaLink( id )
	--P6792's format regex: [1-9]\d* (e.g. 9785)
	if not id:match( '^[1-9]%d*$' ) then
		return false
	end
	return '[https://findnzartists.org.nz/artist/'..id..'/ '..id..']'..p.getCatForId( 'FNZA' )
end

function p.gndLink( id )
	--P227's format regex: 1[012]?\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 id:match( '^1[012]?%d%d%d%d%d%d%d[0-9X]$' ) and
	   not id:match( '^[47]%d%d%d%d%d%d%-%d$' ) and
	   not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%-[0-9X]$' ) and
	   not id:match( '^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.hdsLink( id )
	--P902's format regex: \d{6} (e.g. 050123)
	if not id:match( '^%d%d%d%d%d%d$' ) then
		return false
	end
	return '[https://hls-dhs-dss.ch/fr/articles/'..id..' '..id..']'..p.getCatForId( 'HDS' )
end

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

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

function p.isniLink( id )
	id = p.validateIsni( id ) --e.g. 0000-0000-6653-4145
	if not id then
		return false
	end
	return '[https://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 as of 9/2019
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 id:match( regex ) then
		return false
	end
	return '[https://www.pop.culture.gouv.fr/notice/joconde/'..id..' '..id..']'..p.getCatForId( 'Joconde' )
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 id:match( '^%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 as of 9/2019
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.lirLink( id )
	--P886's format regex: \d+ (e.g. 1)
	if not id:match( '^%d+$' ) then
		return false
	end
	return '[http://www.e-lir.ch/e-LIR___Lexicon.'..id..'.450.0.html '..id..']'..p.getCatForId( 'LIR' ) --no https as of 9/2019
end

function p.lnbLink( id )
	--P1368's format regex: \d{9} (e.g. 123456789)
	if not id:match( '^%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.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 as of 9/2019
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 id:match( '^%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 id:match( '^%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 id:match( '^%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 id:match( '^%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 id:match( '^%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 id:match( '^%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 id:match( '^%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 id:match( '^%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

function p.mgpLink( id )
	--P549's format regex: \d{1,6} (e.g. 123456)
	if not id:match( '^%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.naraLink( id )
	--P1225's format regex: ^([1-9]\d{0,8})$ (e.g. 123456789)
	if not id:match( '^[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.nclLink( id )
	--P1048's format regex: \d+ (e.g. 1081436)
	if not id:match( '^%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 as of 9/2019
end

function p.ndlLink( id )
	--P349's format regex: 0?\d{8} (e.g. 012345678)
	if not id:match( '^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 id:match( '^%d+$' ) then
		return false
	end
	return '[https://www.ngv.vic.gov.au/explore/collection/artist/'..id..'/ '..id..']'..p.getCatForId( 'NGV' )
end

function p.nkcLink( id )
	--P691's format regex: [a-z]{2,4}[0-9]{2,14} (e.g. abcd12345678901234)
	if not id:match( '^[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.nlaLink( id )
	--P409's format regex: [1-9][0-9]{0,11} (e.g. 123456789012)
	if not id:match( '^[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.nlgLink( id )
	--P3348's format regex: [1-9]\d* (e.g. 1)
	if not id:match( '^[1-9]%d*$' ) then
		return false
	end
	return '[https://data.nlg.gr/resource/authority/record'..id..' '..id..']'..p.getCatForId( 'NLG' )
end

function p.nliLink( id )
	--P949's format regex: \d{9} (e.g. 123456789)
	if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[http://uli.nli.org.il/F/?func=direct&doc_number='..id..'&local_base=nlx10'..' '..id..']'..p.getCatForId( 'NLI' )
end

function p.nlkLink( id )
	--P5034's format regex: KA.(19|20).{7} (e.g. KAC201501465)
	if not id:match( '^KA.19.......$' ) and
	   not id:match( '^KA.20.......$' ) then
		return false
	end
	return '[https://nl.go.kr/authorities/resource/'..id..' '..id..']'..p.getCatForId( 'NLK' )
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 id:match( '^9810%d+$' ) and
	   not id:match( '^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.nlrLink( id )
	--P1003's format regex: \d{9} (e.g. 123456789)
	if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[http://aleph.bibnat.ro:8991/F/?func=direct&local_base=NLR10&doc_number='..id..']'..p.getCatForId( 'NLR' )
end

function p.nskLink( id )
	--P1375's format regex: \d{9} (e.g. 123456789)
	if not id:match( '^%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 as of 9/2019
end

function p.ntaLink( id )
	--P1006's format regex: \d{8}[\dX] (e.g. 12345678X)
	if not id:match( '^%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.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.picLink( id )
	--P2750's format regex: [1-9]\d* (e.g. 1)
	if not id:match( '^[1-9]%d*$' ) then
		return false
	end
	return '[https://pic.nypl.org/constituents/'..id..' '..id..']'..p.getCatForId( 'PIC' )
end

function p.ridLink( id )
	--P1053's format regex: [A-Z]{1,3}-\d{4}-20[0-2]\d   (e.g. AAS-5150-2020)
	if not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-20[0-2]%d$' ) then
		return false
	end
	return '[https://www.researcherid.com/rid/'..id..' '..id..']'..p.getCatForId( 'RID' )
end

function p.reroLink( id )
	--P3065's format regex: 0[1-2]-[A-Z0-9]{1,10} (e.g. 02-A012345678)
	if not id:match( '^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.rkdartistsLink( id )
	--P650's format regex: [1-9]\d{0,5} (e.g. 123456)
	if not id:match( '^[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.rkdidLink( id )
	--P350's format regex: [1-9]\d{0,5} (e.g. 123456)
	if not id:match( '^[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.rslLink( id )
	--P947's format regex: \d{1,9} (e.g. 123456789)
	if not id:match( '^%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 as of 9/2019
end

function p.iccuLink( id )
	--P396's format regex: IT\\ICCU\\(\d{10}|\D\D[\D\d]\D\\\d{6}) (e.g. IT\ICCU\CFIV\000163)
	if not id:match( '^IT\\ICCU\\%d%d%d%d%d%d%d%d%d%d$' ) and
	   not id:match( '^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( 'ICCU' ) end

function p.selibrLink( id )
	--P906's format regex: [1-9]\d{4,5} (e.g. 123456)
	if not id:match( '^[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.sikartLink( id )
	--P781's format regex: \d{7,9} (e.g. 123456789)
	if not id:match( '^%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 as of 9/2019
end

function p.snacLink( id )
	--P3430's format regex: \d*[A-Za-z][0-9A-Za-z]* (e.g. A)
	if not id:match( '^%d*[A-Za-z][0-9A-Za-z]*$' ) then
		return false
	end
	return '[https://snaccooperative.org/ark:/99166/'..id..' '..id..']'..p.getCatForId( 'SNAC-ID' )
end

function p.sudocLink( id )
	--P269's format regex: (\d{8}[\dX]|) (e.g. 026927608)
	if not id:match( '^%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.s2authoridLink( id )
	--P4012's format regex: [1-9]\d* (e.g. 1796130)
	if not id:match( '^[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.ta98Link( id )
	--P1323's format regex: A\d{2}\.\d\.\d{2}\.\d{3}[FM]? (e.g. A12.3.45.678)
	if not id:match( '^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.tdviaLink( id )
	--P7314's format regex: [a-z/-]+] (e.g. barkan-omer-lutfi)
	if not id:match( '^[a-z/-]+$' ) then
		return false
	end
	return '[https://islamansiklopedisi.org.tr/'..id..' '..id..']'..p.getCatForId( 'TDVİA' )
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 = id:match( '^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 id:match( '^%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 = id:match( '^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.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 as of 9/2019
end

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

function p.ukparlLink( id )
	--P6213's format regex: [a-zA-Z\d]{8} (e.g. AQUupyiR)
	if not id:match( '^[a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d]$' ) then
		return false
	end
	return '[https://id.parliament.uk/'..id..' '..id..']'..p.getCatForId( 'UKPARL' )
end

function p.ulanLink( id )
	--P245's format regex: 500\d{6} (e.g. 500123456)
	if not id:match( '^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.uscongressLink( id )
	--P1157's format regex: [A-Z]00[01]\d{3} (e.g. A000123)
	if not id:match( '^[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 as of 9/2019
end

function p.vcbaLink( id )
	--P8034's format regex: \d{3}\/[1-9]\d{0,5} (e.g. 494/9793)
	if not id:match( '^%d%d%d\/[1-9]%d?%d?%d?%d?%d?$' ) then
		return false
	end
	local id2 = id:gsub('\/', '_')
	return '[https://opac.vatlib.it/auth/detail/'..id2..' '..id..']'..p.getCatForId( 'VcBA' )
end

function p.viafLink( id )
	--P214's format regex: [1-9]\d(\d{0,7}|\d{17,20}) (e.g. 123456789, 1234567890123456789012)
	if not id:match( '^[1-9]%d%d?%d?%d?%d?%d?%d?%d?$' ) and
	   not id:match( '^[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
	-- If the "VIAF" entry at [[:m:Interwiki map]] would resolve to "https://viaf.org/viaf/$1" (rather than "http://viaf.org/viaf/$1", as it currently still does), the code below could change from '[https://viaf.org/viaf/'..id..' '..id..']' to '[[:VIAF:'..id..'|'..id..']]'.
	return '[https://viaf.org/viaf/'..id..' '..id..']'..p.getCatForId( 'VIAF' )
end

--[[=========================== Helper functions =============================]]

function p.append(str, c, length)
	while str:len() < length do
		str = c .. str
	end
	return str
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.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

--[[==========================================================================]]
--[[                    Wikidata & 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

-- 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'..
					  '! [[:Category:Wikipedia articles with authority control information|'..       'Articles]]\n'..
					  '! [[:Category:User pages with authority control information|'..               'User pages]]\n'..
					  '! [[:Category:Miscellaneous pages with authority control information|'..      'Misc. pages]]\n'..
					  '! [[:Category:Wikipedia articles with faulty authority control information|'..'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                               ]]
--[[==========================================================================]]

-- If a specific "(identifier) redirect" exists for an identifier, please route through this particular redirect rather than linking directly to the target page. This reduces clutter in "What links here" and improves reverse lookup of articles where a manifestation of this particular identifier is used.

-- 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', '[[AAG (identifier)|AAG]]', 3372, p.aagLink },
	{ 'ACM-DL', '[[ACM DL (identifier)|ACM DL]]', 864, p.acmLink },
	{ 'ADB', '[[ADB (identifier)|ADB]]', 1907, p.adbLink },
	{ 'AGSA', '[[AGSA (identifier)|AGSA]]', 6804, p.agsaLink },
	{ 'autores.uy', '[[autores.uy (identifier)|autores.uy]]', 2558, p.autoresuyLink },
	{ 'AWR', '[[AWR (identifier)|AWR]]', 4186, p.awrLink },
	{ 'BALaT', '[[BALaT (identifier)|BALaT]]', 3293, p.balatLink },
	{ 'BIBSYS', '[[BIBSYS (identifier)|BIBSYS]]', 1015, p.bibsysLink },
	{ 'Bildindex', '[[Bildindex (identifier)|Bildindex]]', 2092, p.bildLink },
	{ 'BNC', '[[BNC (identifier)|BNC]]', 1890, p.bncLink },
	{ 'BNE', '[[BNE (identifier)|BNE]]', 950, p.bneLink },
	{ 'BNF', '[[BNF (identifier)|BNF]]', 268, p.bnfLink },
	{ 'Botanist', '[[Botanist (identifier)|Botanist]]', 428, p.botanistLink },
	{ 'BPN', '[[BPN (identifier)|BPN]]', 651, p.bpnLink },
	{ 'CANTIC', '[[CANTIC (identifier)|CANTIC]]', 1273, p.canticLink },
	{ 'CINII', '[[CiNii (identifier)|CiNii]]', 271, p.ciniiLink },
	{ 'CWGC', '[[CWGC (identifier)|CWGC]]', 1908, p.cwgcLink },
	{ 'DAAO', '[[DAAO (identifier)|DAAO]]', 1707, p.daaoLink },
	{ 'DBLP', '[[DBLP (identifier)|DBLP]]', 2456, p.dblpLink },
	{ 'DSI', '[[DSI (identifier)|DSI]]', 2349, p.dsiLink },
	{ 'FNZA', '[[:d:Property:P6792|FNZA]]', 6792, p.fnzaLink },
	{ 'GND', '[[GND (identifier)|GND]]', 227, p.gndLink },
	{ 'HDS', '[[HDS (identifier)|HDS]]', 902, p.hdsLink },
	{ 'IAAF', '[[IAAF (identifier)|IAAF]]', 1146, p.iaafLink },
	{ 'ICCU', '[[ICCU (identifier)|ICCU]]', 396, p.iccuLink }, --formerly SBN
	{ 'ICIA', '[[ICIA (identifier)|ICIA]]', 1736, p.iciaLink },
	{ 'ISNI', '[[ISNI (identifier)|ISNI]]', 213, p.isniLink },
	{ 'Joconde', '[[Joconde (identifier)|Joconde]]' , 347, p.jocondeLink },
	{ 'KULTURNAV', '[[KulturNav (identifier)|KulturNav]]', 1248, p.kulturnavLink },
	{ 'LCCN', '[[LCCN (identifier)|LCCN]]', 244, p.lccnLink },
	{ 'LIR', '[[LIR (identifier)|LIR]]', 886, p.lirLink },
	{ 'LNB', '[[LNB (identifier)|LNB]]', 1368, p.lnbLink },
	{ 'Léonore', '[[Léonore (identifier)|Léonore]]', 640, p.leonoreLink },
	{ 'MBA', '[[MBA (identifier)|MBA]]', 434, p.mbaLink, category = 'MusicBrainz' }, --special category name
	{ 'MBAREA', '[[MBAREA (identifier)|MBAREA]]', 982, p.mbareaLink, category = 'MusicBrainz area' }, --special category name
	{ 'MBI', '[[MBI (identifier)|MBI]]', 1330, p.mbiLink, category = 'MusicBrainz instrument' }, --special category name
	{ 'MBL', '[[MBL (identifier)|MBL]]', 966, p.mblLink, category = 'MusicBrainz label' }, --special category name
	{ 'MBP', '[[MBP (identifier)|MBP]]', 1004, p.mbpLink, category = 'MusicBrainz place' }, --special category name
	{ 'MBRG', '[[MBRG (identifier)|MBRG]]', 436, p.mbrgLink, category = 'MusicBrainz release group' }, --special category name
	{ 'MBS', '[[MBS (identifier)|MBS]]', 1407, p.mbsLink, category = 'MusicBrainz series' }, --special category name
	{ 'MBW', '[[MBW (identifier)|MBW]] work', 435, p.mbwLink, category = 'MusicBrainz work' }, --special category name
	{ 'MGP', '[[MGP (identifier)|MGP]]', 549, p.mgpLink },
	{ 'NARA', '[[NARA (identifier)|NARA]]', 1225, p.naraLink },
	{ 'NCL', '[[NCL (identifier)|NBL]]', 1048, p.nclLink },
	{ 'NDL', '[[NDL (identifier)|NDL]]', 349, p.ndlLink },
	{ 'NGV', '[[NGV (identifier)|NGV]]', 2041, p.ngvLink },
	{ 'NKC', '[[NKC (identifier)|NKC]]', 691, p.nkcLink },
	{ 'NLA', '[[NLA (identifier)|NLA]]', 409, p.nlaLink },
	{ 'NLG', '[[NLG (identifier)|NLG]]', 3348, p.nlgLink },
	{ 'NLI', '[[NLI (identifier)|NLI]]', 949, p.nliLink },
	{ 'NLK', '[[NLK (identifier)|NLK]]', 5034, p.nlkLink },
	{ 'NLP', '[[NLP (identifier)|NLP]]', 1695, p.nlpLink },
	{ 'NLR', '[[NLR (identifier)|NLR]]', 1003, p.nlrLink },
	{ 'NSK', '[[NSK (identifier)|NSK]]', 1375, p.nskLink },
	{ 'NTA', '[[NTA (identifier)|NTA]]', 1006, p.ntaLink },
	{ 'ORCID', '[[ORCID (identifier)|ORCID]]', 496, p.orcidLink },
	{ 'PIC', '[[:d:Q23892012|PIC]]', 2750, p.picLink },
	{ 'RID', '[[RID (identifier)|ResearcherID]]', 1053, p.ridLink },
	{ 'RERO', '[[RERO (identifier)|RERO]]', 3065, p.reroLink },
	{ 'RKDartists', '[[RKDartists (identifier)|RKD]]', 650, p.rkdartistsLink },
	{ 'RKDID', '[[RKDID (identifier)|RKDimages ID]]', 350, p.rkdidLink },
	{ 'RSL', '[[RSL (identifier)|RSL]]', 947, p.rslLink },
	{ 'SELIBR', '[[SELIBR (identifier)|SELIBR]]', 906, p.selibrLink },
	{ 'SIKART', '[[SIKART (identifier)|SIKART]]', 781, p.sikartLink },
	{ 'SNAC-ID', '[[SNAC-ID (identifier)|SNAC]]', 3430, p.snacLink },
	{ 'SUDOC', '[[SUDOC (identifier)|SUDOC]]', 269, p.sudocLink },
	{ 'S2AuthorId', '[[S2AuthorId (identifier)|S2AuthorId]]', 4012, p.s2authoridLink, category = 'Semantic Scholar author' }, --special category name
	{ 'TA98', '[[TA98 (identifier)|TA98]]', 1323, p.ta98Link },
	{ 'TDVİA', '[[TDVİA (identifier)|TDVİA]]', 7314, p.tdviaLink },
	{ 'TE', '[[TE (identifier)|TE]]', 1693, p.teLink },
	{ 'TePapa', '[[TePapa (identifier)|TePapa]]', 3544, p.tepapaLink },
	{ 'TH', '[[TH (identifier)|TH]]', 1694, p.thLink },
	{ 'TLS', '[[TLS (identifier)|TLS]]', 1362, p.tlsLink },
	{ 'Trove', '[[Trove (identifier)|Trove]]', 1315, p.troveLink }, --formerly NLA-person
	{ 'UKPARL', '[[UKPARL (identifier)|UKPARL]]', 6213, p.ukparlLink },
	{ 'ULAN', '[[ULAN (identifier)|ULAN]]', 245, p.ulanLink },
	{ 'USCongress', '[[US Congress (identifier)|US Congress]]', 1157, p.uscongressLink },
	{ 'VcBA', '[[VcBA (identifier)|VcBA]]', 8034, p.vcbaLink },
	{ 'VIAF', '[[VIAF (identifier)|VIAF]]', 214, p.viafLink },
	{ 'WORLDCATID', '[[WorldCat Identities (identifier)|WorldCat Identities]]', 7859, nil },
}

-- Legitimate aliases to p.conf, for convenience
-- Format: { 'alias', 'parameter name in p.conf' }
p.aliases = {
	{ 'Leonore', 'Léonore' }, --alias name without diacritics
	{ 'leonore', 'Léonore' }, --lowercase variant without diacritics
	{ 'MusicBrainz', 'MBA' },
	{ 'MusicBrainz artist', 'MBA' },
	{ 'MusicBrainz label', 'MBL' },
	{ 'MusicBrainz release group', 'MBRG' },
	{ 'MusicBrainz work', 'MBW' },
	{ 'SBN', 'ICCU' }, --SBN alias to be deprecated at a later stage
	{ 'TDVIA', 'TDVİA' }, --alias name without diacritics
	{ 'tdvia', 'TDVİA' }, --lowercase variant without diacritics
}

-- Deprecated aliases to p.conf; tracked in [[Category:Wikipedia articles with deprecated authority control identifiers]]
-- Format: { 'deprecated parameter name', 'replacement parameter name in p.conf' }
p.deprecated = {
	{ 'GKD', 'GND' },
	{ 'PND', 'GND' },
	{ 'RLS', 'RSL' },	
	{ '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[mw.ustring.lower(params[1])] or 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[mw.ustring.lower(params[1])] or parentArgs[params[1]]
		if val and val ~= '' and type(params[4]) == 'function' 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'] or parentArgs['WORLDCATID']
	if worldcatId and worldcatId ~= '' then --if WORLDCATID present & unsuppressed
		table.insert( elements, p.createRow( 'WORLDCATID', '', worldcatId, '[[WorldCat Identities (identifier)|WorldCat Identities]]: [https://www.worldcat.org/identities/'..mw.uri.encode(worldcatId, 'PATH')..' '..worldcatId..']', false ) ) --Validation?
		worldcatCat = p.getCatForId( 'WORLDCATID' )
	elseif worldcatId == nil then --if WORLDCATID absent but unsuppressed
		local viafId = parentArgs['viaf'] or parentArgs['VIAF']
		local lccnId = parentArgs['lccn'] or 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 (identifier)|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 (identifier)|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 WORLDCATID suppressed
		suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|WORLDCATID]]'
	end
	
	local Navbox = require('Module:Navbox')
	local elementsCat = ''
	if rct == 0 or rct >= 25 then
		local eCat = 'AC with '..rct..' elements'
		elementsCat  = '[[Category:'..eCat..']]'..p.redCatLink(eCat)
	end
	
	local outString = ''
	if #elements > 0 then
		local args = { pid = 'identifiers' } -- #target the list of identifiers
		if testcases and itemId then args = { pid = 'identifiers', 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 )
			} )
	end

	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
	
	return outString
end

return p