Module:Authority control: Difference between revisions

From Good Old TV Fan Wiki
m (1 revision imported)
 
gi>Tom.Reding
Line 2: Line 2:


local p = {}
local p = {}
local title = mw.title.getCurrentTitle()
local namespace = title.namespace
local testcases = (string.sub(title.subpageText,1,9) == 'testcases')


--[[==========================================================================]]
--[[==========================================================================]]
Line 8: Line 11:


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


function p.redCatLink( catName ) --catName == 'Blah', not 'Category:Blah', not '[[Category:Blah]]'
function p.redCatLink( catName ) --catName == 'Blah' (not 'Category:Blah', not '[[Category:Blah]]')
if catName and catName ~= '' and mw.title.new(catName, 14).exists == false then
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]]'
return '[[Category:Pages with red-linked authority control categories]]'
end
end
return ''
return ''
end
function p.createRow( id, label, rawValues, link, links, withUid, specialCat )
local catName = 'Wikipedia articles with faulty '..(specialCat or id)..' identifiers'
if links then -- all links[] use withUid = false; no check needed
local row = '*<span class="nowrap">'..label
for i, l in ipairs( links ) do
if i == 1 then row = row..' '
else          row = row..', ' end
if l then
row = row..'<span class="uid">'..l..'</span>'
else
row = row..'<span class="error">The '..id..' id '..rawValues[i]..' is not valid.</span>[[Category:'..catName..']]'..p.redCatLink(catName)
end
end
return row..'</span>\n'
elseif 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
return '* <span class="error">The '..id..' id '..rawValues..' is not valid.</span>[[Category:'..catName..']]'..p.redCatLink(catName)..'\n'
end
end


Line 32: Line 60:
--[[==========================================================================]]
--[[==========================================================================]]


function p.iaafLink( id )
-- 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.
--P1146's format regex: [1-9][0-9]* (e.g. 123)
 
if not string.match( id, '^[1-9]%d*$' ) then
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
return false
end
end
return '[https://www.iaaf.org/athletes/biographies/athcode=' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'IAAF' )
return '[https://www.agsa.sa.gov.au/collection-publications/collection/creators/_/'..id..'/ '..id..']'..p.getCatForId( 'AGSA' )
end
end


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


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


function p.sikartLink( id )
function p.balatLink( id )
--P781's format regex: \d{7,9} (e.g. 123456789)
--P3293's format regex: \d+ (e.g. 1)
if not string.match( id, '^%d%d%d%d%d%d%d%d?%d?$' ) then
if not id:match( '^%d+$' ) then
return false
return false
end
end
return '[http://www.sikart.ch/KuenstlerInnen.aspx?id=' .. id .. '&lng=en ' .. id .. ']' .. p.getCatForId( 'SIKART' )
return '[http://balat.kikirpa.be/object/104257'..id..' '..id..']'..p.getCatForId( 'BALaT' ) --no https as of 9/2019
end
end


function p.tlsLink( id )
function p.bibsysLink( id )
local id2 = id:gsub(' +', '_')
--P1015's format regex: [1-9]\d* or [1-9](\d{0,8}|\d{12}) (e.g. 1234567890123)
--P1362's format regex: \p{Lu}[\p{L}\d_',\.\-\(\)\*/–]{3,59} (e.g. Abcd)
--TODO: follow up @ [[d:Property talk:P1015#Discrepancy between the 2 regex constraints]] or escalate/investigate
local class = "[%a%d_',%.%-%(%)%*/–]"
if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) and
local regex = "^%u" .. string.rep(class, 3) .. string.rep(class.."?", 56) .. "$"
  not id:match( '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d$' ) then
if not mw.ustring.match( id2, regex ) then
return false
return false
end
end
return '[http://tls.theaterwissenschaft.ch/wiki/' .. id2 .. ' ' .. id .. ']' .. p.getCatForId( 'TLS' )
return '[https://authority.bibsys.no/authority/rest/authorities/html/'..id..' '..id..']'..p.getCatForId( 'BIBSYS' )
end
end


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


function p.bneLink( id )
function p.bncLink( 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)
--P1890's format regex: \d{9} (e.g. 123456789)
if not string.match( id, '^[XF][XF]%d%d%d%d%d?%d?%d?$' ) and
if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
  not string.match( id, '^a%d%d%d%d%d?%d?%d?$' ) and
  not string.match( id, '^bi[mcsv][aoei]%d%d%d%d%d%d%d%d%d%d$' ) and
  not string.match( id, '^Mi[sm][eoa]%d%d%d%d%d%d%d%d%d%d$' ) then
return false
return false
end
end
return '[http://catalogo.bne.es/uhtbin/authoritybrowse.cgi?action=display&authority_id=' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'BNE' )
return '[http://www.bncatalogo.cl/F?func=direct&local_base=red10&doc_number='..id..' '..id..']'..p.getCatForId( 'BNC' )
end
end


function p.uscongressLink( id )
function p.bneLink( id )
--P1157's format regex: [A-Z]00[01]\d{3} (e.g. A000123)
--P950's format regex: (XX|FF|a)\d{4,7}|(bima|bimo|bica|bis[eo]|bivi|Mise|Mimo|Mima)\d{10} (e.g. XX1234567)
if not string.match( id, '^[A-Z]00[01]%d%d%d$' ) then
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
return false
end
end
return '[http://bioguide.congress.gov/scripts/biodisplay.pl?index=' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'USCongress' )
return '[http://catalogo.bne.es/uhtbin/authoritybrowse.cgi?action=display&authority_id='..id..' '..id..']'..p.getCatForId( 'BNE' ) --no https as of 9/2019
end
end


function p.naraLink( id )
function p.bnfLink( id )
--P1225's format regex: ^([1-9]\d{0,7})$ (e.g. 12345678)
--P268's format regex: \d{8}[0-9bcdfghjkmnpqrstvwxz] (e.g. 123456789)
if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?$' ) then
if not id:match( '^c?b?%d%d%d%d%d%d%d%d[0-9bcdfghjkmnpqrstvwxz]$' ) then
return false
return false
end
end
return '[https://catalog.archives.gov/id/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'NARA' )
--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
end


Line 118: Line 176:
end
end
local id2 = id:gsub(' +', '%%20')
local id2 = id:gsub(' +', '%%20')
return '[http://www.ipni.org/ipni/advAuthorSearch.do?find_abbreviation=' .. id2 .. ' ' .. id .. ']' .. p.getCatForId( 'Botanist' )
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
end


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


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


function p.leonoreLink( id )
function p.daaoLink( 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)
--P1707's format regex: [a-z\-]+\d* (e.g. rolf-harris)
if not id:match( '^LH/%d%d?%d?%d?/%d%d?%d?$' ) and            --IDs from      LH/1/1 to        LH/2794/54 (legionaries)
if not id:match( '^[a-z%-]+%d*$' ) then
  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
return false
end
end
return '[http://www.culture.gouv.fr/public/mistral/leonore_fr?ACTION=CHERCHER&FIELD_1=COTE&VALUE_1=' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'Léonore' )
return '[https://www.daao.org.au/bio/'..id..' '..id..']'..p.getCatForId( 'DAAO' )
end
end


function p.sbnLink( id )
function p.dblpLink( id )
--P396's format regex: IT\\ICCU\\(\d{10}|\D\D[\D\d]\D\\\d{6}) (e.g. IT\ICCU\CFIV\000163)
--P2456's format regex: \d{2,3} /\d+(-\d+)?|[a-z] /[a-zA-Z][0-9A-Za-z]*(-\d+)? (e.g. 123/123)
if not string.match( id, '^IT\\ICCU\\%d%d%d%d%d%d%d%d%d%d$' ) and
if not id:match( '^%d%d%d?/%d+$' ) and
  not string.match( id, '^IT\\ICCU\\%u%u[%u%d]%u\\%d%d%d%d%d%d$' ) then --legacy: %u used here instead of %D (but the faulty ID cat is empty, out of ~12k uses)
  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
