Module:Authority control: Difference between revisions

From Zoophilia Wiki
Jump to navigationJump to search
m (1 revision imported)
en>Tom.Reding
Line 22: Line 22:
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
if catName and catName ~= '' and
  testcases == false and
  testcases == false and
Line 30: Line 30:
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
local totlen = 0
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
totlen = totlen + #rawValues[i] + 3 -- 3 chars b/w links
end
if totlen > 79 then
row = string.gsub(row, '"nowrap"', '""') -- avoid [[A–Z Series]]
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 35: Line 64:
--[[                      Property formatting functions                      ]]
--[[                      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 )
function p.aagLink( id )
Line 177: Line 208:
end
end
return '[https://ci.nii.ac.jp/author/'..id..'?l=en '..id..']'..p.getCatForId( 'CINII' )
return '[https://ci.nii.ac.jp/author/'..id..'?l=en '..id..']'..p.getCatForId( 'CINII' )
end
function p.cwgcLink( id )
--P1908's format regex: [1-9]\d* (e.g. 75228351)
if not id:match( '^[1-9]%d*$' ) then
return false
end
return '[https://www.cwgc.org/find-war-dead/casualty/'..id..'/ '..id..']'..p.getCatForId( 'CWGC' )
end
end


Line 196: Line 235:
end
end
return '[https://dblp.org/pid/'..id..' '..id..']'..p.getCatForId( 'DBLP' )
return '[https://dblp.org/pid/'..id..' '..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 '[https://dib.cambridge.org/viewReadPage.do?articleId='..id..' '..id..']'..p.getCatForId( 'DIB' )
end
end


Line 204: Line 251:
end
end
return '[http://www.uni-stuttgart.de/hi/gnt/dsi2/index.php?table_name=dsi&function=details&where_field=id&where_value='..id..' '..id..']'..p.getCatForId( 'DSI' )
return '[http://www.uni-stuttgart.de/hi/gnt/dsi2/index.php?table_name=dsi&function=details&where_field=id&where_value='..id..' '..id..']'..p.getCatForId( 'DSI' )
end
function p.fastLink( id )
--P2163's format regex: [1-9]\d{0,7} (e.g. 1916996)
if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?$' ) then
return false
end
return '[http://id.worldcat.org/fast/'..id..'/ '..id..']'..p.getCatForId( 'FAST' )
end
end


Line 247: Line 302:
end
end
return '[https://www.imj.org.il/artcenter/newsite/en/?artist='..id..' '..id..']'..p.getCatForId( 'ICIA' )
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
end
return '[http://www.encyclopediaofukraine.com/display.asp?linkpath=pages\\'..id..' '..id..']'..p.getCatForId( 'IEU' )
end
end


Line 254: Line 317:
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' ) --no https as of 9/2019
return '[https://isni.org/isni/'..id..' '..id:sub( 1, 4 )..' '..id:sub( 5, 8 )..' '..id:sub( 9, 12 )..' '..id:sub( 13, 16 )..']'..p.getCatForId( 'ISNI' ) --no https as of 9/2019
end
end


Line 308: Line 371:
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' ) --no https as of 9/2019
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


Line 315: Line 386:
return false
return false
end
end
return '[https://musicbrainz.org/artist/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz' ) --special category name
return '[https://musicbrainz.org/artist/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz' ) --special cat name
end
end


Line 323: Line 394:
return false
return false
end
end
return '[https://musicbrainz.org/area/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz area' ) --special category name
return '[https://musicbrainz.org/area/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz area' ) --special cat name
end
end


Line 331: Line 402:
return false
return false
end
end
return '[https://musicbrainz.org/instrument/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz instrument' ) --special category name
return '[https://musicbrainz.org/instrument/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz instrument' ) --special cat name
end
end


Line 339: Line 410:
return false
return false
end
end
return '[https://musicbrainz.org/label/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz label' ) --special category name
return '[https://musicbrainz.org/label/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz label' ) --special cat name
end
end


Line 347: Line 418:
return false
return false
end
end
return '[https://musicbrainz.org/place/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz place' ) --special category name
return '[https://musicbrainz.org/place/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz place' ) --special cat name
end
end


Line 355: Line 426:
return false
return false
end
end
return '[https://musicbrainz.org/release-group/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz release group' ) --special category name
return '[https://musicbrainz.org/release-group/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz release group' ) --special cat name
end
end


Line 363: Line 434:
return false
return false
end
end
return '[https://musicbrainz.org/series/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz series' ) --special category name
return '[https://musicbrainz.org/series/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz series' ) --special cat name
end
end


Line 371: Line 442:
return false
return false
end
end
return '[https://musicbrainz.org/work/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz work' ) --special category name
return '[https://musicbrainz.org/work/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz work' ) --special cat name
end
end


Line 469: Line 540:
return false
return false
end
end
return '[http://alephnew.bibnat.ro:8991/F?func=find-b&request='..id..'&find_code=SYS&adjacent=Y&local_base=NLR10 '..id..']'..p.getCatForId( 'NLR' )
return '[http://aleph.bibnat.ro:8991/F/?func=direct&local_base=NLR10&doc_number='..id..']'..p.getCatForId( 'NLR' )
end
end


Line 495: Line 566:
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
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 '[http://mak.bn.org.pl/cgi-bin/KHW/makwww.exe?BM=1&NU=1&IM=4&WI='..id..' '..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 '[https://publons.com/author/'..id..'/ '..id..']'..p.getCatForId( 'Publons' )
end
end


Line 506: Line 593:


function p.ridLink( id )
function p.ridLink( id )
--P1053's format regex: [A-Z]{1,3}-\d{4}-20[0-2]\d   (e.g. AAS-5150-2020)
--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%-20[0-2]%d$' ) then
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
return false
end
end
return '[https://www.researcherid.com/rid/'..id..' '..id..']'..p.getCatForId( 'RID' )
return '[https://www.researcherid.com/rid/'..id..' '..id..']'..p.getCatForId( 'RID' )
end
function p.rismLink( id )
--P5504's format regex: (pe|ks)?\[1-9]d* (e.g. pe30006410)
if not id:match( '^pe[1-9]%d*$' ) and --99% start with 'pe'
  not id:match( '^ks[1-9]%d*$' ) and
  not id:match( '^[1-9]%d*$' ) then
return false
end
return '[https://opac.rism.info/search?id='..id..' '..id..']'..p.getCatForId( 'RISM' )
end
end


Line 545: Line 643:
end
end


function p.sbnLink( id )
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)
--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
if not id:match( '^IT\\ICCU\\%d%d%d%d%d%d%d%d%d%d$' ) and
Line 551: Line 649:
return false
return false
end
end
return '[https://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid='..id..' '..id..']'..p.getCatForId( 'SBN' )
return '[https://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid='..id..' '..id..']'..p.getCatForId( 'ICCU' )
end
end


