# Module:Authority control

require('strict')  
local p = {}  
local configfile = 'Module:Authority control/config' -- default configuation module  
local arg = mw.getCurrentFrame.args.config  
if arg and arg~='' then  
configfile = 'Module:Authority control/config/' .. arg  
end  
local config  
if mw.title.new(configfile).exists then  
config = mw.loadData(configfile)  
else  
return error('Invalid configuration file',0)  
end  
local title = mw.title.getCurrentTitle  
local namespace = title.namespace  
local testcases = title.subpageText == config.i18n.testcases  

local function needsAttention(sortkey)  
return * .. sortkey .. title.text .. *  
end  

local function addCat(cat,sortkey)  
if cat and cat ~= '' and (namespace == 0 or namespace == 14 or testcases) then  
local redlinkcat = ''  
if testcases == false and mw.title.new(cat, 14).exists == false then  
redlinkcat = needsAttention('N')  
end  
if sortkey then  
cat = * .. sortkey .. title.text .. *  
else  
cat = * .. config.i18n.category .. ':'..cat..*  
end  
cat = cat .. redlinkcat  
return cat  
else  
return ''  
end  
end  

local function getCatForId(id,faulty)  
local cat = config.i18n.Articles_with .. ' '  
if faulty then cat = cat .. config.i18n.faulty .. ' ' end  
cat = cat .. id .. ' ' .. config.i18n.identifiers  
return addCat(cat)  
end  

local function getIdsFromWikidata(qid,property)  
local function getquals(statement,qualid)  
if statement.qualifiers and statement.qualifiers['P'..qualid] then  
return mw.wikibase.renderSnak(statement.qualifiers['P'..qualid][1])  
else  
return false  
end  
end  
local ids = {}  
if qid then  
for _, statement in ipairs(mw.wikibase.getBestStatements(qid,property)) do  
if statement.mainsnak.datavalue then  
local val = statement.mainsnak.datavalue.value  
if val then  
local namedas = getquals(statement,1810) or getquals(statement,742) or ''  
table.insert(ids,{id=val,name=namedas})  
end  
end  
end  
end  
return ids  
end  

local _makelink = function(conf,val,nextid,qid) --validate values and create a link  
local function tooltip(text,label)  
if label and label~='' then  
return mw.getCurrentFrame:expandTemplate{title = "Tooltip", args = {text,label}}  
else  
return text  
end  
end  
local link  
if nextid==1 then  
if conf.prefix then  
link = '*' .. conf.prefix .. '\n**'  
else  
link = '*'  
end  
else  
link = '\n**'  
end  
local valid_value = false  
if conf.customlink then -- use function to validate and generate link  
local label = nextid>1 and nextid  
local newlink= require(config.auxiliary)[conf.customlink](val.id,label)  
if newlink then  
link = link .. newlink  
valid_value = true  
end  
else  
if conf.pattern then -- use pattern to determine validity if defined  
valid_value = string.match(val.id,'^'..conf.pattern..'$')  
elseif conf.patterns then  
for _,pattern in ipairs(conf.patterns) do  
valid_value = val.id:match('^'..pattern..'$')  
if valid_value then break end  
end  
elseif conf.valid then -- otherwise use function to determine validity  
valid_value = require(config.auxiliary)[conf.valid](val.id)  
else -- no validation possible  
valid_value = val.id  
end  
if valid_value then  
local newlink  
local label = conf.label  
if not label or nextid>1 then  
label = tostring(nextid)  
end  
if conf.link then  
valid_value = valid_value:gsub('%%', '%%%%')  
newlink = '[' .. mw.ustring.gsub(conf.link,'%$1',valid_value) .. ' ' .. label .. ']'  
else  
newlink = valid_value  
end  
link = link .. '<span>'..tooltip(newlink,val.name)..'</span>'  
end  
end  
if valid_value then  
link = link .. getCatForId(conf.category or conf[1])  
else  
--local preview = require("Module:If preview")  
local wdlink = qid and ':wikidata:' .. qid .. '#P' .. conf.property .. * or *  
link = link .. ''  
if conf.errorcat then  
link = link .. addCat(conf.errorcat)  
else  
link = link .. getCatForId(conf.category or conf[1],true)  
end  
link = link .. addCat(config.i18n.allfaultycat,conf[1])-- .. preview._warning({'The '..conf[1]..' id '..val..' is not valid.'})  
end  
return link  
end  