return false
end
end
return '[http://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid=' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'SBN' )
return '[https://dblp.org/pid/'..id..' '..id..']'..p.getCatForId( 'DBLP' )
end
end


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


function p.nclLink( id )
function p.dsiLink( id )
--P1048's format regex: \d+ (e.g. 1081436)
--P2349's format regex: [1-9]\d* (e.g. 1538)
if not string.match( id, '^%d+$' ) then
if not id:match( '^[1-9]%d*$' ) then
return false
return false
end
end
return '[http://aleweb.ncl.edu.tw/F/?func=accref&acc_sequence=' .. id .. '&CON_LNG=ENG ' .. id .. ']' .. p.getCatForId( 'NCL' )
return '[http://www.uni-stuttgart.de/hi/gnt/dsi2/index.php?table_name=dsi&function=details&where_field=id&where_value='..id..' '..id..']'..p.getCatForId( 'DSI' )
end
end


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


function p.sudocLink( id )
function p.gndLink( id )
--P269's format regex: (\d{8}[\dX]|) (e.g. 026927608)
--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 string.match( id, '^%d%d%d%d%d%d%d%d[%dxX]$' ) then --legacy: allow lowercase 'x'
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
return false
end
end
return '[https://www.idref.fr/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'SUDOC' )
return '[https://d-nb.info/gnd/'..id..' '..id..']'..p.getCatForId( 'GND' )
end
end


function p.hdsLink( id )
function p.hdsLink( id )
--P902's format regex: 50\d{3}|[1-4]\d{4}|[1-9]\d{0,3}| (e.g. 50123)
--P902's format regex: \d{6} (e.g. 050123)
if not string.match( id, '^50%d%d%d$' ) and
if not id:match( '^%d%d%d%d%d%d$' ) then
  not string.match( id, '^[1-4]%d%d%d%d$' ) and
return false
  not string.match( id, '^[1-9]%d?%d?%d?$' ) then
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
return false
end
end
return '[http://www.hls-dhs-dss.ch/textes/f/F' .. id .. '.php ' .. id .. ']' .. p.getCatForId( 'HDS' )
return '[https://www.iaaf.org/athletes/_/'..id..' '..id..']'..p.getCatForId( 'IAAF' )
end
end


function p.lirLink( id )
function p.iciaLink( id )
--P886's format regex: \d+ (e.g. 1)
--P1736's format regex: \d+ (e.g. 1)
if not string.match( id, '^%d+$' ) then
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.ieuLink( id )
--P9070's format regex: [A-Z]\\[A-Z]\\[A-Za-z0-9]+ (e.g. K\Y\Kyiv)
if not id:match( '^[A-Z]\\[A-Z]\\%w+$' ) then
return false
return false
end
end
return '[http://www.e-lir.ch/e-LIR___Lexicon.' .. id .. '.450.0.html ' .. id .. ']' .. p.getCatForId( 'LIR' )
return '[http://www.encyclopediaofukraine.com/display.asp?linkpath=pages\\'..id..' '..id..']'..p.getCatForId( 'IEU' )
end
end


function p.splitLccn( id )
function p.isniLink( 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)
id = p.validateIsni( id ) --e.g. 0000-0000-6653-4145
if id:match( '^%l%l?%l?%d%d%d%d%d%d%d%d%d?%d?$' ) then
if not id then
id = id:gsub( '^(%l+)(%d+)(%d%d%d%d%d%d)$', '%1/%2/%3' )
return false
end
end
if id:match( '^%l%l?%l?/%d%d%d?%d?/%d+$' ) then
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
return mw.text.split( id, '/' )
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
end
return false
return '[https://www.pop.culture.gouv.fr/notice/joconde/'..id..' '..id..']'..p.getCatForId( 'Joconde' )
end
end


function p.append(str, c, length)
function p.kulturnavLink( id )
while str:len() < length do
--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)
str = c .. str
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
end
return str
return '[http://kulturnav.org/'..id..' '..id..']'..p.getCatForId( 'KULTURNAV' ) --no https as of 9/2019
end
end


Line 231: Line 331:
local lccnType = parts[1] ~= 'sh' and 'names' or 'subjects'
local lccnType = parts[1] ~= 'sh' and 'names' or 'subjects'
id = parts[1] .. parts[2] .. p.append( parts[3], '0', 6 )
id = parts[1] .. parts[2] .. p.append( parts[3], '0', 6 )
return '[http://id.loc.gov/authorities/' .. lccnType .. '/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'LCCN' )
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.maLink( id )
--P6366's format regex: [1-9]\d{4,9} (e.g. 1498221862)
if not id:match( '^[1-9]%d%d%d%d%d?%d?%d?%d?%d?$' ) then
return false
end
return '[https://academic.microsoft.com/v2/detail/'..id..' '..id..']'..p.getCatForId( 'MA' )
end
end


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


--Returns the ISNI check digit isni must be a string where the 15 first elements are digits, e.g. 0000000066534145
function p.mbareaLink( id )
function p.getIsniCheckDigit( isni )
--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)
local total = 0
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
for i = 1, 15 do
return false
local digit = isni:byte( i ) - 48 --Get integer value
end
total = (total + digit) * 2
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
end
local remainder = total % 11
return '[https://nl.go.kr/authorities/resource/'..id..' '..id..']'..p.getCatForId( 'NLK' )
local result = (12 - remainder) % 11
end
if result == 10 then
 
return "X"
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
end
return tostring( result )
return '[https://tools.wmflabs.org/wikidata-externalid-url?p=1695&id='..id..' '..id..']'..p.getCatForId( 'NLP' )
end
end


--Validate ISNI (and ORCID) and retuns it as a 16 characters string or returns false if it's invalid
function p.nlrLink( id )
--See http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
--P1003's format regex: \d{9} (e.g. 123456789)
function p.validateIsni( id )
if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
--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
return false
end
end
if p.getIsniCheckDigit( id ) ~= string.char( id:byte( 16 ) ) then
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
return false
end
end
return id
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


function p.isniLink( id )
function p.ntaLink( id )
id = p.validateIsni( id ) --e.g. 0000-0000-6653-4145
--P1006's format regex: \d{8}[\dX] (e.g. 12345678X)
if not id then
if not id:match( '^%d%d%d%d%d%d%d%d[%dX]$' ) then
return false
return false
end
end
return '[http://isni.org/isni/' .. id .. ' ' .. id:sub( 1, 4 ) .. ' ' .. id:sub( 5, 8 ) .. ' '  .. id:sub( 9, 12 ) .. ' '  .. id:sub( 13, 16 ) .. ']' .. p.getCatForId( 'ISNI' )
return '[http://data.bibliotheken.nl/id/thes/p'..id..' '..id..']'..p.getCatForId( 'NTA' )
end
end


Line 285: Line 551:
return false
return false
end
end
id = id:sub( 1, 4 ) .. '-' .. id:sub( 5, 8 ) .. '-' .. id:sub( 9, 12 ) .. '-' .. id:sub( 13, 16 )
id = id:sub( 1, 4 )..'-'..id:sub( 5, 8 )..'-'..id:sub( 9, 12 )..'-'..id:sub( 13, 16 )
return '[https://orcid.org/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'ORCID' )
return '[https://orcid.org/'..id..' '..id..']'..p.getCatForId( 'ORCID' )
end
end


function p.gndLink( id )
function p.plwabnLink( id )
--P227's format regex: (1|1[01])\d{7}[0-9X]|[47]\d{6}-\d|[1-9]\d{0,7}-[0-9X]|3\d{7}[0-9X] (e.g. 4079154-3)
--P7293's format regex: 981[0-9]{8}05606 (e.g. 9810696457305606)
if not string.match( id, '^1[01]?%d%d%d%d%d%d%d[0-9X]$' ) and
if not id:match( '^981%d%d%d%d%d%d%d%d05606*$' ) then
  not string.match( id, '^[47]%d%d%d%d%d%d%-%d$' ) and
  not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?%-[0-9X]$' ) and
  not string.match( id, '^3%d%d%d%d%d%d%d[0-9X]$' ) then
