Module:Authority control: Difference between revisions

From Sidlangs
Jump to navigation Jump to search
(get example id from wikidata rather than using locally defined, add function to produce table of errors, adjust tracking categories)
 
m (1 revision imported: This: work pls)
(No difference)

Revision as of 18:05, 5 February 2023

{{#ifeq:Authority control|doc|{{#if:|Template:Pp}}|{{#switch:

 {{#if:
 |     
 | {{#ifeq:Module|Module
   | module
   | other
   }}
 }}
| module =

| other | #default = Template:Error }}}}

Lua error in Module:Lua_banner at line 112: attempt to index field 'edit' (a nil value). This module contains the code for the {{Authority control}} template.

Please see Template:Authority control/doc.

{{#if:{{#ifeq:Authori|sandbox|1}}{{#ifeq:Authority control|doc|1}}|| }}


require('strict')
local p = {}
local title = mw.title.getCurrentTitle()
local namespace = title.namespace
local testcases = (string.sub(title.subpageText,1,9) == 'testcases')
local config = require("Module:Authority control/config")

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 = '[[Category:Pages with red-linked authority control categories]]'
		end
		if sortkey then
			cat = '[[Category:'..cat..'|' .. sortkey .. title.text .. ']]'
		else
			cat = '[[Category:'..cat..']]'
		end
		cat = cat .. redlinkcat
		return cat
	else
		return ''
	end
end

local function getCatForId(id,faulty)
	local cat = 'Articles with '
	if faulty then cat = cat .. 'faulty ' end
	cat = cat .. id .. ' identifiers'
	return addCat(cat)
end

local function getIdsFromWikidata(qid,property)
	local ids = {}
	if not mw.wikibase or not qid then
		return ids
	end
	local statements = mw.wikibase.getBestStatements(qid,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

local function makelink(conf,val,nextid,qid) --validate values and create a link
	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.link2 then -- use function to validate and generate link
		if conf.link2(val) then
			link = link .. conf.link2(val)
			valid_value = true
		end
	else
		if conf.pattern then -- use pattern to determine validity if defined
			valid_value = val:match(conf.pattern)
		elseif conf.patterns then
			for i = 1,#conf.patterns do
				valid_value = val:match(conf.patterns[i])
				if valid_value then break end
			end
		elseif conf.valid then -- otherwise use function to determine validity
			valid_value = conf.valid(val)
		else -- no validation possible
			valid_value = val
		end
		if valid_value then
			link = link .. '<span class="uid">'
			if not conf.label or nextid>1 then
				conf.label = tostring(nextid)
			end
			if conf.link then
				valid_value = valid_value:gsub('%%', '%%%%')
				link = link .. '[' .. mw.ustring.gsub(conf.link,'%$1',valid_value) .. ' ' .. conf.label .. ']'
			else
				link = link .. valid_value
			end
			link = link .. '</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 .. '[[File:345-409 Ambox warning centered.svg|20px|frameless|link=' .. wdlink ..'|The '..conf[1]..' id '..val..' is not valid.]]'
		if conf.errorcat then
			link = link .. addCat(conf.errorcat)
		else
			link = link .. getCatForId(conf.category or conf[1],true)
		end
		link = link .. addCat('All articles with faulty authority control information',conf[1])-- .. preview._warning({'The '..conf[1]..' id '..val..' is not valid.'})
	end
	return link
end

--[[==========================================================================]]
--[[                                   Main                                   ]]
--[[==========================================================================]]
function p.authorityControl(frame)
	local resolveEntity = require('Module:ResolveEntityId')
	local function resolveQID(qid)
		if qid then
			qid = 'Q'..mw.ustring.gsub(qid, '^[Qq]', '')
			qid = resolveEntity._id(qid) --nil if unresolvable
		end
		return qid
	end
	local conf = config.config
	local parentArgs = frame:getParent().args
	local iParentArgs = 0 --count original/manual parent args
	local iMatches = 0
	local auxCats = ''
	local rct = 0 -- total number of links returned
	local qid
	if namespace == 0 then
		qid = mw.wikibase.getEntityIdForCurrentPage()
	end
	if not qid then
		qid = resolveQID(parentArgs['qid']) --use qid parameter if no wikidata item is connected
	end
	local qids = {} -- setup any additional QIDs
	if parentArgs.additional and parentArgs.additional ~= '' then
		for _,v in ipairs(mw.text.split(parentArgs.additional,"%s*,%s*")) do
			table.insert(qids,resolveQID(v))
		end
	end

	local sections = {}
	for _ = 1,#config.sectionNames + #qids 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 addshowlist(list)
		if list and list ~= '' then
			for _,v in ipairs(mw.text.split(string.lower(list),"%s*,%s*")) do
				if config.whitelists[v] then
					for _,w in ipairs(config.whitelists[v].properties) do
						show[w] = true
					end
				end
			end
			showall = false
		end
	end
	addshowlist(frame.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
			if v:match("^[Pp]%d+$") then
				v = mw.ustring.gsub(v,'[Pp]','') --strip P from property number
			end
			if v:match("^%d+$") then
				v = tonumber(v)
			else
				v = string.upper(v) -- convert to uppercase
			end
			show[v] = false
		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 addit then
				if not showb then
					tval[params.property] = false -- indicates the identifier is suppressed
				end
			else
				local val = parentArgs[mw.ustring.lower(params[1])] or parentArgs[params[1]]
				if not showb or val == '' then
					tval[params.property] = false -- indicates the identifier is suppressed
					auxCats = auxCats .. '[[Category:Articles with suppressed authority control identifiers|'..params[1]..']]'
				elseif val then -- add local parameter to list if not already in 
					iParentArgs = iParentArgs + 1
					local bnew = true
					for _, w in pairs(tval[params.property]) do
						if val == w 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],val)
					else
						iMatches = iMatches+1
					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 = rct + 1
				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],#config.sectionNames+c)
	end

	--configure Navbox
	local outString = ''
	if rct > 0 then -- there is at least one link to display
		local Navbox = require('Module:Navbox')
		local sect,lastsect = 0,0
		local navboxArgs = {
			name  = 'Authority control',
			navboxclass = 'authority-control',
			bodyclass = 'hlist',
			state = parentArgs.state or 'autocollapse',
			navbar = 'off'
		}
		for c=1,#config.sectionNames+#qids do
			if #sections[c] ~= 0 then -- section is non-empty
				sect = sect + 1
				lastsect = c
				local sectname
				if c <= #config.sectionNames then -- regular section
					sectname = config.sectionNames[c]
				else -- section from additional qid
					sectname = mw.wikibase.getLabel(qids[c-#config.sectionNames]) .. pencil(qids[c-#config.sectionNames])
				end
				navboxArgs['group' .. c] = sectname
				navboxArgs['list' .. c] = table.concat(sections[c])
			end
		end
		local aclink = '[[Help:Authority control|Authority control]]'
		if qslink ~= '' then
			qslink = '<span class="qs autoconfirmed-show">&#32;[[File:Commons to Wikidata QuickStatements.svg|20px|link=https://quickstatements.toolforge.org/#/v1=' .. qslink .. '|Add values to Wikidata.]]</span>'
		end
		if sect == 1 then -- special display when only one section
			if lastsect == 1 or lastsect == 8 then -- no special label when only general or other IDs are present
				navboxArgs['group' .. lastsect] = aclink .. pencil(qid) .. qslink
			elseif lastsect <= #config.sectionNames then -- other regular section
				navboxArgs['group' .. lastsect] = aclink .. ': ' .. config.sectionNames[lastsect] .. pencil(qid) .. qslink
			else -- section from additional qid
				navboxArgs['group' .. lastsect] = aclink .. ': ' .. navboxArgs['group' .. lastsect] .. qslink
			end
		else -- add title to navbox
			navboxArgs.title = aclink .. pencil(qid) .. qslink
		end
		outString = Navbox._navbox(navboxArgs)
	end

	if parentArgs.state then
		if namespace == 0 or testcases then
			local sCat
			if parentArgs.state == 'collapsed' then sCat = 'AC using state parameter: collapsed'
			elseif parentArgs.state == 'expanded' then sCat = 'AC using state parameter: expanded'
			elseif parentArgs.state == 'autocollapse' then sCat = 'AC using state parameter: autocollapse'
			else sCat = 'AC using state parameter: other'
			end
			auxCats = auxCats .. addCat(sCat)
		end
	end
	if testcases then
		auxCats = mw.ustring.gsub(auxCats, '(%[%[)(Category)', '%1:%2') --for easier checking
	end

	--out
	outString = outString..auxCats
	if namespace ~= 0 then
		outString = mw.ustring.gsub(outString,'(%[%[)(Category:Articles)([^%|%]]+)%|?[^%|%]]*(%]%])','%1:%2%3%4')
		outString = mw.ustring.gsub(outString,'(%[%[)(Category: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
	local tracking = check({
		['unknown']='[[Category:Pages using authority control with parameters|' .. sortkey .. ']]',
		['preview']='Page using [[Template:Authority control]] with "_VALUE_", please move this to Wikidata.',
		'show', 'country', 'suppress', 'additional', 'qid', 'state'
		}, parentArgs)
	if namespace == 0 -- mainspace
		or namespace == 14 -- category
		or namespace == 2 -- user
		or namespace == 118 then -- draft
		outString = outString .. tracking
	end
	return outString
end

-- Creates a human-readable standalone wikitable version of conf, and tracking categories with page counts, for use in the documentation
function p.docConfTable(frame)
	local wikiTable = '<table class="wikitable sortable">'..
	  '<tr><th>Code</th>'..
	  '<th>Identifier</th>'..
	  '<th data-sort-type=number>Wikidata property</th>'..
	  '<th>Section</th>'..
	  '<th>Appears as</th>'..
	  '<th>[[:Category:Articles with authority control information|Articles]] ([[:Category:Articles with faulty authority control information|Faults]])</th></tr>'
	local columns = 6
	local lang = mw.getContentLanguage()
	local a, P, f = 0, 0, 0 --cumulative sums
	local getlink = require("Module:Wikidata table")._getLink
	local function checkcat(category,label)
		local ret='[[:Category:'..category..'|'..label..']]'
		if mw.title.new(category,14).exists == false then
			ret = ret..' <span class="plainlinks" style="font-size:85%;">&#91;['..tostring(mw.uri.fullUrl('Category:'..category,'action=edit&preload=Template:Authority_control/preload'))..' create]&#93;</span>'
		end
		return ret
	end
	for _, conf in pairs(config.config) do
		local category = conf.category or conf[1]
		local articleCat = 'Articles with '..category..' identifiers'
		local articleCount = mw.site.stats.pagesInCategory(articleCat,'pages')
		local errorCat = conf.errorcat or 'Articles with faulty '..(conf.category or conf[1])..' identifiers'
		local errorCount =  mw.site.stats.pagesInCategory(errorCat,'pages')
		P = P + 1 --property count
		a = a + articleCount
		f = f + errorCount
		local example = mw.wikibase.getBestStatements('P'..conf.property,'P1855') or ''
		if example then
			example = example[1].qualifiers['P'..conf.property][1].datavalue.value
			example = mw.getCurrentFrame():expandTemplate{title = "Hlist", args = {'\n' .. makelink(conf,example,1)}} .. '\n'
		end
		local name = mw.wikibase.getBestStatements('P'..conf.property,"P1629")
		if name then
			if name[1] then
				name = name[1].mainsnak.datavalue.value.id
				if name then
					name = getlink(name)
				end
			else
				name = false
			end
		end
		if conf.remark then
			wikiTable = wikiTable..'<tr><td rowspan=2>'
		else
			wikiTable = wikiTable..'<tr><td>'
		end
		wikiTable = wikiTable..'[['..(conf.idlink or conf[1]..' (identifier)')..'|'..conf[1]..']]</td>'
		wikiTable = wikiTable..'<td>'..(name or '')..'</td>'..
			'<td data-sort-value='..conf.property..'>'..frame:expandTemplate{title='Wikidata property link',args={id='f',conf.property}}..'</td>'..
			'<td>'..config.sectionNames[conf.section]..'</td>'..
			'<td>'..example..'</td>'..
			'<td style="text-align: right;">'..checkcat(articleCat,lang:formatNum(articleCount))..' ('..checkcat(errorCat,errorCount)..')</td></tr>'
		if conf.remark then
			wikiTable = wikiTable.."<tr><td colspan=" .. columns-1 .. ">'''Remarks:''' "..conf.remark.."</td></tr>"
		end
	end
	wikiTable = wikiTable..'<tr><th style="text-align: right;" colspan=' .. columns-2 .. '>Totals</th>'..
		'<th style="text-align: right;">'..P..'</th>'..
		'<th style="text-align: right;">'..lang:formatNum(a)..' ([[:Category:All articles with faulty authority control information|'..f..']])</th></tr></table>'
	return require('Module:Suppress categories').main(wikiTable)
end

function p.errorTable(frame)
	local Table = '<table class="wikitable sortable">'..
	  '<tr><th>Code</th>'..
	  '<th>Wikidata property</th>'..
	  '<th>Faulty IDs</th>'..
	  '<th>[[:Category:Articles with faulty authority control information|Tracking category]]</th></tr>'
	local f, P = 0, 0, 0 --cumulative sums
	for _, conf in pairs(config.config) do
		local category = conf.errorcat or 'Articles with faulty '..(conf.category or conf[1])..' identifiers'
		local count =  mw.site.stats.pagesInCategory(category,'pages')
		if count > 0 then
			P = P + 1
			f = f + count
			Table = Table..'<tr><th>[['..(conf.idlink or conf[1]..' (identifier)')..'|'..conf[1]..']]</th>'..
				'<td>'..frame:expandTemplate{ title = 'Wikidata property link', args = { id = 'f', conf.property } } .. '</td>'..
				'<td style="text-align:center;">'..tostring(count)..'</td>'..
				'<td>[[:Category:'..category..']]</td></tr>'
		end
	end
	Table = Table..'<tr><th colspan=2 style="text-align: right;">Totals</th>'..
		'<th style="text-align:center;">' .. '[[:Category:All articles with faulty authority control information|' .. tostring(f) .. ']]</th>'..
		'<th style="text-align:center;">'..tostring(P)..'</th></tr></table>'
	return Table
end

function p.whitelisttable(frame)
	local Table = '<table class="wikitable sortable">'..
	  '<tr><th>Code</th>'..
	  '<th>Topic</th>'..
	  '<th>Identifiers</th></tr>'
	for code, wlist in pairs(config.whitelists) do
		Table = Table .. '<tr><th>' .. code .. '</th>'..
			'<td>[[' .. mw.wikibase.getSitelink('Q' .. wlist.topic) .. ']]</td>'
		local plist = {}
		for _, property in pairs(wlist.properties) do
			table.insert(plist,frame:expandTemplate{title='Wikidata property link', args={'P' .. property}})
		end
		Table = Table .. '<td>' .. table.concat(plist,', ') .. '</td></tr>'
	end
	Table = Table .. '</table>'
	return Table
end

-- Main/External Call for Pages with authority control identifiers
function p.autoDetect( frame )
	local function whichTOC( frame ) -- standardize TOC behavior via {{CatAutoTOC}}
		return frame:expandTemplate{ title = 'CatAutoTOC', args = { align = 'center' } }
	end
	local ac_conf = require('Module:Authority control/config').config
	local rmCats = require('Module:Suppress categories').main
	--For use in [[Category:Articles with faulty authority control information]], i.e. on [[Category:Articles with faulty VIAF identifiers]]
	local function wpfaulty( frame, id )
		for _, conf in pairs(ac_conf) do
			if conf.category == id or conf[1] == id then
				local outString = frame:expandTemplate{ title = 'Cat more', args = {'Wikipedia:Authority control', conf.idlink or conf[1]..' (identifier)', ':d:Property:P'..conf.property} }
					.. frame:expandTemplate{ title = 'Possibly empty category' }
					.. frame:expandTemplate{ title = 'Wikipedia category', args = { hidden = 'yes', tracking = 'yes' } }
					.. frame:expandTemplate{ title = 'Polluted category' }
					.. whichTOC( frame )
					.. '\nPages in this category should only be added by [[Module:Authority control]].'
					.. addCat('Articles with '..id..' identifiers')
					.. addCat('Articles with faulty authority control information',id)
				return outString
			end
		end
		return ''
	end
	--For use in [[Category:Articles with authority control information]], i.e. on [[Category:Articles with VIAF identifiers]]
	local function wp(frame,id )
		for _, conf in pairs( ac_conf ) do
			if conf.category == id or conf[1] == id then
				local link = '[[' .. (conf.idlink or conf[1] .. ' (identifier)') .. '|' .. conf[1] .. ']]'
				local outString = frame:expandTemplate{ title = 'Category explanation', args = {'articles with '..link..' identifiers.'..' Please do not add [[Wikipedia:Categorization#Subcategorization|subcategories]].'} }
					.. frame:expandTemplate{ title = 'Cat more', args = {'Wikipedia:Authority control', ':d:Property:P'..conf.property} }
					.. frame:expandTemplate{ title = 'Possibly empty category' }
					.. frame:expandTemplate{ title = 'Wikipedia category', args = { hidden = 'yes', tracking = 'yes' } }
					.. whichTOC( frame )
					.. '\nPages in this category should only be added by [[Module:Authority control]].'
					.. addCat('Articles with authority control information',id)
				return outString
			end
		end
		return ''
	end
	if namespace == 14 then --cat space
		local wpfaultyID = mw.ustring.match(title.text, 'Articles with faulty ([%w%.%- ]+) identifiers')
		local wpID       = mw.ustring.match(title.text, 'Articles with ([%w%.%- ]+) identifiers')
		if wpfaultyID then
			return wpfaulty(frame,wpfaultyID)-- must be before wpID check, in case they both match
		elseif wpID then
			return wp(frame, wpID)-- to keep the regex simple
		else
			return '[[Category:Pages with authority control identifiers unknown category]]'
		end
	end
	return ''
end

return p