--==========================================================================  
-- Main  
--==========================================================================  
function p.authorityControl  
local function resolveQID(qid)  
if qid then  
qid = 'Q'..mw.ustring.gsub(qid, '^[Qq]', '')  
if mw.wikibase.isValidEntityId(qid) and mw.wikibase.entityExists(qid) then  
local sitelink = mw.wikibase.getSitelink(qid)  
if sitelink then  
return mw.wikibase.getEntityIdForTitle(sitelink) or mw.wikibase.getEntity(qid).id  
end  
return mw.wikibase.getEntity(qid).id  
end  
end  
end  
local conf = config.config  
local parentArgs = :getParent.args  
local auxCats = ''  
local rct = false -- boolean to track if there are any links to be returned  
local qid,topic  
local wikilink = function(qid,hideifequal)  
local label,sitelink = mw.wikibase.getLabel(qid),mw.wikibase.getSitelink(qid)  
if label then  
if sitelink then  
local target = mw.title.new(sitelink)  
if target==title or (target.isRedirect and target.redirectTarget==title) then -- do not link  
return label  
else -- make wikilink to article  
return * .. label .. *  
end  
else  
return label  
end  
else  
auxCats = auxCats .. needsAttention('L')  
return qid  
end  
end  
if namespace == 0 then  
qid = mw.wikibase.getEntityIdForCurrentPage  
end  
if qid then -- article is connected to Wikidata item  
if parentArgs.qid and (resolveQID(parentArgs.qid) ~= qid) then -- non-matching qid parameter  
auxCats = auxCats .. needsAttention('D')  
end  
else -- page is not connected to any Wikidata item  
qid = resolveQID(parentArgs.qid) -- check qid parameter if no wikidata item is connected  
if qid then -- qid parameter is valid, set topic to display  
topic = mw.wikibase.getLabel(qid)  
if topic then  
if mw.ustring.lower(title.subpageText) == mw.ustring.lower(topic) then -- suppress topic display if subpagename equals topic up to case change  
topic = nil  
end  
if topic and mw.wikibase.getSitelink(qid) then -- make wikilink to article  
topic = * .. topic .. *  
end  
else  
auxCats = auxCats .. needsAttention('L')  
end  
elseif parentArgs.qid and parentArgs.qid~='' then -- invalid qid has been supplied, add to tracking cat  
auxCats = auxCats .. needsAttention('Q')  
end  
end  
local qids = {} -- setup any additional QIDs  
if parentArgs.additional=='auto' and qid then -- check P527 for parts to add additional qids  
local checkparts = function(property)  
local parts = mw.wikibase.getBestStatements(qid,property)  
if parts then  
for _,part in ipairs(parts) do  
if part.mainsnak.datavalue and part.mainsnak.datavalue.value.id then  
local resolvedqid = resolveQID(part.mainsnak.datavalue.value.id)  
if resolvedqid then  
table.insert(qids,resolvedqid)  
end end end end end  
for _,part in ipairs(config.auto_additional) do  
checkparts('P'..tostring(part))  
end  
elseif parentArgs.additional and parentArgs.additional ~= '' then  
for _,v in ipairs(mw.text.split(parentArgs.additional,"%s*,%s*")) do  
v = resolveQID(v)  
if v then  
if v == qid then -- duplicate of qid parameter  
auxCats = auxCats .. needsAttention('R')  
end  
table.insert(qids,v)  
else -- invalid QID specified  
auxCats = auxCats .. needsAttention('A')  
end  
end  
end  

local sections = {}  
local localparams = false  
local numsections = 0  
for _,_ in ipairs(config.sections) do numsections = numsections + 1 end  
for _ = 1,#qids+numsections do table.insert(sections,{}) end  
local qslink = '' -- setup link to add using QuickStatements  