return false
return false
end
end
return '[https://d-nb.info/gnd/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'GND' )
return '[http://mak.bn.org.pl/cgi-bin/KHW/makwww.exe?BM=1&NU=1&IM=4&WI='..id..' '..id..']'..p.getCatForId( 'PLWABN' )
end
end


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


function p.bnfLink( id )
function p.picLink( id )
--P268's format regex: \d{8}[0-9bcdfghjkmnpqrstvwxz] (e.g. 123456789)
--P2750's format regex: [1-9]\d* (e.g. 1)
if not string.match( id, '^c?b?%d%d%d%d%d%d%d%d[0-9bcdfghjkmnpqrstvwxz]$' ) then
if not id:match( '^[1-9]%d*$' ) then
return false
return false
end
end
--Add cb prefix if it has been removed
return '[https://pic.nypl.org/constituents/'..id..' '..id..']'..p.getCatForId( 'PIC' )
if not string.match( id, '^cb.+$' ) then
end
id = 'cb' .. id
 
function p.ridLink( id )
--P1053's format regex: [A-Z]{1,3}-\d{4}-(19|20)\d\d (e.g. AAS-5150-2020)
if not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-19%d%d$' ) and
  not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-20%d%d$' ) then
return false
end
end
return '[http://catalogue.bnf.fr/ark:/12148/' .. id .. ' ' .. id .. '] [http://data.bnf.fr/ark:/12148/' .. id .. ' (data)]' .. p.getCatForId( 'BNF' )
return '[https://www.researcherid.com/rid/'..id..' '..id..']'..p.getCatForId( 'RID' )
end
end


function p.bpnLink( id )
function p.reroLink( id )
--P651's format regex: \d{8} (e.g. 12345678)
--P3065's format regex: 0[1-2]-[A-Z0-9]{1,10} (e.g. 02-A012345678)
if not string.match( id, '^%d%d%d%d%d%d%d%d$' ) then
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
return false
end
end
return '[http://www.biografischportaal.nl/en/persoon/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'BPN' )
return '[http://data.rero.ch/'..id..' '..id..']'..p.getCatForId( 'RERO' )
end
end


function p.ridLink( id )
function p.rkdartistsLink( id )
--P1053's format regex: [A-Z]-\d{4}-(19|20)\d\d (e.g. A-1234-1934)
--P650's format regex: [1-9]\d{0,5} (e.g. 123456)
if not string.match( id, '^[A-Z]%-%d%d%d%d%-19%d%d$' ) and
if not id:match( '^[1-9]%d?%d?%d?%d?%d?$' ) then
  not string.match( id, '^[A-Z]%-%d%d%d%d%-20%d%d$' ) then
return false
return false
end
end
return '[https://www.researcherid.com/rid/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'RID' )
return '[https://rkd.nl/en/explore/artists/'..id..' '..id..']'..p.getCatForId( 'RKDartists' )
end
end


function p.bibsysLink( id )
function p.rkdidLink( id )
--P1015's format regex: [1-9]\d* or [1-9](\d{0,8}|\d{12}) (e.g. 1234567890123)
--P350's format regex: [1-9]\d{0,5} (e.g. 123456)
--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?$' ) then
if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) and
  not string.match( id, '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d$' ) then
return false
return false
end
end
return '[https://authority.bibsys.no/authority/rest/authorities/html/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'BIBSYS' )
return '[https://rkd.nl/nl/explore/images/'..id..' '..id..']'..p.getCatForId( 'RKDID' )
end
end


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


function p.nlaLink( id )
function p.iccuLink( id )
--P409's format regex: [1-9][0-9]{0,11} (e.g. 123456789012)
--P396's format regex: IT\\ICCU\\(\d{10}|\D\D[\D\d]\D\\\d{6}) (e.g. IT\ICCU\CFIV\000163)
if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
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
return false
end
end
return '[https://nla.gov.au/anbd.aut-an' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'NLA' )
return '[https://libris.kb.se/auth/'..id..' '..id..']'..p.getCatForId( 'SELIBR' )
end
end


function p.rkdartistsLink( id )
function p.sikartLink( id )
--P650's format regex: [1-9]\d{0,5} (e.g. 123456)
--P781's format regex: \d{7,9} (e.g. 123456789)
if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?$' ) then
if not id:match( '^%d%d%d%d%d%d%d%d?%d?$' ) then
return false
return false
end
end
return '[https://rkd.nl/en/explore/artists/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'RKDartists' )
return '[http://www.sikart.ch/KuenstlerInnen.aspx?id='..id..'&lng=en '..id..']'..p.getCatForId( 'SIKART' ) --no https as of 9/2019
end
end


function p.snacLink( id )
function p.snacLink( id )
--P3430's format regex: \d*[A-Za-z][0-9A-Za-z]* (e.g. A)
--P3430's format regex: \d*[A-Za-z][0-9A-Za-z]* (e.g. A)
if not string.match( id, '^%d*[A-Za-z][0-9A-Za-z]*$' ) then
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
return false
end
end
return '[http://socialarchive.iath.virginia.edu/ark:/99166/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'SNAC-ID' )
return '[https://islamansiklopedisi.org.tr/'..id..' '..id..']'..p.getCatForId( 'TDVİA' )
end
end


function p.dblpLink( id )
function p.teLink( id )
--P2456's format regex: \d{2,3} /\d+(-\d+)?|[a-z] /[a-zA-Z][0-9A-Za-z]*(-\d+)? (e.g. 123/123)
--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)
if not string.match( id, '^%d%d%d?/%d+$' ) and
local e1, e2 = id:match( '^E([1-8])%.(%d%d?)%.%d%d?%.%d%d?%.%d%.%d%.%d%d?%d?$' )
  not string.match( id, '^%d%d%d?/%d+%-%d+$' ) and
if not e1 then
  not string.match( id, '^[a-z]/[a-zA-Z][0-9A-Za-z]*$' ) and
return false
  not string.match( id, '^[a-z]/[a-zA-Z][0-9A-Za-z]*%-%d+$' ) then
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
return false
end
end
return '[https://dblp.org/pid/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'DBLP' )
return '[https://collections.tepapa.govt.nz/agent/'..id..' '..id..']'..p.getCatForId( 'TePapa' )
end
end


function p.acmLink( id )
function p.thLink( id )
--P864's format regex: \d{11} (e.g. 12345678901)
--P1694's format regex: H\d\.\d{2}\.\d{2}\.\d\.\d{5} (e.g. H1.23.45.6.78901)
if not string.match( id, '^%d%d%d%d%d%d%d%d%d%d%d$' ) then
local h1, h2 = id:match( '^H(%d)%.(%d%d)%.%d%d%.%d%.%d%d%d%d%d$' )
if not h1 then
return false
return false
end
end
return '[https://dl.acm.org/author_page.cfm?id=' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'ACM-DL' )
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


function p.autoresuyLink( id )
function p.tlsLink( id )
--P2558's format regex: [1-9]\d{0,4} (e.g. 12345)
local id2 = id:gsub(' +', '_')
if not string.match( id, '^[1-9]%d?%d?%d?%d?$' ) then
--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
return false
end
end
return '[http://autores.uy/autor/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'autores.uy' )
return '[http://tls.theaterwissenschaft.ch/wiki/'..id2..' '..id..']'..p.getCatForId( 'TLS' ) --no https as of 9/2019
end
end


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


function p.bildLink( id )
function p.ukparlLink( id )
--P2092's format regex: \d+ (e.g. 1)
--P6213's format regex: [a-zA-Z\d]{8} (e.g. AQUupyiR)
if not string.match( id, '^%d+$' ) then
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
return false
end
end
return '[https://www.bildindex.de/document/obj' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'Bildindex' )
return '[https://id.parliament.uk/'..id..' '..id..']'..p.getCatForId( 'UKPARL' )
end
end


