Module:Artwork

--[[   __  __           _       _           _         _                      _     |  \/  | ___   __| |_   _| | ___ _   / \   _ __| |___      _____  _ __| | __ | |\/| |/ _ \ / _` | | | | |/ _ (_) / _ \ | '__| __\ \ /\ / / _ \| '__| |/ / | |  | | (_) | (_| | |_| | |  __/_ / ___ \| |  | |_ \ V  V / (_) | |  |   <  |_|  |_|\___/ \__,_|\__,_|_|\___(_)_/   \_\_|   \__| \_/\_/ \___/|_|  |_|\_\ This module is intended to be the engine behind "Template:Artwork", "Template:Art photo", "Template:Photograph" and "Template:Book".

Please do not modify this code without applying the changes first at "Module:Artwork/sandbox" and testing at "Template:Artwork/testcases".

Authors and maintainers: ]] require('strict') -- used for debugging purposes as it detects cases of unintended global variables local getLabel        = require("Module:Wikidata label")._getLabel            -- used for creation of name based on Wikidata local getSitelinks    = require("Module:Wikidata label")._sitelinks           -- local getDate         = require("Module:Wikidata date")._date                 -- used for processing of date properties local authorityControl = require("Module:Authority control")._authorityControl -- used for formatting of Authority control row local ISOdate         = require('Module:ISOdate')                             -- used for simple date formating local Size            = require('Module:Size')._size                          -- Lua code behind  template local TitleFromWD     = require('Module:Title').wikidata_title                -- Lua code behind  template local Art             = require('Module:Wikidata art')                        -- local alterName       = require("Module:Name")._name local TagQS           = require('Module:TagQS') local core            = require("Module:Core") local artcore         = require("Module:Artwork/core")
 * User:Jarekt - original version

-- Lazy loading functions: load them only if they are needed local function Information(args) return require("Module:Information")._information(args) end

local function ObjectTypeID(object_type) local LUT = mw.loadData('Module:I18n/objects/data') -- lazy loading (only if needed	return LUT[string.lower(object_type)] end

local function periodSpan(id, lang) local PeriodSpan = require('Module:Period')._periodSpan return PeriodSpan(id, lang) end

-- ================================================== -- === Internal functions =========================== -- ==================================================

--- local function empty2nil(str) if str=='' then return nil else return str end end

--- local function nowiki(str) -- remove all the links if not str then return str end str = TagQS.removeTag(str, nil) str = mw.ustring.gsub(str, '%', '') str = mw.ustring.gsub(str, "<[^>]*>", "")   -- remove all html tags from str str = mw.ustring.gsub(str, "'''", "")       -- remove bold str = mw.ustring.gsub(str, "''", "")        -- remove italics str = mw.ustring.gsub(str, "%[%Ff]ile:[^%+%]%]", "") -- remove file icons str = mw.ustring.gsub(str, "%[%", "") -- remove piped links, like "[[:en:test|"	str = mw.ustring.gsub(str, "%[[hH][tT][tT][pP][sS]?://[^ ]+ ([^%*)%]", "%1")  -- remove URL links, like "xxxx -> xxxx"	str = mw.ustring.gsub(str, "%[%[", "" )      -- remove piped links, like ""	str = mw.ustring.gsub(str, "%]%]", "" )      -- remove piped links, like ""	return str end

--- local function getProperty(entity, prop) return (core.parseStatements(entity:getBestStatements( prop ), nil) or {nil})[1] end

--- local function getBestProperties(entity, prop) return core.parseStatements(entity:getBestStatements( prop ), nil) end

--- local function quote(str) return '"' .. str .. '"' end

--- local function if_else(Boolean, TrueStatement, FalseStatement) if Boolean then return TrueStatement else return FalseStatement end end

--- local function add_QS(qsTable, prop, value, qualifiers) local qsStr = prop .. '|' .. value if qualifiers then for qual, val in pairs(qualifiers) do qsStr = qsStr .. '|' .. qual .. '|' .. val end end table.insert( qsTable, qsStr) end

--- local function render_QS_URL(qsTable, item, withIcon) local today = '+' .. os.date('!%F') .. 'T00:00:00Z/11' -- today's date in QS format local url = mw.title.getCurrentTitle:canonicalUrl -- URL to current page local ref = '|S143|Q565|S813|' .. today .. '|S4656|"' .. mw.uri.decode(url) .. '"' local icon = 'File:Commons_to_Wikidata_QuickStatements.svg' for k, qs in ipairs(qsTable) do		if qs:sub(1,1)=='P' then qsTable[k] = item .. '|' .. qs .. ref else qsTable[k] = item .. '|' .. qs -- for labels, etc.		end end local qsURL = table.concat(qsTable, '||') local hoverMsg = 'Add properties to Wikidata item based on this file' if string.upper(item)=='CREATE' then qsURL = 'CREATE||' .. string.gsub(qsURL, item..'|', 'LAST|') icon = 'File:Plus Wikidata.svg' hoverMsg = 'Create new Wikidata item based on this file' end qsURL = mw.uri.encode(qsURL, "PATH") qsURL = 'https://quickstatements.toolforge.org/#/v1=' .. qsURL   -- create full URL link withIcon = withIcon or true -- define default if withIcon then -- use URL as a link accessed by clicking an icon qsURL = ' 15px|link=' .. qsURL .. '|' .. hoverMsg ..'' end return qsURL end

-- =========================================================================== -- === This function is responsible for adding maintenance categories     === -- === which are not related to Wikidata                                  === -- === INPUTS:                                                            === -- === * args0  - data from the local arguments                           === -- === * args   - merged data from the local arguments and Wikidata       === -- =========================================================================== local function add_maintenance_categories(args0, args) local cats = '' -- categories mw.getCurrentFrame:expandTemplate{ title = 'Infobox template tag' } -- add the template tag

-- ====================================================	-- === automatic tagging of pages in all namespaces === -- ====================================================	if args.date or args.year then -- add an empty template which can be used as a tag in PetScan local d   = os.date('!*t')                   -- current date table local current_year = tonumber(d.year)        -- current year local creation_year = tonumber(ISOdate._ISOyear(args.year or args.date)) if creation_year and current_year and (current_year-creation_year)>200 then mw.getCurrentFrame:expandTemplate{ title ='Works created more than 200 years ago' } end end if args0.namespace==0 and mw.ustring.sub(args0.pagename,1,8) == "Artwork:" then cats = cats .. '\n' if args.homecat then cats = cats .. '\n' end end -- add categories related to accession number for artworks if args0.id and args0.infobox=='artwork' then local sortkey = nowiki(args0.id) -- strip any links that might be there if #sortkey>30 then sortkey = 'zzz' end cats = string.format('%s\n', cats, sortkey) end

-- add categories related to template:book transcluded into template for specific book if args0.infobox=='book' and args0.namespace==10 then cats = cats .. '\n' -- add homecat category if args0.homecat~='~' then local page = {} if args0.homecat then cats = string.format('%s\n',cats, args0.homecat) page = mw.title.new( args0.homecat, 'category' ) end if not page or not page.exists or not args0.homecat then cats = cats .. '\n' end end end return cats end

-- =========================================================================== -- === This function is responsible for creating QuickStatements          === -- === to pages in creator namespace which are related to wikidata        === -- === INPUTS:                                                            === -- === * args0 - local inputs from the Artwork template                   === -- === * data  - data pulled from Wikidata                                === -- =========================================================================== local function create_QuickStatements(args0, data) -- setup QuickStatements local qsTable = {} -- table to store QuickStatements local item = args0.wikidata if args0.no_qs then return nil -- "no_qs" flag means we do not want QuickStatements based on this file end

-- 	if not data.id and args0.id then local id = nowiki(args0.id) -- strip any links that might be there if args0.institution_id and #id<20 then add_QS(qsTable, 'P217', quote(id), {P195=args0.institution_id}) end end -- add QS based on "image" field local lut = { jpg='P18', jpeg='P18', png='P18', pdf='P996', djvu='P996', stl='P4896'} --			 webm='P10', ogv='P10', mp3='P51', wav='P51' -- ignore other extensions as they are mostly wrong if (args0.image) then -- QS code to help transfer content of "image" field to Wikidata local ext = mw.ustring.lower(mw.ustring.match(args0.image, "^.+%.(.+)$") or '') -- file extension local image = quote(mw.ustring.gsub(args0.image,'_',' ')) local prop = lut[ext] if prop and not data[prop] then add_QS(qsTable, prop, image) data[prop] = image -- mark it so we don't add it twice end end -- add QS based on current filename local image = quote(mw.ustring.gsub(args0.pagename,'_',' ')) -- get current filename local multiPageFile = (args0.namespace==6 and args0.num_pages>1 and (args0.mimeType=='application/pdf' or args0.mimeType=='image/vnd.djvu')) if (not data.P996 and multiPageFile) then -- QS code to help transfer image to Wikidata local qual = if_else(args0.image_page, {P4714=tostring(args0.image_page)}, nil) add_QS(qsTable, 'P996', image, qual) end if (args0.namespace==6 and args0.num_pages==1 and args0.infobox=='artwork') then -- QS code to help transfer image to Wikidata local ext = mw.ustring.lower(mw.ustring.match(args0.pagename, "^.+%.(.+)$")) -- file extension local prop = lut[ext] --prop = if_else(prop, prop, 'P18') if prop and not data[prop] then add_QS(qsTable, prop, image) end end -- add "Commons Category" (P373) if template at category page if not data.homecat and args0.namespace==14 then add_QS(qsTable, 'P373', image) end -- look for hidden text in various templates so they can be passed to Wikidata if needed using QS	-- copy args0['data'] to args0['era'] if it contains an "era QS" -- because they share one parameter/field at template level input, -- but need separate handling here -- TODO: splitting/sorting (at an earlier stage) would be better than simply copying, -- in cases where there is one value for each they won't be processed without splitting if args0['date'] and TagQS.hasTag('era') then args0['era'] = args0['date'] end -- different fields from different tables are allowed to create QS codes to allow data transfer to Wikidata -- some fields are often not used correctly in some templates like institution in book template and are not allowed to create QS	local fields = {object_type='artwork', era='artwork', medium='artwork', dimensions='artwork', institution='artwork', artist='artwork', reference_wga='artwork', date='all' , language='book', author='book', translator='book', publisher='book', printer='book', illustrator='book', editor='book', publication_date='book'} for field, infobox in pairs( fields ) do		if args0[field] and not data[field] and (args0.infobox==infobox or infobox=='all') then local qs = TagQS.readTag(args0[field], field) if qs then for _, v in ipairs( mw.text.split( qs, ';', true ) ) do					table.insert( qsTable, v ) end end end end if args0.dimensions and not data.dimensions and (args0.infobox=='artwork') then for _, v in ipairs(TagQS.readTags(args0.dimensions, 'dimensions')) do			table.insert( qsTable, v ) end end -- Special case of QS codes for multilingual labels extracted from and  templates, etc.	if args0.title then local max_title_len = 100 -- strip titles from title field with template: local title = TagQS.readTag(args0.title, 'title') or '' local label, lang = mw.ustring.match(title, 'P1476%|((%w+):.+)') if lang and label then label = nowiki(label) if not(data.title_ and data.title_[lang]) and #label<max_title_len then add_QS(qsTable, 'P1476', label) end end lang, label = mw.ustring.match(title, 'P1476%|(%w+):(.+)') if lang and label then label = nowiki(label) if not(data.labels and data.labels[lang]) and #label<max_title_len then add_QS(qsTable, 'L'..lang, label) end end

-- strip titles from title field with templates, with fields like |en=, |es=, etc.		for _, title in ipairs(TagQS.readTags(args0.title, 'label')) do		    lang, label = mw.ustring.match(title, 'L(%w+)[%|,](.+)') if lang and label then label = nowiki(label) if not(data.labels and data.labels[lang]) and #label *([^%<]+)%' for lang, label in mw.ustring.gmatch(args0.title, pat) do			label = nowiki(label) if not(data.labels and data.labels[lang]) and #label<max_title_len then add_QS(qsTable, 'L'..lang, quote(label)) end end end -- add "instance of" statement local type_id = data.object_type_id if not data.object_type_id then if args0.infobox=='book' then add_QS(qsTable, 'P31', 'Q3331189') -- version, edition, or translation elseif args0.object_type then type_id = ObjectTypeID(args0.object_type) if type_id then add_QS(qsTable, 'P31', type_id)  -- painting etc.				data.object_type_id = { type_id } -- object_type_id is an array end end end -- add descriptions local artist_id = data.artist_id or args0.artist_id local typeList = { Q3305213={de="Gemälde von %s", en="painting by %s", es="cuadro de %s", fr="tableau de %s", it="pittura di %s", nl="schilderij van %s", sl="slika avtorja %s"} }	if type_id and artist_id and typeList[type_id] then for lang, phrase in pairs(typeList[type_id]) do 			if not data.descriptions or not data.descriptions[lang] then local artist = mw.wikibase.getLabelByLang(artist_id, lang) if artist then add_QS(qsTable, 'D'..lang, quote(string.format(phrase, artist))) end end end end -- ==================================================	-- === Create QuickStatement codes ================== -- ==================================================	if #qsTable==0 then return nil end local QS = render_QS_URL(qsTable, args0.wikidata) return QS end

-- =========================================================================== -- === Function for creating search icons                                 === -- === INPUTS:                                                            === -- === * args0 - local inputs from the Artwork template                   === -- =========================================================================== local function add_search_link(args0) local sTable, tTable = {}, {} local creator_id, s, t, text, QS, title -- get English title if args0.title then local title = TagQS.readTag(args0.title, 'label') or ''

-- strip titles from title field with templates, with fields like, , etc.		s = 'Len%|(.+)' for label in mw.ustring.gmatch(args0.title, s) do			title = nowiki(label) end end if args0.title and not title then -- strip titles from title field with templates, like, , etc.		s = '% *([^%<]+)%' for label in mw.ustring.gmatch(args0.title, s) do			title = nowiki(label) end end text = title or nowiki(args0.title) or '' -- get author info if args0.infobox=='artwork' then creator_id = args0.artist_id or args0.author_id elseif args0.infobox=='photograph' then creator_id = args0.photographer_id or args0.author_id elseif args0.infobox=='book' then creator_id = args0.author_id end if creator_id then local prop = if_else(args0.infobox=='book', 'P50', 'P170') sTable[prop] = creator_id else text = text .. ' ' .. nowiki(args0.artist or args0.author or '') end

-- set other properties if args0.object_type=='painting' and args0.infobox=='artwork' then sTable.P31 = 'Q3305213' -- painting elseif args0.infobox=='book' then sTable.P31 = 'Q3331189' -- version, edition, or translation end if args0.institution_id then sTable.P195 = args0.institution_id end if args0.id then text = text .. ' ' .. nowiki(args0.id) -- add accession_id as text not as P217 statement end

-- If no usable info than abort local count = 0 for _ in pairs(sTable) do count = count + 1 end if count==0 then return nil end

-- create URL and clickable icon with either Cirrus search -- see https://www.mediawiki.org/wiki/Help:Extension:WikibaseCirrusSearch table.insert(tTable, text) for prop, field in pairs( sTable) do table.insert(tTable, 'haswbstatement:' .. prop .. '=' .. field) end s = mw.uri.encode(table.concat(tTable, ' '), "QUERY") s = {'search='..s, 'title=Special:Search', 'profile=advanced', 'fulltext=1', 'ns0=1'} s = 'https://www.wikidata.org/w/index.php?' .. table.concat(s,'&')  -- create full URL link s = '' -- create URL and clickable icon with SPARQL search tTable = {} table.insert(tTable, 'SELECT ?item ?itemLabel ?image') table.insert(tTable, 'WHERE {') for prop, field in pairs( sTable) do table.insert(tTable, ' ?item wdt:' .. prop .. ' wd:' .. field .. '.') end table.insert(tTable, ' OPTIONAL { ?item wdt:P18 ?image } .') table.insert(tTable, ' SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }') table.insert(tTable, '}') t = mw.uri.encode(table.concat(tTable, '\n'), "PATH") t = 'https://query.wikidata.org/#' .. t	t = ''

-- go to Data:Completely indexed painting collections.tab and look up if 	-- args0.institution_id is one of the items in column 1 local collCmpl, cat = false, '' if args0.object_type=='painting' and args0.infobox=='artwork' and args0.institution_id then local tab = mw.ext.data.get('Completely indexed painting collections.tab', 'en') for iRow, row in pairs(tab.data) do			if row[1]==args0.institution_id then collCmpl = true -- no need for adding painting items to this collection end end end if collCmpl then cat = '\n' end

-- go to Data:Completely indexed painters.tab and look up if 	-- args0.artist_id is one of the items in column 1 local authorCmpl = false if args0.object_type=='painting' and args0.infobox=='artwork' and (args0.artist_id or args0.author_id) then local tab = mw.ext.data.get('Completely indexed painters.tab', 'en') for iRow, row in pairs(tab.data) do			if row[1]==args0.artist_id or row[1]==args0.author_id then authorCmpl = true -- no need for adding painting items by this painter end end end if authorCmpl then cat = '\n' end if not collCmpl and not authorCmpl then args0.wikidata = 'CREATE' QS = create_QuickStatements(args0, {}) end local searchStr = ' (' .. table.concat({s, t, QS}, ' ') ..')' return searchStr, cat end

-- =========================================================================== local function chect_instance_of(instance_of) -- check object_type_id against a list of incorrect values for P31 property of associated item -- Black and white list id is of wrong type if bwLUT returns "1", bwLUT = "2" means good type -- bad {Q5='human', Q11266439='template ', Q4167410='disambiguation', Q4167836='category', Q532='village', Q482994='album', Q16521='taxon' } -- groups {Q15727816='painting series', sculpture series (Q19479037),  artwork series (Q15709879),  group of sculptures (Q27031439), --        group of paintings (Q18573970),  polyptych (Q1278452),  diptych (Q475476),  triptych (Q79218)  } -- good {Q199414='bog body', Q7881='skeleton'} -- one of those overwrites "bad" flag so 'human' & 'bog body' is OK (those are museum objects) local groupItem = false if instance_of then local bad = false local bwLUT = {Q5=1, Q11266439=1, Q4167410=1, Q4167836=1, Q532=1, Q482994=1, Q16521=1, Q15727816=2, Q19479037=2, Q15709879=2, Q27031439=2, Q18573970=2, Q1278452=2, Q475476=2, Q79218 = 2, Q199414=3, Q7881=3} for _, typeId in ipairs( instance_of ) do			local v = bwLUT[typeId] if v==1 then bad = true elseif v==2 then groupItem = true return 'group' elseif v==3 then bad = false return 'good' end end return if_else(bad, 'bad', 'good') end return 'unknown' end

-- =========================================================================== -- === This function is responsible for adding maintenance categories     === -- === to pages in creator namespace which are related to Wikidata        === -- === INPUTS:                                                            === -- === * args0 - local inputs from the Artwork template                   === -- === * args1 - merge of local and Wikidata metadata                     === -- === * data  - data pulled from Wikidata                                === -- =========================================================================== local function add_wikidata_maintenance_categories(args0, args1, data) local cats = '' -- categories local comp = {} -- outcome of argument vs. Wikidata comparison local OK = ((args0.infobox=='artwork' or args0.infobox=='photograph') and (args0.namespace==6 or args0.namespace==14)) -- artworks and photographs can be in file or category namespace or (args0.infobox=='book' and (args0.namespace==10 or args0.namespace==14) ) -- books can be in template or category namespace or (args0.infobox=='book' and args0.namespace==6 and (args0.mimeType=='application/pdf' or args0.mimeType=='image/vnd.djvu')) -- books can also be in file namespace if it is PDF or DjVu

if ( not OK or (args0.wikidata_cat==false)) then -- continue only if the namespace is a Category or file return cats, args1 end local LUT = {artwork='Artworks', photograph='Photographs', book='Books'}

-- skip the rest if no item ID	if not args0.wikidata then local cat2, TypeLUT, oType TypeLUT = {['grave']='Graves', ['tomb']='Graves', ['funeral chapel']='Graves', ['funeral niche']='Graves', ['painting']='Paintings', ['aircraft']='SKIP' } oType  = TypeLUT[string.lower(args0.object_type or '')] or LUT[args0.infobox] if oType~='SKIP' then cats = string.format('%s\n', cats, oType) end args1.QS, cat2 = add_search_link(args0) return cats .. (cat2 or ''), args1 end --=======================================================================================================	--=== Categories and files with template linked to Wikidata item below --=======================================================================================================	cats = string.format('%s\n', cats, LUT[args0.infobox], args0.wikidata)

-- check object_type_id against a list of incorrect values for P31 property of associated item local groupItem = false if data.object_type_id then local status = chect_instance_of(data.object_type_id ) groupItem = (status == 'group') if status == 'bad' or status == 'group' then cats = string.format('%s\n', cats, LUT[args0.infobox], status, args0.wikidata) end end -- local fields which are missing on Wikidata local fields = {'date', 'publication_date', 'medium', 'dimensions', 'image', 'institution', 'author', 'artist'} for _, field in ipairs( fields ) do		if not data[field] and args0[field] and not string.match(args0[field], '% Unknown '..field..'<%/span%>') then comp[field] = 'missing' end end if not data.id and args0.id then comp['accession number'] = 'missing' end

if comp.artist=='missing' and string.match(args0.artist, '% Unknown a%w+%<%/span%>') then comp.artist = nil -- ignore artist end

-- mark local fields redundant to Wikidata local fields = {['date']='date', medium='medium', dimensions='dimensions', institution_id='institution', author_id='author', artist_id='artist'} for field1, field2 in ipairs( fields ) do		if data[field1] and args0[field1] and data[field1]==args0[field1] then comp[field2] = 'redundant' end end -- Redundant author and artist if (data.author_id==args0.artist_id and data.author_id) then comp.artist = 'redundant' end if (data.artist_id==args0.author_id and data.artist_id) then comp.author = 'redundant' end -- handle case when creator template is a red-link but Wikidata has creator item ID	if (string.match(args0.artist or '', "%[%[:Creator:") and data.artist) then args1.artist = data.artist comp.artist = 'redundant' end if (string.match(args0.author or '', "%[%[:Creator:") and data.author) then args1.author = data.author comp.author = 'redundant' end -- process "image" field if (data.image and args0.image) then comp.image = 'redundant' end local simpleFile = (args0.namespace==6 and args0.num_pages==1 and args0.infobox=='artwork') if not data.image and (args0.image or simpleFile) then comp.image = 'missing' end -- add categories related to accession number for artworks if not args0.id and data.id and args0.infobox=='artwork' then cats = string.format('%s\n', cats, args0.wikidata) cats = string.format('%s\n', cats, data.id_id or 'zzz') end if not data.genre_id and args0.infobox=='artwork' and args0.namespace==6 then cats = string.format('%s\n', cats, args0.wikidata) end

-- ==================================================	-- === Create categories based on comp structure ==== -- ==================================================	for field, outcome in pairs( comp ) do		cats = string.format('%s\n', cats, LUT[args0.infobox], outcome, field, args0.wikidata) end -- Add QuickStatements if not groupItem and args0.wikidata then args1.QS = create_QuickStatements(args0, data) end if args1.QS then cats = string.format('%s\n', cats, LUT[args0.infobox]) end return cats, args1 end

-- =========================================================================== -- === Harvest Structured data on Commons properties                      === -- === INPUTS:                                                            === -- === * mid - SDC ID                                                     === -- === * lang  - language id of the desired language                      === -- === * namespace - namespace number of the page calling the module      === -- =========================================================================== local function harvest_SDC(args0, lang)

local bookFlag = (args0.infobox=='book' and (args0.mimeType=='application/pdf' or args0.mimeType=='image/vnd.djvu')) local sdc = {} -- structure similar to "args" but filled with SDC data local cats = '' -- Harvest the different artwork types local tab = mw.ext.data.get('Artwork types.tab', 'en') sdc.artwork_types = {} for iRow, row in pairs(tab.data) do		sdc.artwork_types[row[1]] = row[3] end local entity = mw.wikibase.getEntity if not entity then if args0.wikidata and bookFlag then cats = cats .. '\n' elseif args0.wikidata and args0.infobox~='book' then cats = cats .. '\n' end return sdc, cats end local property = {P6243='digital_representation_of', P4714='image_page', P921='main_subject', P180='depicts', P760='dpla_id' } for prop, field in pairs( property ) do		sdc[field] = getProperty(entity, prop) end

-- get Wikidata item ID from digital representation of (P6243) if sdc.digital_representation_of then local wEntity = mw.wikibase.getEntity(sdc.digital_representation_of) if not wEntity then cats = cats .. ''		elseif wEntity.id~=sdc.digital_representation_of then cats = cats .. ''		end sdc.wikidata = sdc.digital_representation_of -- If digital representation of is set, main subject should be set too to the same if sdc.main_subject then if sdc.digital_representation_of~=sdc.main_subject then cats = cats .. ''			end else cats = cats .. ''		end

-- If digital representation of is set, depicts should be set too to the same if sdc.depicts then if sdc.digital_representation_of~=sdc.depicts then cats = cats .. ''			end else cats = cats .. ''		end end -- get Wikidata item ID from main subject (P921). If multiple, this will get the first preferred one -- temporarly exclude files with "dpla_id" property while I debug why are they often connect to 	-- wrong wikidata items if not sdc.wikidata and sdc.main_subject and not sdc.dpla_id then local msEntity = mw.wikibase.getEntity(sdc.main_subject) if not msEntity then cats = cats .. ''		elseif msEntity.id~=sdc.main_subject then cats = cats .. ''		end -- Make sure it's an actual thing (instance) instead of something general (subclass) local msProperty = {P31='instance_of', P279='subclass_of' } local msProperties = {} for prop, field in pairs( msProperty ) do			msProperties[field] = getProperty(msEntity, prop) end if msProperties.instance_of and not msProperties.subclass_of then local instances_of = getBestProperties(msEntity, 'P31') local status = chect_instance_of(instances_of ) if status ~= 'bad' then sdc.wikidata = sdc.main_subject sdc.main_subject_accepted = sdc.main_subject

-- If main subject is set, depicts should be set too to the same if sdc.main_subject_accepted and sdc.depicts then if sdc.main_subject~=sdc.depicts then -- Here we'll probably see some false positives cats = cats .. ''					end else cats = cats .. ''				end end end end if not sdc.wikidata and args0.wikidata then if bookFlag then cats = cats .. '\n' elseif args0.infobox~='book' then cats = cats .. '\n' end elseif sdc.wikidata and args0.wikidata and sdc.wikidata~=args0.wikidata then cats = cats .. '\n' end -- get source from P7482 if entity.statements and entity.statements.P7482 then local statement = entity.statements.P7482[1] if statement.mainsnak.datavalue.value.id=='Q74228490' and statement.qualifiers and statement.qualifiers.P973 then local url = statement.qualifiers.P973[1].datavalue.value if statement.qualifiers.P137 then local id = statement.qualifiers.P137[1].datavalue.value.id				local label = getLabel(id, lang, '-') sdc.source_ = '[' .. url ..' ' .. label ..']' .. core.editAtSDC(args0.pagename, 'P7482', lang) else sdc.source_ = url .. core.editAtSDC(args0.pagename, 'P7482', lang) end end end return sdc, cats end

-- =========================================================================== -- === Harvest Wikidata properties matching creator template fields       === -- === INPUTS:                                                            === -- === * itemID1 - item id or a q-code from the template                  === -- === * itemID2 - item id or a q-code from SDC                           === -- === * lang  - language id of the desired language                      === -- === * namespace - namespace number of the page calling the module      === -- =========================================================================== local function harvest_wikidata(itemID1, itemID2, lang, namespace, infobox, pagename) local data = {} -- structure similar to "args" but filled with Wikidata data local cats = '' local frame = mw.getCurrentFrame local entity = nil local itemID = itemID1 or itemID2 if mw.wikibase and itemID then local LUT = {artwork='Artworks', photograph='Photographs', book='Books'} entity = mw.wikibase.getEntity(itemID) if not entity then cats = string.format('', LUT[infobox]) elseif entity.id~=itemID and itemID1 then cats = string.format('', LUT[infobox]) end end if not entity then return data, cats end

-- inception date: translated date and year number local d = getDate(entity, 'P571', lang) -- inception date if d.str then data.date_, data.year = d.iso, d.year data.date = d.str .. core.editAtWikidata(entity.id, 'P571', lang) end

-- publication date: translated date and year number local d = getDate(entity, 'P577', lang) -- publication date if d.str then data.publication_date_, data.publication_year = d.iso, d.year data.publication_date = d.str .. core.editAtWikidata(entity.id, 'P577', lang) end -- harvest string properties local Debug ={} local property = {P10='video', P18='image', P996='scan', P4896='model3D', P373='homecat', P51='audio', P2093='authorStr', P393='edition', P4714='image_page', P1957='wikisource_index', P7420='framed_image' } for prop, field in pairs( property ) do		data[field] = getProperty(entity, prop) data[prop] = data[field] -- keep tract which properties were present at Wikidata end data.image = data.image or data.scan or data.video or data.model3D or data.framed_image data.image_page = tonumber(data.image_page) if data.edition then data.edition = data.edition .. core.editAtWikidata(entity.id, 'P393', lang) end -- harvest Q-code properties which are than converted from Q-number to labels (pick one) local property = { P189='place_of_discovery', P2079='technique', P872='printer', P136='genre', P921='subject', P179='series_title', P361='part_of', P629='edition_of', P1071='place_of_creation', P495='country_of_origin'} for prop, field in pairs( property ) do		local id = getProperty(entity, prop) if id then data[field] = getLabel(id, lang, "wikipedia") .. core.editAtWikidata(entity.id, prop, lang) end end data.place_of_creation = data.place_of_creation or data.country_of_origin data.genre_id = getProperty(entity, 'P136') -- harvest Q-code properties which are than converted from Q-number to labels (pick all) local property = { P31='object_type', P407='language', P123='publisher', P291='place_of_publication'} for prop, field in pairs( property ) do		local ids = getBestProperties(entity, prop) if ids then local T = {} for _, id in ipairs( ids ) do				table.insert(T, getLabel(id, lang)) end data[field] = table.concat(T, " / ") .. core.editAtWikidata(entity.id, prop, lang) data[field ..'_id'] = ids end end

-- get era data.era_id = getBestProperties(entity, 'P2348') if data.era_id then local T = {} for _, id in ipairs( data.era_id ) do			local eraText = getLabel(id, lang) local spanText = periodSpan(id, lang) if spanText then eraText = eraText .. ' ' .. spanText end table.insert(T, eraText) end data.era = table.concat(T, " / ") .. core.editAtWikidata(entity.id, 'P2348', lang) end if data.era and data.date then data.date = data.date .. " " .. data.era elseif data.era and not data.date then data.date = data.era end -- get author and/or author creator template local property = { P170='creator', P50='author', P84='architect', P287='designer', P98='editor', P655='translator', P110='illustrator', P2679='author_of_foreword', P2680='author_of_afterword' } for prop, field in pairs( property ) do		local d = Art.get_creator(entity, prop, lang) data[field] = d.str data[field.."_id"] = d.id	end data.author = data.author or data.authorStr     -- P2093='author name string'

-- get title (from 3 properties and label) --local property = { P1476 = 'title', P1448='official_name', P1705='native_label', P1680='subtitle'} local property = { P1476 = 'title', P1680='subtitle'} for prop, field in pairs( property ) do		local title = {} for _, statement in pairs( entity:getBestStatements(field)) do 			if (statement.mainsnak.snaktype == "value") then local val = statement.mainsnak.datavalue.value title[val.language] = val.text -- look for multiple values each with a language code end end if #title>0 then data[field] = core.langSwitch(title, lang) end if field=='title' then data.title_ = title end end --data.title = data.title or data.official_name or data.native_label data.title = TitleFromWD(entity, lang) data.label = getLabel(entity, lang) -- get labels in all the languages data.labels = {} if entity.labels then for lang, val in pairs(entity.labels) do -- loop over all labels data.labels[lang] = val.value; data.label = data.label or data.labels[lang] -- no label in preferred language so grab any label end end -- get labels in all the languages data.descriptions = {} if entity.descriptions then for lang, val in pairs(entity.descriptions) do -- loop over all descriptions data.descriptions[lang] = 1; -- just record if it is present or not end end -- get authority control (rarely used for artworks) local AC_cats data.authority, AC_cats = authorityControl(entity, {wikidata = itemID}, lang, 5) local _,nIdentifiers = string.gsub(data.authority, "*", "") if nIdentifiers<=1 then data.authority, AC_cats = nil, '' end if not (namespace == 2 or namespace == 828 or math.fmod(namespace,2)==1) then cats = cats .. AC_cats -- lets not add authorityControl categories to user pages, modules or talk pages and concentrate on templates and categories instead end -- get object location if getProperty(entity, 'P625') or getProperty(entity, 'P9149') then local coorFun = require('Module:Coordinates')._LocationTemplateCore local coori18n = require('Module:i18n/coordinates') data.coordinates = coorFun({wikidata=entity, lang=lang, globe='earth', mode='object', bare=true}) end -- prepare fallback list of languages local langList = mw.language.getFallbacksFor(lang) table.insert(langList, 1, lang) -- get wikisource or wikiquote local projects = {s='wikisource', q='wikiquote'} for code, project in pairs(projects) do		local sitelinks = getSitelinks(entity, project) if sitelinks then local lng, _ = next(sitelinks)   -- get language of the first sitelink table.insert(langList, lng) -- and add it to the list	so there is at least one lang with sitelink on the list for _, language in ipairs(langList) do 				local sitelink = sitelinks[language] if sitelink then data[project] = string.format('%s:%s:%s', code, language, sitelink) break end end end end -- if no wikisource sitelink than use P1957 'wikisource_index' property -- wikisource_index is in full url format (like https://es.wikisource.org/wiki/%C3%8Dndice:Sonetos_-_Leopoldo_Diaz.pdf) -- instead of interlink format (like s:es:%C3%8Dndice:Sonetos_-_Leopoldo_Diaz.pdf) data.wikisource = data.wikisource or data.wikisource_index

-- properties with functions data.object_history    = Art.get_object_history(entity, lang)     -- object history data.exhibition_history = Art.get_exhibition_history(entity, lang) -- exhibition.history data.inscriptions      = Art.get_inscription(entity, lang) data.medium            = Art.get_medium(entity, lang) data.medium            = empty2nil(data.medium) or data.technique; data.references        = Art.get_references(entity, lang) data.other_versions    = Art.get_other_versions(entity, pagename) data.reference_wga     = data.references and mw.ustring.isutf8(data.references) and mw.ustring.find( data.references, 'www%.wga%.hu' ) -- is there a link to wga.hu? local X                = Art.get_depicted(entity, lang) data.depicted_people   = X[1] data.description       = X[2] -- depicted artistic or religious themes X                      = Art.get_accession_number(entity, lang) data.id                = X.str -- wikitext version of the accession number data.id_id             = X.id  -- one of accession numbers, which will be used as a sortkey X                      = Art.get_institution(entity, lang) data.institution       = X.institution data.institution_id    = X.id	data.department         = X.location data.dimensions        = Size({entity=entity}, nil, nil, lang) data.dimensions        = empty2nil(data.dimensions);

return data, cats end

-- =========================================================================== -- === Adjust parameters related to books  === -- === and resolve potential aliases                                      === -- === INPUTS:                                                            === -- === * frame - contains imput parameters passed from the template       === -- === OUTPUTS:                                                           === -- === * args - cleaned up inputs                                         === -- =========================================================================== local function header_customization(args0, data, args1)

-- get author Wikidata ID based on Wikidata local creator_label, creator_id, creator_str, title_label, creator_tag if args0.wikidata then creator_id = data.creator_id or data.author_id or data.architect_id or data.designer_id or data.editor_id or data.translator_id or data.illustrator_id if creator_id then creator_label = getLabel(creator_id, args0.lang) end title_label = data.label -- create name based on Wikidata label end

-- get author Wikidata ID based on commons if not creator_label then if args0.infobox=='artwork' then creator_id = args0.artist_id or args0.author_id creator_str = args0.artist or args0.author elseif args0.infobox=='photograph' then creator_id = args0.photographer_id or args0.author_id creator_str = args0.photographer or args0.author elseif args0.infobox=='book' then creator_id = args0.author_id creator_str = args0.author end creator_tag = TagQS.readTag(creator_str or '', 'creator') if creator_tag then -- if author was "" than creator_id will be "P170|Q4233718|P1776|Q446631 " local res = {creator_tag:match("^P170%|Q4233718%|(P%d+)%|(Q%d+)$")} if res and res[1] and res[2]then creator_label = getLabel(res[2], args0.lang) local LUT = {P1773='attributed to', P1774='workshop of', P1775='follower of', P1776='circle of', P1777='manner of', P1779='possibly', P1780='school of', P1877='after'} creator_label = alterName(LUT[res[1]], creator_label, args0.lang) -- call module:Name with the task if creator_label == "name not supported" then creator_label = nil end end elseif creator_id and creator_id:match("^Q%d+$") then creator_label = getLabel(creator_id, args0.lang) end end -- scrape labels from template if not title_label and args0.title then local labels = {} for _, title in ipairs(TagQS.readTags(args0.title, 'label')) do			local lang, label = mw.ustring.match(title, 'L(%w+)%|"([^"]+)"') 			if lang and label then				labels[lang]=label			end		end		title_label = core.langSwitch(labels,args0.lang)	end	-- get title based on commons	if not title_label and args0.title then		title_label = nowiki(args0.title) 	end	-- if title too long than truncate it	if title_label and #title_label>250 then 		title_label = nil	end	if creator_label and #creator_label>150 then 		creator_label = nil	end	-- combing author and title labels	if creator_label and title_label then 		local colon = mw.message.new( "Colon-separator" ):inLanguage(args0.lang):plain		args1.name = creator_label .. colon .. title_label	else		args1.name = title_label	end

-- if we have a template in file namespace and file of in PDF or DjVu than use it as image if args0.infobox=='book' and args0.namespace==6 and args1.image==nil and (args0.noimage==false or args1.image_page) and (args0.mimeType=='application/pdf' or args0.mimeType=='image/vnd.djvu') then args1.image = args0.pagename end

return args1 end

-- ================================================== -- === External functions =========================== -- ================================================== local p = {}

-- =========================================================================== -- === Version of the function to be called from other LUA codes -- =========================================================================== function p.create_infobox(args0) local lang = args0.lang -- user's language local cats, cats1, cats2 = , , ''        -- categories local str, data, sdc -- ===========================================================================	-- === Step 1: clean up of template arguments "args0" -- ===========================================================================	args0 = artcore.clean_input_parameters(args0) if not args0.wikidata and args0.namespace==14 then local entity = mw.wikibase.getEntity if entity then -- category page connected to Wikidata through a sitelink args0.wikidata = entity.id			cats1 = '' local qid = getProperty(entity, 'P301') -- follow wikidata's item redirect-like properties if qid then -- item had "category's main topic (P301)" -> use it				args0.wikidata = qid end end end -- ===========================================================================	-- === Step 2: one by one merge Wikidata and creator data -- ===========================================================================	local wikidata_temp = args0.wikidata -- Wikidata from template sdc = {} if not args0.no_sdc and args0.namespace==6 then -- if file namespace sdc, cats2 = harvest_SDC(args0, lang) -- harvest Structured Data on Commons if (args0.sdc_source==false) or (args0.source) then -- get source from SDC only if this is NOT the artwork infobox of template -- also skip sdc.source if we have args0.source, otherwise we might and up with 2 source fields sdc.source_ = nil args0.sdc_source = nil -- not needed anymore end args0.source_   = args0.source_    or sdc.source_    -- if source not provided than get it from SDC args0.wikidata  = args0.wikidata   or sdc.wikidata   -- if Wikidata not provided than get it from P6243 ("digital representation of") args0.image_page = args0.image_page or sdc.image_page -- title image page for multipage book files end data, cats = harvest_wikidata(wikidata_temp, sdc.wikidata, lang, args0.namespace, args0.infobox, args0.pagename) cats = (cats or '') .. (cats1 or '') .. (cats2 or '') -- based on the template type determine the meaning of "creator" if args0.infobox=='photograph' then data.photographer, data.photographer_id = data.creator, data.creator_id elseif args0.infobox=='book' then --data.author, data.author_id = data.creator, data.creator_id else -- args0.infobox=='artwork' data.artist, data.artist_id = data.creator, data.creator_id end -- mass merge (prioritize local values) local args = {} local fields = { 'artist', 'artist_id', 'author', 'author_id', 'architect', 'designer', 'illustrator', 'publisher', 'editor', 'translator', 'printer', 'photographer', 'photographer_id', 'wikisource', 'wikiquote', 'title', 'object_type', 'authority', 'image', 'id', 'homecat', 'coordinates', 'genre', 'subject', 'image_page', 'date', 'medium', 'name', 'depicted_people', 'depicted_place', 'place_of_creation', 'place_of_discovery', 'dimensions', 'institution', 'department', 'references', 'object_history', 	'exhibition_history', 'inscriptions', 'place_of_publication', 'publication_date', 'language', 'subtitle', 'series_title', 'volume', 'edition', 'edition_of', 'author_of_foreword', 'author_of_afterword','description','other_versions', 'part_of' }	for _, field in ipairs( fields ) do 		args[field] = args0[field] or data[field] end -- copy fields only defined locally local fields = { 'wikidata', 'original_description_info', 'original_description', 'biased', 'camera_coord', 'depicted_part', 'source', 'source_', 'strict', 'permission', 'demo', 'lang', 'notes', 'credit_line', 'linkback', 'pageoverview', 'other_fields', 'other_fields_1', 'other_fields_2', 'other_fields_3', 'wikidata_cat', 'namespace', 'infobox', 'demo_image', 'no_qs' }	for _, field in ipairs( fields ) do 		args[field] = args0[field] end

if args.artist_id and #args.artist_id>1 and args.artist_id==args.author_id and data.artist_id~=data.author_id then -- args.artist_id is string so #args.artist_id is string length. data.artist_id!=data.author_id is to make sure -- both _ids are not coming from Wikidata. Might need to also skip if args0.author has content other than creator template args.author, args.author_id = nil, nil; -- if artist and author are the same than drop one cats = cats .. '\n' end if args0.infobox=='artwork' and args0.photo_date and args0.photographer then cats = cats .. '\n' end if args0.infobox=='book' and args0.publication_date and data.date then -- some magazines have local publication date for specific issue and inception date on Wikidata for the -- magazine (publication of the first issue) -> ignore the inception date args.date = nil end -- look for cases where a field like "photographer" has a single creator template with a role -- "photographer" stated in front of it -> strip the role. local LUT = { Q483501='artist', Q482980='author', Q42973='architect', Q5322166='designer', Q1607826='editor', Q333634='translator', Q644687='illustrator', Q33231='photographer' } for item, field in pairs( LUT ) do if args[field] and args[field .. '_id'] then local tag = TagQS.createTag('creator_role', nil, item) local start, finish = mw.ustring.find( args[field], tag, 1, true ) if start then -- remove "...: " + tag -- split the string in 2 using the tag as a divider local str1 = mw.ustring.sub( args[field], 1, start-1 ) local str2 = mw.ustring.sub( args[field],  finish+1 ) -- remove bold text at the end of str1 local str1 = mw.ustring.gsub( str1, ".*: $", '' ) args[field] = str1 .. str2 -- splice 2 halfs end end end

-- internationalize local object_type string if args0.object_type and args.object_type==args0.object_type then local objectType = require('Module:I18n/objects')._object args.object_type = objectType(args.object_type, nil, lang) end

-- convert all empty strings to nils for _, field in ipairs( fields ) do 		if args[field] == '' then args[field] = nil; end end -- ===========================================================================	-- === Step 3: create maintenance categories and render html of the table -- ===========================================================================	args = header_customization(args0, data, args) cats = cats .. add_maintenance_categories(args0, args) args.QS = nil; if not args0.no_sdc then cats2, args = add_wikidata_maintenance_categories(args0, args, data) cats = cats .. cats2 end if args0.wikidata and (args0.wikidata ~='CREATE') and (args.namespace==6) and not (sdc.digital_representation_of or sdc.main_subject) then if data.object_type_id then for _, typeId in ipairs( data.object_type_id ) do				local artwork_type = sdc.artwork_types[typeId] if artwork_type == '2D' then cats = string.format('%s\n', cats, args0.wikidata) elseif artwork_type == '3D' then cats = string.format('%s\n', cats, args0.wikidata) -- Can add the other types here later end end end elseif sdc.digital_representation_of or sdc.main_subject_accepted then if data.object_type_id then for _, typeId in ipairs( data.object_type_id ) do				local artwork_type = sdc.artwork_types[typeId] if artwork_type and sdc.digital_representation_of then if artwork_type == '2D' then cats = string.format('%s\n', cats, sdc.wikidata) elseif artwork_type == '3D' then cats = string.format('%s\n', cats, sdc.wikidata) elseif artwork_type == 'both' then cats = string.format('%s\n', cats, sdc.wikidata) elseif artwork_type == 'unclassified' then cats = string.format('%s\n', cats, sdc.wikidata) elseif artwork_type == 'no' then cats = string.format('%s\n', cats, sdc.wikidata) else cats = string.format('%s\n', cats, sdc.wikidata) end elseif not artwork_type and sdc.digital_representation_of then cats = string.format('%s\n', cats, sdc.wikidata) elseif artwork_type and sdc.main_subject_accepted then if artwork_type == '2D' then cats = string.format('%s\n', cats, sdc.wikidata) elseif artwork_type == '3D' then cats = string.format('%s\n', cats, sdc.wikidata) elseif artwork_type == 'both' then cats = string.format('%s\n', cats, sdc.wikidata) elseif artwork_type == 'unclassified' then cats = string.format('%s\n', cats, sdc.wikidata) elseif artwork_type == 'no' then cats = string.format('%s\n', cats, sdc.wikidata) else cats = string.format('%s\n', cats, sdc.wikidata) end elseif not artwork_type and sdc.main_subject_accepted then cats = string.format('%s\n', cats, sdc.wikidata) end end end end

local results = artcore.build_html(args) return results, cats end

-- =========================================================================== -- === Versions of the function to be called from template namespace -- === Each template with it's own entry point -- =========================================================================== function p.artwork(frame) local args = artcore.read_input_parameters(frame) args.infobox = 'artwork' local cats0 = artcore.verify_input_parameters(args) args.demo_image = 'Noun project - Mona Lisa - in frame.svg' local results, cats1 = p.create_infobox(args) -- call the inner "core" function return results .. cats0 .. cats1 end

-- =========================================================================== function p.photograph(frame) local args = artcore.read_input_parameters(frame) args.infobox = 'photograph' local cats0 = artcore.verify_input_parameters(args) args.source_ = args.source or args.source_ args.source = nil args.demo_image = 'Breezeicons-actions-22-view-preview.svg' local results, cats = p.create_infobox(args) -- call the inner "core" function -- most photographs do not have Wikidata id so remove cat "Photographs without Wikidata item" cats = mw.ustring.gsub(cats,'%\n%[%[Category:Photographs without Wikidata item%]%]','') return results .. cats end

-- =========================================================================== -- This is for when we have photograph template in addition to artwork and we don't want any Wikidata function p.photograph_of_object(frame) local args = artcore.read_input_parameters(frame) args.infobox = 'photograph' local cats0 = artcore.verify_input_parameters(args) args.source_ = args.source or args.source_ args.source = nil args.no_sdc = true local results, cats = p.create_infobox(args) -- call the inner "core" function return results .. cats end

-- =========================================================================== function p.art_photo(frame) local args = artcore.read_input_parameters(frame)	-- clean up input parameters args.permission = args.permission or args.photo_license or args.photo_licence  -- additional aliases args.artwork_license = args.artwork_license or args.artwork_licence args.source = args.source or args.source_ args.photo_license, args.photo_licence, args.artwork_licence, args.source_ = nil, nil, nil, nil local results = {} -- split input arguments local args2 = {} local fields = {['date']='photo_date', description='photo_description', author='photographer', source='source', other_versions='other_versions', other_fields ='other_fields', permission='photo_license', permission='permission' }	for field1, field2 in pairs( fields ) do 		if args[field2] then args2[field1] = args[field2] args [field2] = nil end end for _, field in ipairs( {'lang', 'demo'} ) do 		args2[field] = args[field] end args.permission = args.artwork_license args.artwork_license = nil

-- create object infobox args.infobox = 'artwork' local cats0 = artcore.verify_input_parameters(args) local header = frame:expandTemplate{ title = 'Section header', args = { ["1"]=args.artwork_header or 'Object', lang=args.lang } } table.insert(results, "===" .. header .. "===") args.demo_image = 'Noun project - Mona Lisa - in frame.svg' args.strict = false args.sdc_source = false -- no picking sources from SDC for this infobox local infobox, cats1 = p.create_infobox(args) -- call the inner "core" function table.insert(results, infobox) --create infobox header = frame:expandTemplate{ title = 'Section header', args = { ["1"]='Photograph', lang=args.lang } } table.insert(results, "===" .. header .. "===") args2.strict = false table.insert(results, Information(args2) ) table.insert(results, cats0 .. cats1) return table.concat(results, '\n') end

-- =========================================================================== function p.book(frame) local args = artcore.read_input_parameters(frame) args.infobox              = 'book' args.demo_image           = 'Placeholder book.svg' args.strict               = false args.place_of_publication = args.place_of_publication or args.city -- book specific aliases args.publication_date     = args.publication_date or args.date args.source_              = args.source or args.source_ if args.isbn or args.lccn or args.oclc or args.bnf then local auth_args = { ISBN=args.isbn, LCCN=args.lccn, OCLC=args.oclc, BNF=args.bnf, bare=1 } args.authority = frame:expandTemplate{ title = 'Book authority control', args=auth_args } args.isbn, args.lccn, args.oclc, args.bnf = nil, nil, nil, nil end args.source, args.city, args.date = nil, nil, nil local results, cats = p.create_infobox(args) -- call the inner "core" function return results .. cats end

return p