-- check which identifiers to show/suppress in template  
local show = {} -- setup list  
local showall = true  
local function stripP(pid)  
if pid:match("^[Pp]%d+$") then  
pid = mw.ustring.gsub(pid,'[Pp]','') --strip P from property number  
end  
if pid:match("^%d+$") then  
return tonumber(pid)  
end  
end  
local function addshowlist(list)  
if list and list ~= '' then  
for _,v in ipairs(mw.text.split(string.lower(list),"%s*,%s*")) do  
local vprop = stripP(v)  
if vprop then -- e.g. show=P214 to show one particular property  
show[vprop] = true  
else -- e.g. show=arts to use whitelist  
if config.whitelists[v] then  
for _,w in ipairs(config.whitelists[v].properties) do  
show[w] = true  
end  
end  
end  
end  
showall = false  
end  
end  
addshowlist(.args.show) -- check show= parameter on wrapper template  
addshowlist(parentArgs.show or parentArgs.country) -- check show parameter on article template  
if parentArgs.suppress then  
local suppresslist = mw.text.split(parentArgs.suppress,"%s*,%s*") -- split parameter by comma  
for _,v in ipairs(suppresslist) do  
v = stripP(string.upper(v))  
if v then  
show[v] = false  
auxCats = auxCats .. * .. config.i18n.category .. ':' .. config.i18n.suppressedcat .. *  
else  
auxCats = auxCats .. needsAttention('P')  
end  
end  
end  

local function makeSections(qid,addit)  
local tval = {}  
local function parameter_is_used(property)  
local used = false  
if property then  
if tval[property] then  
if tval[property][1] then  
used = true  
end  
elseif tval[property] == false then -- property has been manually suppressed  
used = true  
end  
end  
return used  
end  
for _, params in ipairs(conf) do  
tval[params.property] = getIdsFromWikidata(qid, 'P' .. params.property) -- setup table for values with property number as key  
local showb = true  
if (show[params.property] == nil) and (show[string.upper(params[1])] == nil ) then  
showb = showall -- if not specified then depends on showall  
elseif (show[params.property] == false) or (show[string.upper(params[1])] == false) then -- if either are false then id will be suppressed  
showb = false  
end  
if not showb then  
tval[params.property] = false -- indicates the identifier is suppressed  
elseif not addit then  
local val = parentArgs[mw.ustring.lower(params[1])] or parentArgs[params[1]]  
if val and val~='' then -- add local parameter to list if not already in  
localparams = true  
local bnew = true  
for _, w in pairs(tval[params.property]) do  
if val == w.id then  
bnew = false  
end  
end  
if bnew then -- add new value to table  
if qid then  
qslink = qslink .. '%7C%7C' .. qid .. '%7CP' .. params.property .. '%7C%22' .. mw.uri.encode(val,"PATH") .. '%22%7CS143%7CQ328'  
end  
table.insert(tval[params.property],{id=val,name=''})  
end  
end  
end  
local suppress = false  
if params.suppressedbyproperty then  
for _,sc in ipairs(params.suppressedbyproperty) do  
if parameter_is_used(sc) then  
suppress = true  
end  
end  
end  
if tval[params.property] ~= false and not suppress then  
local tlinks = {} -- setup table for links  
local nextIdVal = 1  
local row = ''  
for _,val in ipairs(tval[params.property]) do  
local link = _makelink(params,val,nextIdVal,qid)  
row = row .. link  
table.insert(tlinks,link)  
nextIdVal = nextIdVal + 1  
end  
if nextIdVal>=2 then  
row = row .. '\n'  
table.insert(sections[addit or params.section],row)  
rct = true  
end  
end  
end  
end  
local function pencil(qid)  
if not qid then  
return ''  
end  
local args = { pid = 'identifiers' } -- #target the list of identifiers  
args.qid = qid  
return require('Module:EditAtWikidata')._showMessage(args)  
end  

makeSections(qid,false)  
for c = 1,#qids do  
makeSections(qids[c],numsections+c)  
end  