function p.jocondeLink( id )
function p.ulanLink( id )
--P347's format regex: [\-0-9A-Za-z]{11} (e.g. 12345678901)
--P245's format regex: 500\d{6} (e.g. 500123456)
local regex = '^' .. string.rep('[%-0-9A-Za-z]', 11) .. '$'
if not id:match( '^500%d%d%d%d%d%d$' ) then
if not string.match( id, regex ) then
return false
return false
end
end
return '[http://www2.culture.gouv.fr/public/mistral/joconde_fr?ACTION=CHERCHER&FIELD_1=REF&VALUE_1=' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'Joconde' )
return '[https://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid='..id..' '..id..']'..p.getCatForId( 'ULAN' )
end
end


function p.rkdidLink( id )
function p.uscongressLink( id )
--P350's format regex: [1-9]\d{0,5} (e.g. 123456)
--P1157's format regex: [A-Z]00[01]\d{3} (e.g. A000123)
if not string.match( id, '^[1-9]%d?%d?%d?%d?%d?$' ) then
if not id:match( '^[A-Z]00[01]%d%d%d$' ) then
return false
return false
end
end
return '[https://rkd.nl/nl/explore/images/' .. id .. ' ' .. id .. ']' .. p.getCatForId( 'RKDID' )
return '[http://bioguide.congress.gov/scripts/biodisplay.pl?index='..id..' '..id..']'..p.getCatForId( 'USCongress' ) --no https as of 9/2019
end
end


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


function p.lnbLink( id )
function p.viafLink( id )
--P1368's format regex: \d{9} (e.g. 123456789)
--P214's format regex: [1-9]\d(\d{0,7}|\d{17,20}) (e.g. 123456789, 1234567890123456789012)
if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
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
return false
end
end
return '[https://kopkatalogs.lv/F?func=direct&local_base=lnc10&doc_number=' .. id .. '&P_CON_LNG=ENG ' .. id .. ']' .. p.getCatForId( 'LNB' )
-- 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
end


function p.nskLink( id )
--Returns the ISNI check digit isni must be a string where the 15 first elements are digits, e.g. 0000000066534145
--P1375's format regex: \d{9} (e.g. 123456789)
function p.getIsniCheckDigit( isni )
if not string.match( id, '^%d%d%d%d%d%d%d%d%d$' ) then
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
return false
end
end
return '[http://katalog.nsk.hr/F/?func=direct&doc_number=' .. id .. '&local_base=nsk10 ' .. id .. ']' .. p.getCatForId( 'NSK' )
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
end


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


Line 482: Line 847:
function p.matchesWikidataRequirements( itemId, reqs )
function p.matchesWikidataRequirements( itemId, reqs )
for _, group in ipairs( reqs ) do
for _, group in ipairs( reqs ) do
local property = 'P' .. group[1]
local property = 'P'..group[1]
local qid = group[2]
local qid = group[2]
local statements = mw.wikibase.getBestStatements( itemId, property )
local statements = mw.wikibase.getBestStatements( itemId, property )
Line 490: Line 855:
if statement.mainsnak.datavalue.value['numeric-id'] == qid then
if statement.mainsnak.datavalue.value['numeric-id'] == qid then
return true
return true
end
end end end end end
end
end
end
end
return false
return false
end
function p.createRow( id, label, rawValue, link, withUid )
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 authority control identifiers (' .. id .. ')'
return '* <span class="error">The ' .. id .. ' id ' .. rawValue .. ' is not valid.</span>[[Category:' .. catName .. ']]' .. p.redCatLink(catName) .. '\n'
end
end


-- Creates a human-readable standalone wikitable version of p.conf, and tracking categories with page counts, for use in the documentation
-- 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 )
function p.docConfTable( frame )
local wikiTable = '{| class="wikitable sortable"\n' ..
local wikiTable = '{| class="wikitable sortable"\n'..
  '! rowspan=2 | Parameter\n' ..
  '! rowspan=2 | Parameter\n'..
  '! rowspan=2 | Label\n' ..
  '! rowspan=2 | Label\n'..
  '! rowspan=2; data-sort-type=number | Wikidata property\n' ..
  '! rowspan=2; data-sort-type=number | Wikidata property\n'..
  '! colspan=4 | Tracking categories and page counts\n' ..
  '! colspan=4 | Tracking categories and page counts\n'..
  '|-\n' ..
  '|-\n'..
  '! Articles\n' ..
  '! [[:Category:Wikipedia articles with authority control information|'..      'Articles]]\n'..
  '! User pages\n' ..
  '! [[:Category:User pages with authority control information|'..              'User pages]]\n'..
  '! Misc. pages\n' ..
  '! [[:Category:Miscellaneous pages with authority control information|'..      'Misc. pages]]\n'..
  '! Faulty IDs\n' ..
  '! [[:Category:Wikipedia articles with faulty authority control information|'..'Faulty IDs]]\n'..
  '|-\n'
  '|-\n'
 
local lang = mw.getContentLanguage()
for _, conf in pairs( p.conf ) do
for _, conf in pairs( p.conf ) do
local param, link, pid = conf[1], conf[2], conf[3]
local param, link, pid = conf[1], conf[2], conf[3]
if param == 'MBA' then param = 'MusicBrainz' end --it's weird; 'MusicBrainz' for cats only
local category = conf.category or param
local args = { id = 'f', pid }
local args = { id = 'f', pid }
local wpl = frame:expandTemplate{ title = 'Wikidata property link', args = args }
local wpl = frame:expandTemplate{ title = 'Wikidata property link', args = args }
local articleCat = 'Wikipedia articles with '..param..' identifiers'
--cats
local userCat =    'User pages with '..param..' identifiers'
local articleCat = 'Wikipedia articles with '..category..' identifiers'
local miscCat =    'Miscellaneous pages with '..param..' identifiers'
local userCat =    'User pages with '..category..' identifiers'
local faultyCat =  'Wikipedia articles with faulty authority control identifiers ('..param..')'
local miscCat =    'Miscellaneous pages with '..category..' identifiers'
local lang = mw.getContentLanguage()
local faultyCat =  'Wikipedia articles with faulty '..category..' identifiers'
--counts
local articleCount = lang:formatNum( mw.site.stats.pagesInCategory(articleCat, 'pages') )
local articleCount = lang:formatNum( mw.site.stats.pagesInCategory(articleCat, 'pages') )
local userCount =    lang:formatNum( mw.site.stats.pagesInCategory(userCat, 'pages') )
local userCount =    lang:formatNum( mw.site.stats.pagesInCategory(userCat, 'pages') )
local miscCount =    lang:formatNum( mw.site.stats.pagesInCategory(miscCat, 'pages') )
local miscCount =    lang:formatNum( mw.site.stats.pagesInCategory(miscCat, 'pages') )
local faultyCount =  lang:formatNum( mw.site.stats.pagesInCategory(faultyCat, 'pages') )
local faultyCount =  lang:formatNum( mw.site.stats.pagesInCategory(faultyCat, 'pages') )
if param == 'MusicBrainz' then param = 'MBA' end --it's weird; 'MBA' for param name only
--concat
wikiTable = wikiTable..'\n'..
wikiTable = wikiTable..'\n'..
'|-\n'..
'|-\n'..
Line 549: Line 900:
'||style="text-align: right;"|[[:Category:'.. faultyCat..'|'.. faultyCount..']]'
'||style="text-align: right;"|[[:Category:'.. faultyCat..'|'.. faultyCount..']]'
end
end
return wikiTable .. '\n|}'
--append derivative WorldCat cats
local wcd = { 'WorldCat-LCCN', 'WorldCat-VIAF' }
for _, w in pairs(wcd) do
local articleCat = 'Wikipedia articles with '..w..' identifiers'
local articleCount = lang:formatNum( mw.site.stats.pagesInCategory(articleCat, 'pages') )
wikiTable = wikiTable..'\n'..
'|-\n'..
'||'..'—'..
'||'..w..
'||data-sort-value='..w..'|'..'—'..
'||style="text-align: right;"|[[:Category:'..articleCat..'|'..articleCount..']]'..
'||style="text-align: right;"|—'..
'||style="text-align: right;"|—'..
'||style="text-align: right;"|—'
end
return wikiTable..'\n|}'
end
end