Line 591: Line 689:
return false
return false
end
end
return '[https://www.semanticscholar.org/author/'..id..' '..id..']'..p.getCatForId( 'Semantic Scholar author' ) --special category name
return '[https://www.semanticscholar.org/author/'..id..' '..id..']'..p.getCatForId( 'Semantic Scholar author' ) --special cat name
end
end


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


Line 675: Line 781:
end
end
return '[http://bioguide.congress.gov/scripts/biodisplay.pl?index='..id..' '..id..']'..p.getCatForId( 'USCongress' ) --no https as of 9/2019
return '[http://bioguide.congress.gov/scripts/biodisplay.pl?index='..id..' '..id..']'..p.getCatForId( 'USCongress' ) --no https as of 9/2019
end
function p.vcbaLink( id )
--P8034's format regex: \d{3}\/[1-9]\d{0,5} (e.g. 494/9793)
if not id:match( '^%d%d%d\/[1-9]%d?%d?%d?%d?%d?$' ) then
return false
end
local id2 = id:gsub('\/', '_')
return '[https://opac.vatlib.it/auth/detail/'..id2..' '..id..']'..p.getCatForId( 'VcBA' )
end
end


Line 683: Line 798:
return false
return false
end
end
-- If the "VIAF" entry at [[:m:Interwiki map]] would resolve to "https://viaf.org/viaf/$1" (rather than "http://viaf.org/viaf/$1", as it currently still does), the code below could change from '[https://viaf.org/viaf/'..id..' '..id..']' to '[[:VIAF:'..id..'|'..id..']]'.
return '[https://viaf.org/viaf/'..id..' '..id..']'..p.getCatForId( 'VIAF' )
return '[https://viaf.org/viaf/'..id..' '..id..']'..p.getCatForId( 'VIAF' )
end
end
Line 737: Line 853:


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


Line 751: Line 867:
end
end
return ids
return ids
end
function p.matchesWikidataRequirements( itemId, reqs )
for _, group in ipairs( reqs ) do
local property = 'P'..group[1]
local qid = group[2]
local statements = mw.wikibase.getBestStatements( itemId, property )
if statements then
for _, statement in ipairs( statements ) do
if statement.mainsnak.datavalue then
if statement.mainsnak.datavalue.value['numeric-id'] == qid then
return true
end end end end end
return false
end
function p.createRow( id, label, rawValue, link, withUid, specialCat )
if link then
if withUid then
return '*<span class="nowrap">'..label..' <span class="uid">'..link..'</span></span>\n'
end
return '*<span class="nowrap">'..label..' '..link..'</span>\n'
end
local catName = 'Wikipedia articles with faulty '..(specialCat or id)..' identifiers'
return '* <span class="error">The '..id..' id '..rawValue..' is not valid.</span>[[Category:'..catName..']]'..p.redCatLink(catName)..'\n'
end
end


Line 820: Line 910:
'||style="text-align: right;"|[[:Category:'.. faultyCat..'|'.. faultyCount..']]'
'||style="text-align: right;"|[[:Category:'.. faultyCat..'|'.. faultyCount..']]'
end
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;"|[[:Category:'..articleCat..'|'..articleCount..']]'..
'||style="text-align: right;"|—'..
'||style="text-align: right;"|—'..
'||style="text-align: right;"|—'
end
return wikiTable..'\n|}'
return wikiTable..'\n|}'
end
end
Line 828: Line 935:


-- 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.
-- 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 }
-- Parameter format: { 'parameter name', 'label', propertyId # in Wikidata, formatting/validation function }
p.conf = {
p.conf = {
{ 'AAG', '[[Auckland Art Gallery Toi o Tāmaki|AAG]]', 3372, p.aagLink },
{ 'AAG', '[[AAG (identifier)|AAG]]', 3372, p.aagLink },
{ 'ACM-DL', '[[Association for Computing Machinery#Portal and Digital Library|ACM DL]]', 864, p.acmLink },
{ 'ACM-DL', '[[ACM DL (identifier)|ACM DL]]', 864, p.acmLink },
{ 'ADB', '[[Australian Dictionary of Biography|ADB]]', 1907, p.adbLink },
{ 'ADB', '[[ADB (identifier)|ADB]]', 1907, p.adbLink },
{ 'AGSA', '[[Art Gallery of South Australia|AGSA]]', 6804, p.agsaLink },
{ 'AGSA', '[[AGSA (identifier)|AGSA]]', 6804, p.agsaLink },
{ 'autores.uy', '[[autores.uy]]', 2558, p.autoresuyLink },
{ 'autores.uy', '[[autores.uy (identifier)|autores.uy]]', 2558, p.autoresuyLink },
{ 'AWR', '[[Australian Women\'s Register|AWR]]', 4186, p.awrLink },
{ 'AWR', '[[AWR (identifier)|AWR]]', 4186, p.awrLink },
{ 'BALaT', '[[Royal Institute for Cultural Heritage#Online artworks pages|BALaT]]', 3293, p.balatLink },
{ 'BALaT', '[[BALaT (identifier)|BALaT]]', 3293, p.balatLink },
{ 'BIBSYS', '[[Bibsys|BIBSYS]]', 1015, p.bibsysLink },
{ 'BIBSYS', '[[BIBSYS (identifier)|BIBSYS]]', 1015, p.bibsysLink },
{ 'Bildindex', '[[Marburg Picture Index|Bildindex]]', 2092, p.bildLink },
{ 'Bildindex', '[[Bildindex (identifier)|Bildindex]]', 2092, p.bildLink },
{ 'BNC', '[[National Library of Chile|BNC]]', 1890, p.bncLink },
{ 'BNC', '[[BNC (identifier)|BNC]]', 1890, p.bncLink },
{ 'BNE', '[[Biblioteca Nacional de España|BNE]]', 950, p.bneLink },
{ 'BNE', '[[BNE (identifier)|BNE]]', 950, p.bneLink },
{ 'BNF', '[[BNF (identifier)|BNF]]', 268, p.bnfLink },
{ 'BNF', '[[BNF (identifier)|BNF]]', 268, p.bnfLink },
{ 'Botanist', '[[Author citation (botany)|Botanist]]', 428, p.botanistLink },
{ 'Botanist', '[[Botanist (identifier)|Botanist]]', 428, p.botanistLink },
{ 'BPN', '[[Biografisch Portaal|BPN]]', 651, p.bpnLink },
{ 'BPN', '[[BPN (identifier)|BPN]]', 651, p.bpnLink },
{ 'CANTIC', '[[Name and Title Authority File of Catalonia|CANTIC]]', 1273, p.canticLink },
{ 'CANTIC', '[[CANTIC (identifier)|CANTIC]]', 1273, p.canticLink },
{ 'CINII', '[[CiNii (identifier)|CiNii]]', 271, p.ciniiLink },
{ 'CINII', '[[CiNii (identifier)|CiNii]]', 271, p.ciniiLink },
{ 'DAAO', '[[Dictionary of Australian Artists|DAAO]]', 1707, p.daaoLink },
{ 'CWGC', '[[CWGC (identifier)|CWGC]]', 1908, p.cwgcLink },
{ 'DBLP', '[[DBLP]]', 2456, p.dblpLink },
{ 'DAAO', '[[DAAO (identifier)|DAAO]]', 1707, p.daaoLink },
{ 'DSI', '[[Stuttgart Database of Scientific Illustrators 1450–1950|DSI]]', 2349, p.dsiLink },
{ 'DBLP', '[[DBLP (identifier)|DBLP]]', 2456, p.dblpLink },
{ 'FNZA', '[[:d:Property:P6792|FNZA]]', 6792, p.fnzaLink },
{ 'DIB', '[[DIB (identifier)|DIB]]', 6829, p.dibLink },
{ 'DSI', '[[DSI (identifier)|DSI]]', 2349, p.dsiLink },
{ 'FAST', '[[FAST (identifier)|FAST]]', 2163, p.fastLink },
{ 'FNZA', '[[FNZA (identifier)|FNZA]]', 6792, p.fnzaLink },
{ 'GND', '[[GND (identifier)|GND]]', 227, p.gndLink },
{ 'GND', '[[GND (identifier)|GND]]', 227, p.gndLink },
{ 'HDS', '[[Historical Dictionary of Switzerland|HDS]]', 902, p.hdsLink },
{ 'HDS', '[[HDS (identifier)|HDS]]', 902, p.hdsLink },
{ 'IAAF', '[[World Athletics]]', 1146, p.iaafLink },
{ 'IAAF', '[[IAAF (identifier)|IAAF]]', 1146, p.iaafLink },
{ 'ICIA', '[[Information Center for Israeli Art|ICIA]]', 1736, p.iciaLink },
{ 'ICCU', '[[ICCU (identifier)|ICCU]]', 396, p.iccuLink }, --formerly SBN
{ 'ICIA', '[[ICIA (identifier)|ICIA]]', 1736, p.iciaLink },
{ 'IEU', '[[IEU (identifier)|IEU]]', 9070, p.ieuLink },
{ 'ISNI', '[[ISNI (identifier)|ISNI]]', 213, p.isniLink },
{ 'ISNI', '[[ISNI (identifier)|ISNI]]', 213, p.isniLink },
{ 'Joconde', '[[Joconde]]' , 347, p.jocondeLink },
{ 'Joconde', '[[Joconde (identifier)|Joconde]]' , 347, p.jocondeLink },
{ 'KULTURNAV', '[[KulturNav]]', 1248, p.kulturnavLink },
{ 'KULTURNAV', '[[KulturNav (identifier)|KulturNav]]', 1248, p.kulturnavLink },
{ 'LCCN', '[[LCCN (identifier)|LCCN]]', 244, p.lccnLink },
{ 'LCCN', '[[LCCN (identifier)|LCCN]]', 244, p.lccnLink },
{ 'LIR', '[[Historical Dictionary of Switzerland#Lexicon_Istoric_Retic|LIR]]', 886, p.lirLink },
{ 'LIR', '[[LIR (identifier)|LIR]]', 886, p.lirLink },
{ 'LNB', '[[National Library of Latvia|LNB]]', 1368, p.lnbLink },
{ 'LNB', '[[LNB (identifier)|LNB]]', 1368, p.lnbLink },
{ 'Léonore', '[[Base Léonore|Léonore]]', 640, p.leonoreLink },
{ 'Léonore', '[[Léonore (identifier)|Léonore]]', 640, p.leonoreLink },
{ 'MBA', '[[MusicBrainz]]', 434, p.mbaLink, category = 'MusicBrainz' }, --special category name
{ 'MA', '[[MA (identifier)|MA]]', 6366, p.maLink },
{ 'MBAREA', '[[MusicBrainz]]', 982, p.mbareaLink, category = 'MusicBrainz area' }, --special category name
{ 'MBA', '[[MBA (identifier)|MBA]]', 434, p.mbaLink, category = 'MusicBrainz' }, --special cat name
{ 'MBI', '[[MusicBrainz]]', 1330, p.mbiLink, category = 'MusicBrainz instrument' }, --special category name
{ 'MBAREA', '[[MBAREA (identifier)|MBAREA]]', 982, p.mbareaLink, category = 'MusicBrainz area' }, --special cat name
{ 'MBL', '[[MusicBrainz]]', 966, p.mblLink, category = 'MusicBrainz label' }, --special category name
{ 'MBI', '[[MBI (identifier)|MBI]]', 1330, p.mbiLink, category = 'MusicBrainz instrument' }, --special cat name
{ 'MBP', '[[MusicBrainz]]', 1004, p.mbpLink, category = 'MusicBrainz place' }, --special category name
{ 'MBL', '[[MBL (identifier)|MBL]]', 966, p.mblLink, category = 'MusicBrainz label' }, --special cat name
{ 'MBRG', '[[MusicBrainz]] release group', 436, p.mbrgLink, category = 'MusicBrainz release group' }, --special category name
{ 'MBP', '[[MBP (identifier)|MBP]]', 1004, p.mbpLink, category = 'MusicBrainz place' }, --special cat name
{ 'MBS', '[[MusicBrainz]]', 1407, p.mbsLink, category = 'MusicBrainz series' }, --special category name
{ 'MBRG', '[[MBRG (identifier)|MBRG]]', 436, p.mbrgLink, category = 'MusicBrainz release group' }, --special cat name
{ 'MBW', '[[MusicBrainz]] work', 435, p.mbwLink, category = 'MusicBrainz work' }, --special category name
{ 'MBS', '[[MBS (identifier)|MBS]]', 1407, p.mbsLink, category = 'MusicBrainz series' }, --special cat name
{ 'MGP', '[[Mathematics Genealogy Project|MGP]]', 549, p.mgpLink },
{ 'MBW', '[[MBW (identifier)|MBW]] work', 435, p.mbwLink, category = 'MusicBrainz work' }, --special cat name
{ 'NARA', '[[National Archives and Records Administration|NARA]]', 1225, p.naraLink },
{ 'MGP', '[[MGP (identifier)|MGP]]', 549, p.mgpLink },
{ 'NCL', '[[National Central Library|NCL]]', 1048, p.nclLink },
{ 'NARA', '[[NARA (identifier)|NARA]]', 1225, p.naraLink },
{ 'NDL', '[[National Diet Library|NDL]]', 349, p.ndlLink },
{ 'NCL', '[[NCL (identifier)|NBL]]', 1048, p.nclLink },
{ 'NGV', '[[National Gallery of Victoria|NGV]]', 2041, p.ngvLink },
{ 'NDL', '[[NDL (identifier)|NDL]]', 349, p.ndlLink },
{ 'NKC', '[[National Library of the Czech Republic|NKC]]', 691, p.nkcLink },
{ 'NGV', '[[NGV (identifier)|NGV]]', 2041, p.ngvLink },
{ 'NLA', '[[National Library of Australia|NLA]]', 409, p.nlaLink },
{ 'NKC', '[[NKC (identifier)|NKC]]', 691, p.nkcLink },
{ 'NLG', '[[National Library of Greece|NLG]]', 3348, p.nlgLink },
{ 'NLA', '[[NLA (identifier)|NLA]]', 409, p.nlaLink },
{ 'NLI', '[[National Library of Israel|NLI]]', 949, p.nliLink },
{ 'NLG', '[[NLG (identifier)|NLG]]', 3348, p.nlgLink },
{ 'NLK', '[[National Library of Korea|NLK]]', 5034, p.nlkLink },
{ 'NLI', '[[NLI (identifier)|NLI]]', 949, p.nliLink },
{ 'NLP', '[[National Library of Poland|NLP]]', 1695, p.nlpLink },
{ 'NLK', '[[NLK (identifier)|NLK]]', 5034, p.nlkLink },
{ 'NLR', '[[National Library of Romania|NLR]]', 1003, p.nlrLink },
{ 'NLP', '[[NLP (identifier)|NLP]]', 1695, p.nlpLink },
{ 'NSK', '[[National and University Library in Zagreb|NSK]]', 1375, p.nskLink },
{ 'NLR', '[[NLR (identifier)|NLR]]', 1003, p.nlrLink },
{ 'NTA', '[[Royal Library of the Netherlands|NTA]]', 1006, p.ntaLink },
{ 'NSK', '[[NSK (identifier)|NSK]]', 1375, p.nskLink },
{ 'NTA', '[[NTA (identifier)|NTA]]', 1006, p.ntaLink },
{ 'ORCID', '[[ORCID (identifier)|ORCID]]', 496, p.orcidLink },
{ 'ORCID', '[[ORCID (identifier)|ORCID]]', 496, p.orcidLink },
{ 'PIC', '[[:d:Q23892012|PIC]]', 2750, p.picLink },
{ 'PIC', '[[PIC (identifier)|PIC]]', 2750, p.picLink },
{ 'RID', '[[ResearcherID]]', 1053, p.ridLink },
{ 'PLWABN', '[[PLWABN (identifier)|PLWABN]]', 7293, p.plwabnLink },
{ 'RERO', '[[RERO (Library Network of Western Switzerland)|RERO]]', 3065, p.reroLink },
{ 'Publons', '[[Publons (identifier)|Publons]]', 3829, p.publonsLink },
{ 'RKDartists', '[[Netherlands Institute for Art History#Online artist pages|RKD]]', 650, p.rkdartistsLink },
{ 'RERO', '[[RERO (identifier)|RERO]]', 3065, p.reroLink },
{ 'RKDID', '[[Netherlands Institute for Art History#Online artworks pages|RKDimages ID]]', 350, p.rkdidLink },
{ 'RID', '[[RID (identifier)|ResearcherID]]', 1053, p.ridLink },
{ 'RSL', '[[Russian State Library|RSL]]', 947, p.rslLink },
{ 'RISM', '[[RISM (identifier)|RISM]]', 5504, p.rismLink },
{ 'SBN', '[[ICCU (identifier)|ICCU]]', 396, p.sbnLink },
{ '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 },
{ 'SELIBR', '[[SELIBR (identifier)|SELIBR]]', 906, p.selibrLink },
{ 'SIKART', '[[SIKART]]', 781, p.sikartLink },
{ 'SIKART', '[[SIKART (identifier)|SIKART]]', 781, p.sikartLink },
{ 'SNAC-ID', '[[SNAC]]', 3430, p.snacLink },
{ 'SNAC-ID', '[[SNAC-ID (identifier)|SNAC]]', 3430, p.snacLink },
{ 'SUDOC', '[[SUDOC (identifier)|SUDOC]]', 269, p.sudocLink },
{ 'SUDOC', '[[SUDOC (identifier)|SUDOC]]', 269, p.sudocLink },
{ 'S2AuthorId', '[[Semantic Scholar|S2AuthorId]]', 4012, p.s2authoridLink, category = 'Semantic Scholar author' }, --special category name
{ 'S2AuthorId', '[[S2AuthorId (identifier)|S2AuthorId]]', 4012, p.s2authoridLink, category = 'Semantic Scholar author' }, --special cat name
{ 'TA98', '[[Terminologia Anatomica|TA98]]', 1323, p.ta98Link },
{ 'TA98', '[[TA98 (identifier)|TA98]]', 1323, p.ta98Link },
{ 'TDVİA', '[[İslâm Ansiklopedisi|TDVİA]]', 7314, p.tdviaLink },
{ 'TDVİA', '[[TDVİA (identifier)|TDVİA]]', 7314, p.tdviaLink },
{ 'TE', '[[Terminologia Embryologica|TE]]', 1693, p.teLink },
{ 'TE', '[[TE (identifier)|TE]]', 1693, p.teLink },
{ 'TePapa', '[[Museum of New Zealand Te Papa Tongarewa|TePapa]]', 3544, p.tepapaLink },
{ 'TePapa', '[[TePapa (identifier)|TePapa]]', 3544, p.tepapaLink },
{ 'TH', '[[Terminologia Histologica|TH]]', 1694, p.thLink },
{ 'TH', '[[TH (identifier)|TH]]', 1694, p.thLink },
{ 'TLS', '[[Theaterlexikon der Schweiz|TLS]]', 1362, p.tlsLink },
{ 'TLS', '[[TLS (identifier)|TLS]]', 1362, p.tlsLink },
{ 'Trove', '[[Trove]]', 1315, p.troveLink }, --formerly NLA-person
{ 'Trove', '[[Trove (identifier)|Trove]]', 1315, p.troveLink }, --formerly NLA-person
{ 'ULAN', '[[Union List of Artist Names|ULAN]]', 245, p.ulanLink },
{ 'UKPARL', '[[UKPARL (identifier)|UKPARL]]', 6213, p.ukparlLink },
{ 'USCongress', '[[Biographical Directory of the United States Congress|US Congress]]', 1157, p.uscongressLink },
{ '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 },
{ 'VIAF', '[[VIAF (identifier)|VIAF]]', 214, p.viafLink },
{ 'WORLDCATID', '[[WorldCat|WorldCat Identities]]', 7859, nil },
{ 'WORLDCATID', '[[WorldCat Identities (identifier)|WorldCat Identities]]', 7859, nil },
}
}


Line 916: Line 1,030:
-- Format: { 'alias', 'parameter name in p.conf' }
-- Format: { 'alias', 'parameter name in p.conf' }
p.aliases = {
p.aliases = {
{ 'RLS', 'RSL' },
{ 'DNB', 'GND' }, --Deutsche Nationalbibliothek -> Gemeinsame Normdatei
{ 'Leonore', 'Léonore' }, --alias name without diacritics
{ 'leonore', 'Léonore' }, --lowercase variant without diacritics
{ 'MusicBrainz', 'MBA' },
{ 'MusicBrainz', 'MBA' },
{ 'MusicBrainz artist', 'MBA' },
{ 'MusicBrainz artist', 'MBA' },
Line 922: Line 1,038:
{ 'MusicBrainz release group', 'MBRG' },
{ 'MusicBrainz release group', 'MBRG' },
{ 'MusicBrainz work', 'MBW' },
{ 'MusicBrainz work', 'MBW' },
{ 'Leonore', 'Léonore' },
{ 'SBN', 'ICCU' }, --SBN alias to be deprecated at a later stage
{ 'TDVIA', 'TDVİA' },
{ 'TDVIA', 'TDVİA' }, --alias name without diacritics
{ 'tdvia', 'TDVİA' }, --lowercase variant without diacritics
}
}


Line 931: Line 1,048:
{ 'GKD', 'GND' },
{ 'GKD', 'GND' },
{ 'PND', 'GND' },
{ 'PND', 'GND' },
{ 'RLS', 'RSL' },
{ 'SWD', 'GND' },
{ 'SWD', 'GND' },
{ 'NARA-organization', 'NARA' },
{ 'NARA-organization', 'NARA' },
Line 942: Line 1,060:
function p.authorityControl( frame )
function p.authorityControl( frame )
local resolveEntity = require( "Module:ResolveEntityId" )
local resolveEntity = require( "Module:ResolveEntityId" )
local parentArgs = frame:getParent().args
local parentArgs = frame:getParent().args --WD IDs added here later
local iParentArgs = 0 --count original/manual parent args only later
local elements = {} --create/insert rows later
local elements = {} --create/insert rows later
local worldcatCat = ''
local worldcatCat = ''
local multipleIdCat = ''
local suppressedIdCat = ''
local suppressedIdCat = ''
local deprecatedIdCat = ''
local deprecatedIdCat = ''
local differentOnWDCat = ''
local sameOnWDCat = ''
--Redirect aliases to proper parameter names
--Redirect aliases to proper parameter names
Line 979: Line 1,101:
end
end
--Wikidata fallback if requested
--Wikidata fallback if available
if itemId then
if itemId then
local iMatches = 0
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 wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
if reqs[params[1]] then
if wikidataIds[1] then
canUseWikidata = p.matchesWikidataRequirements( itemId, reqs[params[1]] )
if val == '' and (namespace == 0 or testcases) then
else
suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|'..params[1]..']]'
canUseWikidata = true
else
parentArgs[params[1]] = wikidataIds[1] --add ID from WD
end
end
end
if canUseWikidata then
else
local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
iParentArgs = iParentArgs + 1
if wikidataIds[1] then
local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
if val == '' and (namespace == 0 or testcases) then
if wikidataIds[1] and differentOnWDCat == '' then
suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|'..params[1]..']]'
local bMatch = false
else
for _, wd in pairs( wikidataIds ) do
parentArgs[params[1]] = wikidataIds[1]
if val == wd then
end end end end end end end
iMatches = iMatches + 1
bMatch = true
end
end
if bMatch == false then
differentOnWDCat = '[[Category:Pages using authority control with parameters different on Wikidata|'..params[1]..']]'
end end end end end
if iMatches > 0 and iMatches == iParentArgs then
sameOnWDCat = '[[Category:Pages using authority control with parameters all matching Wikidata]]'
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]]
local tval, tlinks = {}, {} --init tables
if val and val ~= '' and type(params[4]) == 'function' then
if val and val ~= '' and type(params[4]) == 'function' then
table.insert( elements, p.createRow( params[1], params[2]..':', val, params[4]( val ), true, params.category ) )
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
--WorldCat
local worldcatId = parentArgs['WORLDCATID']
local worldcatId = parentArgs['worldcatid'] or parentArgs['WORLDCATID']
if worldcatId and worldcatId ~= '' then --if WORLDCATID present & unsuppressed
if worldcatId and worldcatId ~= '' then --if WORLDCATID present & unsuppressed
table.insert( elements, p.createRow( 'WORLDCATID', '', worldcatId, '[[WorldCat Identities]]: [https://www.worldcat.org/identities/'..mw.uri.encode(worldcatId, 'PATH')..' '..worldcatId..']', false ) ) --Validation?
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 = '[[Category:Wikipedia articles with WorldCat identifiers]]'
worldcatCat = p.getCatForId( 'WORLDCATID' )
elseif worldcatId == nil then --if WORLDCATID absent but unsuppressed
elseif worldcatId == nil then --if WORLDCATID absent but unsuppressed
local viafId = parentArgs['VIAF']
local viafId = parentArgs['viaf'] or parentArgs['VIAF']
local lccnId = parentArgs['LCCN']
local lccnId = parentArgs['lccn'] or parentArgs['LCCN']
if viafId and viafId ~= '' and p.viafLink( viafId ) then --VIAF must be present, unsuppressed, & validated
if viafId and viafId ~= '' and p.viafLink( viafId ) then --VIAF must be present, unsuppressed, & validated
table.insert( elements, p.createRow( 'VIAF', '', viafId, '[[WorldCat Identities]] (via VIAF): [https://www.worldcat.org/identities/containsVIAFID/'..viafId..' '..viafId..']', false ) )
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  
if namespace == 0 then  
worldcatCat = '[[Category:Wikipedia articles with WorldCat-VIAF identifiers]]'
worldcatCat = '[[Category:Wikipedia articles with WorldCat-VIAF identifiers]]'
Line 1,027: Line 1,185:
if lccnParts and lccnParts[1] ~= 'sh' then
if lccnParts and lccnParts[1] ~= 'sh' then
local lccnIdFmtd = lccnParts[1]..lccnParts[2]..'-'..lccnParts[3]
local lccnIdFmtd = lccnParts[1]..lccnParts[2]..'-'..lccnParts[3]
table.insert( elements, p.createRow( 'LCCN', '', lccnId, '[[WorldCat Identities]] (via LCCN): [https://www.worldcat.org/identities/lccn-'..lccnIdFmtd..' '..lccnIdFmtd..']', false ) )
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
if namespace == 0 then
worldcatCat = '[[Category:Wikipedia articles with WorldCat-LCCN identifiers]]'
worldcatCat = '[[Category:Wikipedia articles with WorldCat-LCCN identifiers]]'
Line 1,039: Line 1,197:
local Navbox = require('Module:Navbox')
local Navbox = require('Module:Navbox')
local elementsCat = ''
local elementsCat = ''
if rct >= 25 then
if rct == 0 or rct >= 25 then
local eCat = 'AC with '..rct..' elements'
local eCat = 'AC with '..rct..' elements'
elementsCat  = '[[Category:'..eCat..']]'..p.redCatLink(eCat)
elementsCat  = '[[Category:'..eCat..']]'..p.redCatLink(eCat)
Line 1,046: Line 1,204:
local outString = ''
local outString = ''
if #elements > 0 then
if #elements > 0 then
local args = {}
local args = { pid = 'identifiers' } -- #target the list of identifiers
if testcases and itemId then args = { qid = itemId } end --expensive
if testcases and itemId then args = { pid = 'identifiers', qid = itemId } end --expensive
local pencil = frame:expandTemplate{ title = 'EditAtWikidata', args = args}
local pencil = frame:expandTemplate{ title = 'EditAtWikidata', args = args}
outString = Navbox._navbox( {
outString = Navbox._navbox( {
Line 1,056: Line 1,214:
list1 = table.concat( elements )
list1 = table.concat( elements )
} )
} )
local auxCats = worldcatCat .. elementsCat .. suppressedIdCat .. deprecatedIdCat
end
if testcases then
auxCats = mw.ustring.gsub(auxCats, '(%[%[)(Category)', '%1:%2') --for easier checking
local auxCats = worldcatCat .. elementsCat .. multipleIdCat .. suppressedIdCat ..  
end
deprecatedIdCat .. differentOnWDCat .. sameOnWDCat
outString = outString .. auxCats
if testcases then
if namespace ~= 0 then
auxCats = mw.ustring.gsub(auxCats, '(%[%[)(Category)', '%1:%2') --for easier checking
outString = mw.ustring.gsub(outString, '(%[%[)(Category:Wikipedia articles)', '%1:%2') --by definition
end
end
outString = outString .. auxCats
if namespace ~= 0 then
outString = mw.ustring.gsub(outString, '(%[%[)(Category:Wikipedia articles)', '%1:%2') --by definition
end
end