--configure Navbox  
local outString = ''  
if rct or localparams then -- there is at least one link to display  
local Navbox = require('Module:Navbox')  
local sect,lastsect = 0,0  
local navboxArgs = {  
name = 'Authority control',  
navbox,  
body,  
state = parentArgs.state or config.i18n.autocollapse,  
navbar = 'off'  
}  
for c=1,numsections+#qids do  
if #sections[c] ~= 0 then -- section is non-empty  
sect = sect + 1  
lastsect = c  
local sectname  
if c <= numsections then -- regular section  
sectname = config.sections[c].name  
else -- section from additional qid  
local qid = qids[c-numsections]  
sectname = wikilink(qid) .. pencil(qid)  
end  
navboxArgs['group' .. c] = sectname  
navboxArgs['list' .. c] = table.concat(sections[c])  
end  
end  
if localparams then  
lastsect = lastsect + 1  
sect = sect + 1  
navboxArgs['group' .. lastsect] = config.i18n.warning  
local warning = :expandTemplate{title = config.i18n.errortemplate, args = {config.i18n.localparams}}  
if qslink ~= '' then  
warning = warning .. ' ' .. config.i18n.movetowd .. '<span>&#32;</span>'  
elseif not qid then  
if namespace == 0 then  
warning = warning .. ' ' .. config.i18n.connecttowd  
elseif namespace==14 or namespace==2 or namespace==118 then  
warning = warning .. ' ' .. config.i18n.qidcode  
end  
end  
navboxArgs['list' .. lastsect] = warning  
end  
if topic then -- display in expanded form with topic  
navboxArgs.title = config.i18n.aclink .. ' &ndash; ' .. topic .. pencil(qid)  
elseif sect == 1 then -- special display when only one section  
if lastsect <= numsections then  
if config.sections[lastsect].hidelabelwhenalone then -- no special label when only general or other IDs are present  
navboxArgs['group' .. lastsect] = config.i18n.aclink .. pencil(qid)  
else -- other regular section  
navboxArgs['group' .. lastsect] = config.i18n.aclink .. ': ' .. navboxArgs['group' .. lastsect] .. pencil(qid)  
end  
else -- section from additional qid  
navboxArgs['group' .. lastsect] = config.i18n.aclink .. ': ' .. navboxArgs['group' .. lastsect]  
end  
else -- add title to navbox  
navboxArgs.title = config.i18n.aclink .. pencil(qid)  
end  
outString = Navbox._navbox(navboxArgs)  
end  

if parentArgs.state  
and parentArgs.state~=''  
and parentArgs.state~=config.i18n.collapsed  
and parentArgs.state~=config.i18n.expanded  
and parentArgs.state~=config.i18n.autocollapse then --invalid state parameter  
auxCats = auxCats .. needsAttention('S')  
end  

if testcases then  
auxCats = mw.ustring.gsub(auxCats, '(%[%[)(' .. config.i18n.category .. ')', '%1:%2') --for easier checking  
end  

--out  
outString = outString..auxCats  
if namespace ~= 0 then  
outString = mw.ustring.gsub(outString,'(%[%[)(' .. config.i18n.category .. ':' .. config.i18n.Articles .. ')([^%|%]]+)%|?[^%|%]]*(%]%])','%1:%2%3%4')  
outString = mw.ustring.gsub(outString,'(%[%[)(' .. config.i18n.category .. ':' .. config.i18n.All_articles .. ')([^%|%]]+)%|?[^%|%]]*(%]%])','%1:%2%3%4')  
end  
local check = require('Module:Check for unknown parameters')._check  
local sortkey  
if namespace == 0 then  
sortkey = '*' .. title.text  
else  
sortkey = title.fullText  
end  
outString = outString .. check({  
['unknown'] = * .. sortkey .. *,  
['preview'] = config.i18n.previewwarning, 'show', 'country', 'suppress', 'additional', 'qid', 'state'  
}, parentArgs)  
return outString  
end  

p.makelink = function(conf,val,nextid,qid)  
return _makelink(conf,val,nextid,qid)  
end  

return p