--[[==========================================================================]]
--[[==========================================================================]]
--[[                                   Main                                  ]]
--[[                             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
-- Check that the Wikidata item has this property-->value before adding it
local reqs = {}
local reqs = {}


-- Parameter format: { name of the parameter, label, propertyId in Wikidata, formatting function }
-- Parameter format: { 'parameter name', 'label', propertyId # in Wikidata, formatting/validation function }
p.conf = {
p.conf = {
{ 'ACM-DL', '[[ACM Digital Library|ACM DL]]', 864, p.acmLink },
{ 'AAG', '[[AAG (identifier)|AAG]]', 3372, p.aagLink },
{ 'autores.uy', '[[autores.uy]]', 2558, p.autoresuyLink },
{ 'ACM-DL', '[[ACM DL (identifier)|ACM DL]]', 864, p.acmLink },
{ 'BALaT', '[[:d:Q2876688|BALaT]]', 3293, p.balatLink },
{ 'ADB', '[[ADB (identifier)|ADB]]', 1907, p.adbLink },
{ 'BIBSYS', '[[BIBSYS]]', 1015, p.bibsysLink },
{ 'AGSA', '[[AGSA (identifier)|AGSA]]', 6804, p.agsaLink },
{ 'Bildindex', '[[Marburg Picture Index|Bildindex]]', 2092, p.bildLink },
{ 'autores.uy', '[[autores.uy (identifier)|autores.uy]]', 2558, p.autoresuyLink },
{ 'BNE', '[[Biblioteca Nacional de España|BNE]]', 950, p.bneLink },
{ 'AWR', '[[AWR (identifier)|AWR]]', 4186, p.awrLink },
{ 'BNF', '[[Bibliothèque nationale de France|BNF]]', 268, p.bnfLink },
{ 'BALaT', '[[BALaT (identifier)|BALaT]]', 3293, p.balatLink },
{ 'Botanist', '[[Author citation (botany)|Botanist]]', 428, p.botanistLink },
{ 'BIBSYS', '[[BIBSYS (identifier)|BIBSYS]]', 1015, p.bibsysLink },
{ 'BPN', '[[Biografisch Portaal|BPN]]', 651, p.bpnLink },
{ 'Bildindex', '[[Bildindex (identifier)|Bildindex]]', 2092, p.bildLink },
{ 'CINII', '[[CiNii]]', 271, p.ciniiLink },
{ 'BNC', '[[BNC (identifier)|BNC]]', 1890, p.bncLink },
{ 'DBLP', '[[DBLP]]', 2456, p.dblpLink },
{ 'BNE', '[[BNE (identifier)|BNE]]', 950, p.bneLink },
{ 'GND', '[[Integrated Authority File|GND]]', 227, p.gndLink },
{ 'BNF', '[[BNF (identifier)|BNF]]', 268, p.bnfLink },
{ 'HDS', '[[Historical Dictionary of Switzerland|HDS]]', 902, p.hdsLink },
{ 'Botanist', '[[Botanist (identifier)|Botanist]]', 428, p.botanistLink },
{ 'IAAF', '[[International Association of Athletics Federations|IAAF]]', 1146, p.iaafLink },
{ 'BPN', '[[BPN (identifier)|BPN]]', 651, p.bpnLink },
{ 'ISNI', '[[International Standard Name Identifier|ISNI]]', 213, p.isniLink },
{ 'CANTIC', '[[CANTIC (identifier)|CANTIC]]', 1273, p.canticLink },
{ 'Joconde', '[[Joconde|Joconde]]' , 347, p.jocondeLink },
{ 'CINII', '[[CiNii (identifier)|CiNii]]', 271, p.ciniiLink },
{ 'KULTURNAV', '[[KulturNav]]', 1248, p.kulturnavLink },
{ 'CWGC', '[[CWGC (identifier)|CWGC]]', 1908, p.cwgcLink },
{ 'LCCN', '[[Library of Congress Control Number|LCCN]]', 244, p.lccnLink },
{ 'DAAO', '[[DAAO (identifier)|DAAO]]', 1707, p.daaoLink },
{ 'LIR', '[[Historical Dictionary of Switzerland#Lexicon_Istoric_Retic|LIR]]', 886, p.lirLink },
{ 'DBLP', '[[DBLP (identifier)|DBLP]]', 2456, p.dblpLink },
{ 'LNB', '[[National Library of Latvia|LNB]]', 1368, p.lnbLink },
{ 'DIB', '[[DIB (identifier)|DIB]]', 6829, p.dibLink },
{ 'Léonore', '[[Base Léonore|Léonore]]', 640, p.leonoreLink },
{ 'DSI', '[[DSI (identifier)|DSI]]', 2349, p.dsiLink },
{ 'MBA', '[[MusicBrainz]]', 434, p.mbaLink },
{ 'FNZA', '[[FNZA (identifier)|FNZA]]', 6792, p.fnzaLink }, --wd
{ 'MGP', '[[Mathematics Genealogy Project|MGP]]', 549, p.mgpLink },
{ 'GND', '[[GND (identifier)|GND]]', 227, p.gndLink },
{ 'NARA', '[[National Archives and Records Administration|NARA]]', 1225, p.naraLink },
{ 'HDS', '[[HDS (identifier)|HDS]]', 902, p.hdsLink },
{ 'NCL', '[[National Central Library|NCL]]', 1048, p.nclLink },
{ 'IAAF', '[[IAAF (identifier)|IAAF]]', 1146, p.iaafLink },
{ 'NDL', '[[National Diet Library|NDL]]', 349, p.ndlLink },
{ 'ICCU', '[[ICCU (identifier)|ICCU]]', 396, p.iccuLink }, --formerly SBN
{ 'NKC', '[[National Library of the Czech Republic|NKC]]', 691, p.nkcLink },
{ 'ICIA', '[[ICIA (identifier)|ICIA]]', 1736, p.iciaLink },
{ 'NLA', '[[National Library of Australia|NLA]]', 409, p.nlaLink },
{ 'IEU', '[[IEU (identifier)|IEU]]', 9070, p.ieuLink },
{ 'NSK', '[[National and University Library in Zagreb|NSK]]', 1375, p.nskLink },
{ 'ISNI', '[[ISNI (identifier)|ISNI]]', 213, p.isniLink },
{ 'ORCID', '[[ORCID]]', 496, p.orcidLink },
{ 'Joconde', '[[Joconde (identifier)|Joconde]]' , 347, p.jocondeLink },
{ 'PIC', '[[:d:Q23892012|PIC]]', 2750, p.picLink },
{ 'KULTURNAV', '[[KulturNav (identifier)|KulturNav]]', 1248, p.kulturnavLink },
{ 'RID', '[[ResearcherID]]', 1053, p.ridLink },
{ 'LCCN', '[[LCCN (identifier)|LCCN]]', 244, p.lccnLink },
{ 'RKDartists', '[[Netherlands Institute for Art History#Online artist pages|RKD]]', 650, p.rkdartistsLink },
{ 'LIR', '[[LIR (identifier)|LIR]]', 886, p.lirLink },
{ 'RKDID', '[[:d:Q17299580|RKDimages ID]]', 350, p.rkdidLink },
{ 'LNB', '[[LNB (identifier)|LNB]]', 1368, p.lnbLink },
{ 'RSL', '[[Russian State Library|RSL]]', 947, p.rslLink },
{ 'Léonore', '[[Léonore (identifier)|Léonore]]', 640, p.leonoreLink },
{ 'SBN', '[[Istituto Centrale per il Catalogo Unico|ICCU]]', 396, p.sbnLink },
{ 'MA', '[[MA (identifier)|MA]]', 6366, p.maLink },
{ 'SELIBR', '[[LIBRIS|SELIBR]]', 906, p.selibrLink },
{ 'MBA', '[[MBA (identifier)|MBA]]', 434, p.mbaLink, category = 'MusicBrainz' }, --special category name
{ 'SIKART', '[[SIKART]]', 781, p.sikartLink },
{ 'MBAREA', '[[MBAREA (identifier)|MBAREA]]', 982, p.mbareaLink, category = 'MusicBrainz area' }, --special category name
{ 'SNAC-ID', '[[SNAC]]', 3430, p.snacLink },
{ 'MBI', '[[MBI (identifier)|MBI]]', 1330, p.mbiLink, category = 'MusicBrainz instrument' }, --special category name
{ 'SUDOC', '[[Système universitaire de documentation|SUDOC]]', 269, p.sudocLink },
{ 'MBL', '[[MBL (identifier)|MBL]]', 966, p.mblLink, category = 'MusicBrainz label' }, --special category name
{ 'TLS', '[[Theaterlexikon der Schweiz|TLS]]', 1362, p.tlsLink },
{ 'MBP', '[[MBP (identifier)|MBP]]', 1004, p.mbpLink, category = 'MusicBrainz place' }, --special category name
{ 'ULAN', '[[Union List of Artist Names|ULAN]]', 245, p.ulanLink },
{ 'MBRG', '[[MBRG (identifier)|MBRG]]', 436, p.mbrgLink, category = 'MusicBrainz release group' }, --special category name
{ 'USCongress', '[[Biographical Directory of the United States Congress|US Congress]]', 1157, p.uscongressLink },
{ 'MBS', '[[MBS (identifier)|MBS]]', 1407, p.mbsLink, category = 'MusicBrainz series' }, --special category name
{ 'VIAF', '[[Virtual International Authority File|VIAF]]', 214, p.viafLink },
{ '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', '[[PIC (identifier)|PIC]]', 2750, p.picLink }, --wd
{ 'PLWABN', '[[PLWABN (identifier)|PLWABN]]', 7293, p.plwabnLink },
{ 'Publons', '[[Publons (identifier)|Publons]]', 3829, p.publonsLink },
{ '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
-- Legitimate aliases to p.conf, for convenience
-- Format: { alias, parameter name in p.conf }
-- Format: { 'alias', 'parameter name in p.conf' }
p.aliases = {
p.aliases = {
{ 'RLS', 'RSL' },
{ 'Leonore', 'Léonore' }, --alias name without diacritics
{ 'leonore', 'Léonore' }, --lowercase variant without diacritics
{ 'MusicBrainz', 'MBA' },
{ 'MusicBrainz', 'MBA' },
{ 'Leonore', 'Léonore' },
{ '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, which also get assigned to a tracking cat
-- 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 }
-- Format: { 'deprecated parameter name', 'replacement parameter name in p.conf' }
p.deprecated = {
p.deprecated = {
{ 'GKD', 'GND' },
{ 'GKD', 'GND' },
{ 'PND', 'GND' },
{ 'PND', 'GND' },
{ 'RLS', 'RSL' },
{ 'SWD', 'GND' },
{ 'SWD', 'GND' },
{ 'NARA-organization', 'NARA' },
{ 'NARA-organization', 'NARA' },
{ 'NARA-person', 'NARA' },
{ 'NARA-person', 'NARA' },
}
}
--[[==========================================================================]]
--[[                                  Main                                  ]]
--[[==========================================================================]]


function p.authorityControl( frame )
function p.authorityControl( frame )
local resolve = require( "Module:ResolveEntityId" )
local resolveEntity = require( "Module:ResolveEntityId" )
local title = mw.title.getCurrentTitle()
local namespace = title.namespace
local talkspace = (mw.site.talkNamespaces[namespace] ~= nil)
local testcases = (string.sub(title.subpageText,1,9) == 'testcases')
local parentArgs = frame:getParent().args
local parentArgs = frame:getParent().args
local elements = {} --create/insert rows later
local elements = {} --create/insert rows later
local worldcatCat = ''
local multipleIdCat = ''
local suppressedIdCat = ''
local suppressedIdCat = ''
local deprecatedIdCat = ''
local deprecatedIdCat = ''
 
--Redirect aliases to proper parameter names
--Redirect aliases to proper parameter names
for _, a in pairs( p.aliases ) do
for _, a in pairs( p.aliases ) do
Line 643: Line 1,064:
end
end
end
end
 
--Redirect deprecated parameters to proper parameter names, and assign tracking cat
--Redirect deprecated parameters to proper parameter names, and assign tracking cat
for _, d in pairs( p.deprecated ) do
for _, d in pairs( p.deprecated ) do
Line 654: Line 1,075:
end
end
end
end
 
--Use QID= parameter for testing/example purposes only
--Use QID= parameter for testing/example purposes only
local itemId = nil
local itemId = nil
if testcases or talkspace then
if namespace ~= 0 then
if parentArgs['QID'] then
local qid = parentArgs['qid'] or parentArgs['QID']
itemId = 'Q' .. mw.ustring.gsub(parentArgs['QID'], '^[Qq]', '')
if qid then
itemId = resolve._entityid(frame, itemId) --nil if unresolvable
itemId = 'Q'..mw.ustring.gsub(qid, '^[Qq]', '')
itemId = resolveEntity._id(itemId) --nil if unresolvable
end
end
else
else
itemId = mw.wikibase.getEntityIdForCurrentPage()
itemId = mw.wikibase.getEntityIdForCurrentPage()
end
end
 
--Wikidata fallback if requested
--Wikidata fallback if requested
if itemId then
if itemId then
for _, params in ipairs( p.conf ) do
for _, params in ipairs( p.conf ) do
if params[3] > 0 then
if params[3] > 0 then
local val = parentArgs[params[1]]
local val = parentArgs[mw.ustring.lower(params[1])] or parentArgs[params[1]]
if val == nil or val == '' then
if val == nil or val == '' then
local canUseWikidata = nil
local canUseWikidata = nil
Line 679: Line 1,101:
end
end
if canUseWikidata then
if canUseWikidata then
local wikidataIds = p.getIdsFromWikidata( itemId, 'P' .. params[3] )
local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
if wikidataIds[1] then
if wikidataIds[1] then
if val == '' and (namespace == 0 or testcases) then
if val == '' and (namespace == 0 or testcases) then
suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|' .. params[1] .. ']]'
suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|'..params[1]..']]'
else
else
parentArgs[params[1]] = wikidataIds[1]
parentArgs[params[1]] = wikidataIds[1]
end end end end end end end
end end end end end end end
 
--Worldcat
if parentArgs['WORLDCATID'] and parentArgs['WORLDCATID'] ~= '' then
table.insert( elements, p.createRow( 'WORLDCATID', '', parentArgs['WORLDCATID'], '[https://www.worldcat.org/identities/' .. parentArgs['WORLDCATID'] .. ' WorldCat Identities]', false ) ) --Validation?
elseif parentArgs['VIAF'] and string.match( parentArgs['VIAF'], '^%d+$' ) then -- Hackishly copy the validation code; this should go away when we move to using P1793 and P1630
table.insert( elements, p.createRow( 'VIAF', '', parentArgs['VIAF'], '[https://www.worldcat.org/identities/containsVIAFID/' .. parentArgs['VIAF'] .. ' WorldCat Identities]', false ) )
elseif parentArgs['LCCN'] and parentArgs['LCCN'] ~= '' then
local lccnParts = p.splitLccn( parentArgs['LCCN'] )
if lccnParts and lccnParts[1] ~= 'sh' then
table.insert( elements, p.createRow( 'LCCN', '', parentArgs['LCCN'], '[https://www.worldcat.org/identities/lccn-' .. lccnParts[1] .. lccnParts[2] .. '-' .. lccnParts[3] .. ' WorldCat Identities]', false ) )
end
end
 
--Configured rows
--Configured rows
local rct = 0
local rct = 0
for _, params in ipairs( p.conf ) do
for _, params in ipairs( p.conf ) do
local val = parentArgs[params[1]]
local val = parentArgs[mw.ustring.lower(params[1])] or parentArgs[params[1]]
if val and val ~= '' then
local tval, tlinks = {}, {}
table.insert( elements, p.createRow( params[1], params[2] .. ':', val, params[4]( val ), true ) )
if val and val ~= '' and type(params[4]) == 'function' then
table.insert( tval, val )
table.insert( tlinks, params[4]( val ) )
end
-- collect other unique vals (IDs) from WD, if present
if itemId and tval[1] then
local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
for _, v in pairs( wikidataIds ) do
local bnew = true
for _, w in pairs( tval ) do
if v == w then bnew = false end
end
if bnew then
table.insert( tval, v )
table.insert( tlinks, params[4]( v ) )
end
end
end
-- assemble
if tval[1] then
table.insert( elements, p.createRow( params[1], params[2]..':', tval, nil, tlinks, true, params.category ) )
rct = rct + 1
rct = rct + 1
if tval[2] then
multipleIdCat = p.getCatForId( 'multiple' )
end
end
end
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..']', nil, 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..']', nil, 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..']', nil, 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 Navbox = require('Module:Navbox')
local elementsCat = ''
local elementsCat = ''
if rct > 13 then
if rct == 0 or rct >= 25 then
local catName = 'AC with ' .. rct .. ' elements'
local eCat = 'AC with '..rct..' elements'
elementsCat  = '[[Category:' .. catName .. ']]' .. p.redCatLink(catName)
elementsCat  = '[[Category:'..eCat..']]'..p.redCatLink(eCat)
end
end
 
local outString = ''
local outString = ''
if #elements > 0 then
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( {
outString = Navbox._navbox( {
name  = 'Authority control',
name  = 'Authority control',
navboxclass = 'authority-control',
bodyclass = 'hlist',
bodyclass = 'hlist',
group1 = '[[Help:Authority control|Authority control]]',
group1 = '[[Help:Authority control|Authority control]]'..pencil,
list1 = table.concat( elements )
list1 = table.concat( elements )
} )
} )
local auxCats = elementsCat .. suppressedIdCat .. deprecatedIdCat
if testcases then
auxCats = mw.ustring.gsub(auxCats, '(%[%[)(Category)', '%1:%2') --for easier checking
end
if namespace ~= 0 then
outString = mw.ustring.gsub(outString, '(%[%[)(Category:Wikipedia articles)', '%1:%2') --by definition
end
outString = outString .. auxCats
end
end
 
local auxCats = worldcatCat .. elementsCat .. multipleIdCat .. 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
return outString
end
end


return p
return p

Revision as of 15:17, 9 March 2021

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 ..p.redCatLink(catName) end

function p.redCatLink( catName ) --catName == 'Blah' (not 'Category:Blah', not ) if catName and catName ~= and testcases == false and mw.title.new(catName, 14).exists == false then return end return end

function p.createRow( id, label, rawValues, link, links, withUid, specialCat ) local catName = 'Wikipedia articles with faulty '..(specialCat or id)..' identifiers' if links then -- all links[] use withUid = false; no check needed local row = '*'..label for i, l in ipairs( links ) do if i == 1 then row = row..' ' else row = row..', ' end if l then row = row..''..l..'' else row = row..'The '..id..' id '..rawValues[i]..' is not valid.'..p.redCatLink(catName) end end return row..'\n' elseif link then if withUid then return '*'..label..' '..link..'\n' end return '*'..label..' '..link..'\n' end

return '* The '..id..' id '..rawValues..' is not valid.'..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..id..''..p.getCatForId( 'DBLP' ) end

function p.dibLink( id ) --P6829's format regex: a\d{4}\d?(-[A-D])? (e.g. a1953) if not id:match( '^a%d%d%d%d%d?%-?[A-D]?$' ) then return false end return ''..id..''..p.getCatForId( 'DIB' ) 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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..id..''..p.getCatForId( 'ICIA' ) end

function p.ieuLink( id ) --P9070's format regex: [A-Z]\\[A-Z]\\[A-Za-z0-9]+ (e.g. K\Y\Kyiv) if not id:match( '^[A-Z]\\[A-Z]\\%w+$' ) then return false end return ''..id..''..p.getCatForId( 'IEU' ) end

function p.isniLink( id ) id = p.validateIsni( id ) --e.g. 0000-0000-6653-4145 if not id then return false end return ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..id..''..p.getCatForId( 'Léonore' ) --no https as of 9/2019 end

function p.maLink( id ) --P6366's format regex: [1-9]\d{4,9} (e.g. 1498221862) if not id:match( '^[1-9]%d%d%d%d%d?%d?%d?%d?%d?$' ) then return false end return ''..id..''..p.getCatForId( 'MA' ) 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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 '[1]'..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 ''..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 ''..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 ''..id..''..p.getCatForId( 'ORCID' ) end

function p.plwabnLink( id ) --P7293's format regex: 981[0-9]{8}05606 (e.g. 9810696457305606) if not id:match( '^981%d%d%d%d%d%d%d%d05606*$' ) then return false end return ''..id..''..p.getCatForId( 'PLWABN' ) end

function p.publonsLink( id ) --P3829's format regex: \d+ (e.g. 654601) if not id:match( '^%d+$' ) then return false end return ''..id..''..p.getCatForId( 'Publons' ) 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 ''..id..''..p.getCatForId( 'PIC' ) end

function p.ridLink( id ) --P1053's format regex: [A-Z]{1,3}-\d{4}-(19|20)\d\d (e.g. AAS-5150-2020) if not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-19%d%d$' ) and not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-20%d%d$' ) then return false end return ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..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 ''..id..'' to ''..id..''. return ''..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'.. '! '.. 'Articles\n'.. '! '.. 'User pages\n'.. '! '.. 'Misc. pages\n'.. '! '..'Faulty IDs\n'.. '|-\n'

local lang = mw.getContentLanguage() for _, conf in pairs( p.conf ) do local param, link, pid = conf[1], conf[2], conf[3] local category = conf.category or param local args = { id = 'f', pid } local wpl = frame:expandTemplate{ title = 'Wikidata property link', args = args } --cats local articleCat = 'Wikipedia articles with '..category..' identifiers' local userCat = 'User pages with '..category..' identifiers' local miscCat = 'Miscellaneous pages with '..category..' identifiers' local faultyCat = 'Wikipedia articles with faulty '..category..' identifiers' --counts local articleCount = lang:formatNum( mw.site.stats.pagesInCategory(articleCat, 'pages') ) local userCount = lang:formatNum( mw.site.stats.pagesInCategory(userCat, 'pages') ) local miscCount = lang:formatNum( mw.site.stats.pagesInCategory(miscCat, 'pages') ) local faultyCount = lang:formatNum( mw.site.stats.pagesInCategory(faultyCat, 'pages') ) --concat wikiTable = wikiTable..'\n'.. '|-\n'.. '||'..param.. '||'..link.. '||data-sort-value='..pid..'|'..wpl.. '||style="text-align: right;"|'..articleCount..''.. '||style="text-align: right;"|'.. userCount..''.. '||style="text-align: right;"|'.. miscCount..''.. '||style="text-align: right;"|'.. faultyCount..'' end

--append derivative WorldCat cats local wcd = { 'WorldCat-LCCN', 'WorldCat-VIAF' } for _, w in pairs(wcd) do local articleCat = 'Wikipedia articles with '..w..' identifiers' local articleCount = lang:formatNum( mw.site.stats.pagesInCategory(articleCat, 'pages') ) wikiTable = wikiTable..'\n'.. '|-\n'.. '||'..'—'.. '||'..w.. '||data-sort-value='..w..'|'..'—'.. '||style="text-align: right;"|'..articleCount..''.. '||style="text-align: right;"|—'.. '||style="text-align: right;"|—'.. '||style="text-align: right;"|—' 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', 3372, p.aagLink }, { 'ACM-DL', 'ACM DL', 864, p.acmLink }, { 'ADB', 'ADB', 1907, p.adbLink }, { 'AGSA', 'AGSA', 6804, p.agsaLink }, { 'autores.uy', 'autores.uy', 2558, p.autoresuyLink }, { 'AWR', 'AWR', 4186, p.awrLink }, { 'BALaT', 'BALaT', 3293, p.balatLink }, { 'BIBSYS', 'BIBSYS', 1015, p.bibsysLink }, { 'Bildindex', 'Bildindex', 2092, p.bildLink }, { 'BNC', 'BNC', 1890, p.bncLink }, { 'BNE', 'BNE', 950, p.bneLink }, { 'BNF', 'BNF', 268, p.bnfLink }, { 'Botanist', 'Botanist', 428, p.botanistLink }, { 'BPN', 'BPN', 651, p.bpnLink }, { 'CANTIC', 'CANTIC', 1273, p.canticLink }, { 'CINII', 'CiNii', 271, p.ciniiLink }, { 'CWGC', 'CWGC', 1908, p.cwgcLink }, { 'DAAO', 'DAAO', 1707, p.daaoLink }, { 'DBLP', 'DBLP', 2456, p.dblpLink }, { 'DIB', 'DIB', 6829, p.dibLink }, { 'DSI', 'DSI', 2349, p.dsiLink }, { 'FNZA', 'FNZA', 6792, p.fnzaLink }, --wd { 'GND', 'GND', 227, p.gndLink }, { 'HDS', 'HDS', 902, p.hdsLink }, { 'IAAF', 'IAAF', 1146, p.iaafLink }, { 'ICCU', 'ICCU', 396, p.iccuLink }, --formerly SBN { 'ICIA', 'ICIA', 1736, p.iciaLink }, { 'IEU', 'IEU', 9070, p.ieuLink }, { 'ISNI', 'ISNI', 213, p.isniLink }, { 'Joconde', 'Joconde' , 347, p.jocondeLink }, { 'KULTURNAV', 'KulturNav', 1248, p.kulturnavLink }, { 'LCCN', 'LCCN', 244, p.lccnLink }, { 'LIR', 'LIR', 886, p.lirLink }, { 'LNB', 'LNB', 1368, p.lnbLink }, { 'Léonore', 'Léonore', 640, p.leonoreLink }, { 'MA', 'MA', 6366, p.maLink }, { 'MBA', 'MBA', 434, p.mbaLink, category = 'MusicBrainz' }, --special category name { 'MBAREA', 'MBAREA', 982, p.mbareaLink, category = 'MusicBrainz area' }, --special category name { 'MBI', 'MBI', 1330, p.mbiLink, category = 'MusicBrainz instrument' }, --special category name { 'MBL', 'MBL', 966, p.mblLink, category = 'MusicBrainz label' }, --special category name { 'MBP', 'MBP', 1004, p.mbpLink, category = 'MusicBrainz place' }, --special category name { 'MBRG', 'MBRG', 436, p.mbrgLink, category = 'MusicBrainz release group' }, --special category name { 'MBS', 'MBS', 1407, p.mbsLink, category = 'MusicBrainz series' }, --special category name { 'MBW', 'MBW work', 435, p.mbwLink, category = 'MusicBrainz work' }, --special category name { 'MGP', 'MGP', 549, p.mgpLink }, { 'NARA', 'NARA', 1225, p.naraLink }, { 'NCL', 'NBL', 1048, p.nclLink }, { 'NDL', 'NDL', 349, p.ndlLink }, { 'NGV', 'NGV', 2041, p.ngvLink }, { 'NKC', 'NKC', 691, p.nkcLink }, { 'NLA', 'NLA', 409, p.nlaLink }, { 'NLG', 'NLG', 3348, p.nlgLink }, { 'NLI', 'NLI', 949, p.nliLink }, { 'NLK', 'NLK', 5034, p.nlkLink }, { 'NLP', 'NLP', 1695, p.nlpLink }, { 'NLR', 'NLR', 1003, p.nlrLink }, { 'NSK', 'NSK', 1375, p.nskLink }, { 'NTA', 'NTA', 1006, p.ntaLink }, { 'ORCID', 'ORCID', 496, p.orcidLink }, { 'PIC', 'PIC', 2750, p.picLink }, --wd { 'PLWABN', 'PLWABN', 7293, p.plwabnLink }, { 'Publons', 'Publons', 3829, p.publonsLink }, { 'RID', 'ResearcherID', 1053, p.ridLink }, { 'RERO', 'RERO', 3065, p.reroLink }, { 'RKDartists', 'RKD', 650, p.rkdartistsLink }, { 'RKDID', 'RKDimages ID', 350, p.rkdidLink }, { 'RSL', 'RSL', 947, p.rslLink }, { 'SELIBR', 'SELIBR', 906, p.selibrLink }, { 'SIKART', 'SIKART', 781, p.sikartLink }, { 'SNAC-ID', 'SNAC', 3430, p.snacLink }, { 'SUDOC', 'SUDOC', 269, p.sudocLink }, { 'S2AuthorId', 'S2AuthorId', 4012, p.s2authoridLink, category = 'Semantic Scholar author' }, --special category name { 'TA98', 'TA98', 1323, p.ta98Link }, { 'TDVİA', 'TDVİA', 7314, p.tdviaLink }, { 'TE', 'TE', 1693, p.teLink }, { 'TePapa', 'TePapa', 3544, p.tepapaLink }, { 'TH', 'TH', 1694, p.thLink }, { 'TLS', 'TLS', 1362, p.tlsLink }, { 'Trove', 'Trove', 1315, p.troveLink }, --formerly NLA-person { 'UKPARL', 'UKPARL', 6213, p.ukparlLink }, { 'ULAN', 'ULAN', 245, p.ulanLink }, { 'USCongress', 'US Congress', 1157, p.uscongressLink }, { 'VcBA', 'VcBA', 8034, p.vcbaLink }, { 'VIAF', 'VIAF', 214, p.viafLink }, { 'WORLDCATID', '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 -- 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 multipleIdCat = 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 = 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 = 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]] local tval, tlinks = {}, {} if val and val ~= and type(params[4]) == 'function' then table.insert( tval, val ) table.insert( tlinks, params[4]( val ) ) end -- collect other unique vals (IDs) from WD, if present if itemId and tval[1] then local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] ) for _, v in pairs( wikidataIds ) do local bnew = true for _, w in pairs( tval ) do if v == w then bnew = false end end if bnew then table.insert( tval, v ) table.insert( tlinks, params[4]( v ) ) end end end -- assemble if tval[1] then table.insert( elements, p.createRow( params[1], params[2]..':', tval, nil, tlinks, true, params.category ) ) rct = rct + 1 if tval[2] then multipleIdCat = p.getCatForId( 'multiple' ) end 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: 'PATH')..' '..worldcatId..'', nil, 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 (via VIAF): '..viafId..'', nil, false ) ) if namespace == 0 then worldcatCat = end elseif lccnId and lccnId ~= and p.lccnLink( lccnId ) then --LCCN must be present, unsuppressed, & validated local lccnParts = p.splitLccn( lccnId ) if lccnParts and lccnParts[1] ~= 'sh' then local lccnIdFmtd = lccnParts[1]..lccnParts[2]..'-'..lccnParts[3] table.insert( elements, p.createRow( 'LCCN', , lccnId, 'WorldCat Identities (via LCCN): '..lccnIdFmtd..'', nil, false ) ) if namespace == 0 then worldcatCat = end end end elseif worldcatId == then --if WORLDCATID suppressed suppressedIdCat = end

local Navbox = require('Module:Navbox') local elementsCat = if rct == 0 or rct >= 25 then local eCat = 'AC with '..rct..' elements' elementsCat = ..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 = 'Authority control'..pencil, list1 = table.concat( elements ) } ) end

local auxCats = worldcatCat .. elementsCat .. multipleIdCat .. 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