Revision as of 13:39, 5 June 2021

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

require('Module:No globals')

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

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

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

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

function p.createRow( id, label, 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
		local totlen = 0
		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
			totlen = totlen + #rawValues[i] + 3 -- 3 chars b/w links
		end
		if totlen > 79 then
			row = string.gsub(row, '"nowrap"', '""') -- avoid [[A–Z Series]]
		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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

function p.cwgcLink( id )
	--P1908's format regex: [1-9]\d* (e.g. 75228351)
	if not id:match( '^[1-9]%d*$' ) then
		return false
	end
	return '[https://www.cwgc.org/find-war-dead/casualty/'..id..'/ '..id..']'..p.getCatForId( 'CWGC' )
end

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

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

function p.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 '[https://dib.cambridge.org/viewReadPage.do?articleId='..id..' '..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 '[http://www.uni-stuttgart.de/hi/gnt/dsi2/index.php?table_name=dsi&function=details&where_field=id&where_value='..id..' '..id..']'..p.getCatForId( 'DSI' )
end

function p.fastLink( id )
	--P2163's format regex: [1-9]\d{0,7} (e.g. 1916996)
	if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?$' ) then
		return false
	end
	return '[http://id.worldcat.org/fast/'..id..'/ '..id..']'..p.getCatForId( 'FAST' )
end

function p.fnzaLink( id )
	--P6792's format regex: [1-9]\d* (e.g. 9785)
	if not id:match( '^[1-9]%d*$' ) then
		return false
	end
	return '[https://findnzartists.org.nz/artist/'..id..'/ '..id..']'..p.getCatForId( 'FNZA' )
end

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

function p.hdsLink( id )
	--P902's format regex: \d{6} (e.g. 050123)
	if not id:match( '^%d%d%d%d%d%d$' ) then
		return false
	end
	return '[https://hls-dhs-dss.ch/fr/articles/'..id..' '..id..']'..p.getCatForId( 'HDS' )
end

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

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

function p.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 '[http://www.encyclopediaofukraine.com/display.asp?linkpath=pages\\'..id..' '..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 '[https://isni.org/isni/'..id..' '..id:sub( 1, 4 )..' '..id:sub( 5, 8 )..' '..id:sub( 9, 12 )..' '..id:sub( 13, 16 )..']'..p.getCatForId( 'ISNI' ) --no https as of 9/2019
end

function p.jocondeLink( id )
	--P347's format regex: [\-0-9A-Za-z]{11} (e.g. 12345678901)
	local regex = '^'..string.rep('[%-0-9A-Za-z]', 11)..'$'
	if not id:match( regex ) then
		return false
	end
	return '[https://www.pop.culture.gouv.fr/notice/joconde/'..id..' '..id..']'..p.getCatForId( 'Joconde' )
end

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

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

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

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

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

function p.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

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

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

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

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

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

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

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

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

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

function p.nlgLink( id )
	--P3348's format regex: [1-9]\d* (e.g. 1)
	if not id:match( '^[1-9]%d*$' ) then
		return false
	end
	return '[https://data.nlg.gr/resource/authority/record'..id..' '..id..']'..p.getCatForId( 'NLG' )
end

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

function p.nlkLink( id )
	--P5034's format regex: KA.(19|20).{7} (e.g. KAC201501465)
	if not id:match( '^KA.19.......$' ) and
	   not id:match( '^KA.20.......$' ) then
		return false
	end
	return '[https://nl.go.kr/authorities/resource/'..id..' '..id..']'..p.getCatForId( 'NLK' )
end

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

function p.nlrLink( id )
	--P1003's format regex: \d{9} (e.g. 123456789)
	if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
		return false
	end
	return '[http://aleph.bibnat.ro:8991/F/?func=direct&local_base=NLR10&doc_number='..id..']'..p.getCatForId( 'NLR' )
end

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

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

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

function p.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 '[http://mak.bn.org.pl/cgi-bin/KHW/makwww.exe?BM=1&NU=1&IM=4&WI='..id..' '..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 '[https://publons.com/author/'..id..'/ '..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 '[https://pic.nypl.org/constituents/'..id..' '..id..']'..p.getCatForId( 'PIC' )
end

function p.ridLink( id )
	--P1053's format regex: [A-Z]{1,3}-\d{4}-(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 '[https://www.researcherid.com/rid/'..id..' '..id..']'..p.getCatForId( 'RID' )
end

function p.rismLink( id )
	--P5504's format regex: (pe|ks)?\[1-9]d* (e.g. pe30006410)
	if not id:match( '^pe[1-9]%d*$' ) and --99% start with 'pe'
	   not id:match( '^ks[1-9]%d*$' ) and
	   not id:match( '^[1-9]%d*$' ) then
		return false
	end
	return '[https://opac.rism.info/search?id='..id..' '..id..']'..p.getCatForId( 'RISM' )
end

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

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

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

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

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

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

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

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

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

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

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

function p.tdviaLink( id )
	--P7314's format regex: [a-z/-]+] (e.g. barkan-omer-lutfi)
	if not id:match( '^[a-z/-]+$' ) then
		return false
	end
	return '[https://islamansiklopedisi.org.tr/'..id..' '..id..']'..p.getCatForId( 'TDVİA' )
end

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

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

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

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

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

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

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

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

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

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

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

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

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

--Validate ISNI (and ORCID) and retuns it as a 16 characters string or returns false if it's invalid
--See http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
function p.validateIsni( id )
	--P213 (ISNI) format regex: [0-9]{4} [0-9]{4} [0-9]{4} [0-9]{3}[0-9X] (e.g. 0000-0000-6653-4145)
	--P496 (ORCID) format regex: 0000-000(1-[5-9]|2-[0-9]|3-[0-4])\d{3}-\d{3}[\dX] (e.g. 0000-0002-7398-5483)
	id = id:gsub( '[ %-]', '' ):upper()
	if not id:match( '^%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d[%dX]$' ) then
		return false
	end
	if p.getIsniCheckDigit( id ) ~= string.char( id:byte( 16 ) ) then
		return false
	end
	return id
end

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

--[[==========================================================================]]
--[[                    Wikidata & documentation functions                    ]]
--[[==========================================================================]]

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

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

--[[==========================================================================]]
--[[                              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.

-- Parameter format: { 'parameter name', 'label', propertyId # in Wikidata, formatting/validation function }
p.conf = {
	{ 'AAG', '[[AAG (identifier)|AAG]]', 3372, p.aagLink },
	{ 'ACM-DL', '[[ACM DL (identifier)|ACM DL]]', 864, p.acmLink },
	{ 'ADB', '[[ADB (identifier)|ADB]]', 1907, p.adbLink },
	{ 'AGSA', '[[AGSA (identifier)|AGSA]]', 6804, p.agsaLink },
	{ 'autores.uy', '[[autores.uy (identifier)|autores.uy]]', 2558, p.autoresuyLink },
	{ 'AWR', '[[AWR (identifier)|AWR]]', 4186, p.awrLink },
	{ 'BALaT', '[[BALaT (identifier)|BALaT]]', 3293, p.balatLink },
	{ 'BIBSYS', '[[BIBSYS (identifier)|BIBSYS]]', 1015, p.bibsysLink },
	{ 'Bildindex', '[[Bildindex (identifier)|Bildindex]]', 2092, p.bildLink },
	{ 'BNC', '[[BNC (identifier)|BNC]]', 1890, p.bncLink },
	{ 'BNE', '[[BNE (identifier)|BNE]]', 950, p.bneLink },
	{ 'BNF', '[[BNF (identifier)|BNF]]', 268, p.bnfLink },
	{ 'Botanist', '[[Botanist (identifier)|Botanist]]', 428, p.botanistLink },
	{ 'BPN', '[[BPN (identifier)|BPN]]', 651, p.bpnLink },
	{ 'CANTIC', '[[CANTIC (identifier)|CANTIC]]', 1273, p.canticLink },
	{ 'CINII', '[[CiNii (identifier)|CiNii]]', 271, p.ciniiLink },
	{ 'CWGC', '[[CWGC (identifier)|CWGC]]', 1908, p.cwgcLink },
	{ 'DAAO', '[[DAAO (identifier)|DAAO]]', 1707, p.daaoLink },
	{ 'DBLP', '[[DBLP (identifier)|DBLP]]', 2456, p.dblpLink },
	{ 'DIB', '[[DIB (identifier)|DIB]]', 6829, p.dibLink },
	{ 'DSI', '[[DSI (identifier)|DSI]]', 2349, p.dsiLink },
	{ 'FAST', '[[FAST (identifier)|FAST]]', 2163, p.fastLink },
	{ 'FNZA', '[[FNZA (identifier)|FNZA]]', 6792, p.fnzaLink },
	{ 'GND', '[[GND (identifier)|GND]]', 227, p.gndLink },
	{ 'HDS', '[[HDS (identifier)|HDS]]', 902, p.hdsLink },
	{ 'IAAF', '[[IAAF (identifier)|IAAF]]', 1146, p.iaafLink },
	{ 'ICCU', '[[ICCU (identifier)|ICCU]]', 396, p.iccuLink }, --formerly SBN
	{ 'ICIA', '[[ICIA (identifier)|ICIA]]', 1736, p.iciaLink },
	{ 'IEU', '[[IEU (identifier)|IEU]]', 9070, p.ieuLink },
	{ 'ISNI', '[[ISNI (identifier)|ISNI]]', 213, p.isniLink },
	{ 'Joconde', '[[Joconde (identifier)|Joconde]]' , 347, p.jocondeLink },
	{ 'KULTURNAV', '[[KulturNav (identifier)|KulturNav]]', 1248, p.kulturnavLink },
	{ 'LCCN', '[[LCCN (identifier)|LCCN]]', 244, p.lccnLink },
	{ 'LIR', '[[LIR (identifier)|LIR]]', 886, p.lirLink },
	{ 'LNB', '[[LNB (identifier)|LNB]]', 1368, p.lnbLink },
	{ 'Léonore', '[[Léonore (identifier)|Léonore]]', 640, p.leonoreLink },
	{ 'MA', '[[MA (identifier)|MA]]', 6366, p.maLink },
	{ 'MBA', '[[MBA (identifier)|MBA]]', 434, p.mbaLink, category = 'MusicBrainz' }, --special cat name
	{ 'MBAREA', '[[MBAREA (identifier)|MBAREA]]', 982, p.mbareaLink, category = 'MusicBrainz area' }, --special cat name
	{ 'MBI', '[[MBI (identifier)|MBI]]', 1330, p.mbiLink, category = 'MusicBrainz instrument' }, --special cat name
	{ 'MBL', '[[MBL (identifier)|MBL]]', 966, p.mblLink, category = 'MusicBrainz label' }, --special cat name
	{ 'MBP', '[[MBP (identifier)|MBP]]', 1004, p.mbpLink, category = 'MusicBrainz place' }, --special cat name
	{ 'MBRG', '[[MBRG (identifier)|MBRG]]', 436, p.mbrgLink, category = 'MusicBrainz release group' }, --special cat name
	{ 'MBS', '[[MBS (identifier)|MBS]]', 1407, p.mbsLink, category = 'MusicBrainz series' }, --special cat name
	{ 'MBW', '[[MBW (identifier)|MBW]] work', 435, p.mbwLink, category = 'MusicBrainz work' }, --special cat 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 },
	{ 'PLWABN', '[[PLWABN (identifier)|PLWABN]]', 7293, p.plwabnLink },
	{ 'Publons', '[[Publons (identifier)|Publons]]', 3829, p.publonsLink },
	{ 'RERO', '[[RERO (identifier)|RERO]]', 3065, p.reroLink },
	{ 'RID', '[[RID (identifier)|ResearcherID]]', 1053, p.ridLink },
	{ 'RISM', '[[RISM (identifier)|RISM]]', 5504, p.rismLink },
	{ '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 cat name
	{ 'TA98', '[[TA98 (identifier)|TA98]]', 1323, p.ta98Link },
	{ 'TDVİA', '[[TDVİA (identifier)|TDVİA]]', 7314, p.tdviaLink },
	{ 'TE', '[[TE (identifier)|TE]]', 1693, p.teLink },
	{ 'TePapa', '[[TePapa (identifier)|TePapa]]', 3544, p.tepapaLink },
	{ 'TH', '[[TH (identifier)|TH]]', 1694, p.thLink },
	{ 'TLS', '[[TLS (identifier)|TLS]]', 1362, p.tlsLink },
	{ 'Trove', '[[Trove (identifier)|Trove]]', 1315, p.troveLink }, --formerly NLA-person
	{ 'UKPARL', '[[UKPARL (identifier)|UKPARL]]', 6213, p.ukparlLink },
	{ 'ULAN', '[[ULAN (identifier)|ULAN]]', 245, p.ulanLink },
	{ 'USCongress', '[[US Congress (identifier)|US Congress]]', 1157, p.uscongressLink },
	{ 'VcBA', '[[VcBA (identifier)|VcBA]]', 8034, p.vcbaLink },
	{ 'VIAF', '[[VIAF (identifier)|VIAF]]', 214, p.viafLink },
	{ 'WORLDCATID', '[[WorldCat Identities (identifier)|WorldCat Identities]]', 7859, nil },
}

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

-- Deprecated aliases to p.conf; tracked in [[Category:Wikipedia articles with deprecated authority control identifiers]]
-- Format: { 'deprecated parameter name', 'replacement parameter name in p.conf' }
p.deprecated = {
	{ 'GKD', 'GND' },
	{ 'PND', 'GND' },
	{ 'RLS', 'RSL' },	
	{ 'SWD', 'GND' },
	{ 'NARA-organization', 'NARA' },
	{ 'NARA-person', 'NARA' },
}

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

function p.authorityControl( frame )
	local resolveEntity = require( "Module:ResolveEntityId" )
	local parentArgs = frame:getParent().args --WD IDs added here later
	local iParentArgs = 0 --count original/manual parent args only later
	local elements = {} --create/insert rows later
	local worldcatCat = ''
	local multipleIdCat = ''
	local suppressedIdCat = ''
	local deprecatedIdCat = ''
	local differentOnWDCat = ''
	local sameOnWDCat = ''
	
	--Redirect aliases to proper parameter names
	for _, a in pairs( p.aliases ) do
		local alias, param = a[1], a[2]
		if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[alias] then
			parentArgs[param] = parentArgs[alias]
		end
	end
	
	--Redirect deprecated parameters to proper parameter names, and assign tracking cat
	for _, d in pairs( p.deprecated ) do
		local dep, param = d[1], d[2]
		if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[dep] then
			parentArgs[param] = parentArgs[dep]
			if namespace == 0 then
				deprecatedIdCat = '[[Category:Wikipedia articles with deprecated authority control identifiers|'..dep..']]'
			end
		end
	end
	
	--Use QID= parameter for testing/example purposes only
	local itemId = nil
	if namespace ~= 0 then
		local qid = parentArgs['qid'] or parentArgs['QID']
		if qid then
			itemId = 'Q'..mw.ustring.gsub(qid, '^[Qq]', '')
			itemId = resolveEntity._id(itemId) --nil if unresolvable
		end
	else
		itemId = mw.wikibase.getEntityIdForCurrentPage()
	end
	
	--Wikidata fallback if available
	if itemId then
		local iMatches = 0
		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 wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
					if wikidataIds[1] then
						if val == '' and (namespace == 0 or testcases) then
							suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|'..params[1]..']]'
						else
							parentArgs[params[1]] = wikidataIds[1] --add ID from WD
						end
					end
				else
					iParentArgs = iParentArgs + 1
					local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
					if wikidataIds[1] and differentOnWDCat == '' then
						local bMatch = false
						for _, wd in pairs( wikidataIds ) do
							if val == wd then
								iMatches = iMatches + 1
								bMatch = true
							end
						end
						if bMatch == false then
							differentOnWDCat = '[[Category:Pages using authority control with parameters different on Wikidata|'..params[1]..']]'
		end	end	end	end	end
		if iMatches > 0 and iMatches == iParentArgs then
			sameOnWDCat = '[[Category:Pages using authority control with parameters all matching Wikidata]]'
		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 = {}, {} --init tables
		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 (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 elementsCat = ''
	if rct == 0 or rct >= 25 then
		local eCat = 'AC with '..rct..' elements'
		elementsCat  = '[[Category:'..eCat..']]'..p.redCatLink(eCat)
	end
	
	local outString = ''
	if #elements > 0 then
		local args = { pid = 'identifiers' } -- #target the list of identifiers
		if testcases and itemId then args = { pid = 'identifiers', qid = itemId } end --expensive
		local pencil = frame:expandTemplate{ title = 'EditAtWikidata', args = args}
		outString = Navbox._navbox( {
			name  = 'Authority control',
			navboxclass = 'authority-control',
			bodyclass = 'hlist',
			group1 = '[[Help:Authority control|Authority control]]'..pencil,
			list1 = table.concat( elements )
			} )
	end
	
	local auxCats = worldcatCat .. elementsCat .. multipleIdCat .. suppressedIdCat .. 
					deprecatedIdCat .. differentOnWDCat .. sameOnWDCat
	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