ਸਮੱਗਰੀ 'ਤੇ ਜਾਓ

ਮੌਡਿਊਲ:Artwork

ਵਿਕੀਸਰੋਤ ਤੋਂ

Documentation for this module may be created at ਮੌਡਿਊਲ:Artwork/doc

--[[  
  __  __           _       _           _         _                      _    
 |  \/  | ___   __| |_   _| | ___ _   / \   _ __| |___      _____  _ __| | __
 | |\/| |/ _ \ / _` | | | | |/ _ (_) / _ \ | '__| __\ \ /\ / / _ \| '__| |/ /
 | |  | | (_) | (_| | |_| | |  __/_ / ___ \| |  | |_ \ 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:
* User:Jarekt - original version 
]]
require('Module:No globals') -- 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 City             = require("Module:City")                                -- used to add wikidata based links to names of places
local labels           = require("Module:I18n/artwork")                        -- internationalization of labels
local ISOdate          = require('Module:ISOdate')                        
local Size             = require('Module:Size')._size                          -- Lua code behing {{Size}} template
local Art              = require('Module:Wikidata art')                        --
local alterName        = require("Module:Name")._name
local yesno            = require('Module:Yesno')
local bit32            = require( 'bit32' )

-- ==================================================
-- === Internal functions ===========================
-- ==================================================
local function langSwitch(list,lang)
	local langList = mw.language.getFallbacksFor(lang)
	table.insert(langList,1,lang)
	for i,language in ipairs(langList) do
		if list[language] then
			return list[language]
		end
	end
	return nil
end

local function isodate2timestamp(dateStr)
-- convert isodate to timestamp used by quick statements
	local tStamp = nil
	if string.match(dateStr,"^[0-1]%d%d%d$") then               -- if YYYY  format 
		tStamp = '+' .. dateStr .. '-00-00T00:00:00Z/9'
	elseif string.match(dateStr,"^[0-1]%d%d%d%-[0-1]%d$") then      -- if YYYY-MM format 
		tStamp = '+' .. dateStr .. '-00T00:00:00Z/10'
	elseif string.match(dateStr,"^[0-1]%d%d%d%-[0-1]%d%-[0-3]%d$") then  -- if YYYY-MM-DD format 
		tStamp = '+' .. dateStr .. 'T00:00:00Z/11'
	end
	return tStamp
end

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 = mw.ustring.gsub(str, '%<div style="display: none;"%>[^%<]+%</div%>', '')
	str = mw.ustring.gsub(str, '%<abbr %</abbr%>', '')
	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, "%[%[[^|]*|", "") -- remove piped links, like "[[:en:test|"
	str = mw.ustring.gsub(str, "%[[^ ]+ ", "")   -- remove URL links, like "[https://www.wikidata.org/wiki/Q2706250 "
	str = mw.ustring.gsub(str, "%[%[", "" )      -- remove piped links, like "[["
	str = mw.ustring.gsub(str, "%]", "" )        -- remove piped links, like "]" or "]]"
	return str
end

local function nowiki_title(str) -- remove all the links and url encode some characters
	if not str then
		return str
	end
	str = mw.ustring.gsub(str, "%)", "%%29" )    -- encode parenthesis
	str = mw.ustring.gsub(str, "%(", "%%28" )    -- encode parenthesis
	str = mw.ustring.gsub(str, "  ", " " )       -- multi-space to a single one
	str = mw.ustring.gsub(str, " ", "%%20" )     -- encode parenthesis
	return nowiki(str)
end


local function getProperty(entity, prop, outputType)
	local Output = {}
	if entity.claims and entity.claims[prop] then
		for _, statement in pairs( entity:getBestStatements( prop )) do
			if (statement.mainsnak.snaktype == "value") then 
				local val = statement.mainsnak.datavalue.value
				if val.id then 
					val = val.id
				elseif val.text then
					val = val.text
				end
				table.insert(Output, val)
			end
		end
	end
	if #Output==0 then
		return nil
	elseif outputType=='one' then
		return Output[1]
	else
		return Output
	end
end

-- ====================================================================
-- This function is responsible for producing HTML of a single row of the template
-- At this stage all the fields are already filed. There is either one or two fields
-- INPUTS:
-- * param1 and param2 - structures for 2 fields containing fields:
--    - tag      - I18n tag used for localization of the field name. Usually name of page in MediaWiki namespace which was imported from translatewiki.org. 
--                 Alternative is to pass already translated field name.
--    - field    - field content
--    - id       - ID tag added to HTML's <td> cell. if IDs of 2 fields ar the same than we ignore the second one
--    - wrapper  - some fields need a <span class=...> wrapper around the field content 
-- ====================================================================

--All other fields
local function Build_html_row(param, args)
	local LUT = {artwork=0, photograph=1, book=2}
    local demo = args.demo and bit32.extract( param.demo or 0, LUT[args.infobox])==1
	local field = args[param.field]
	if field=='' then field=nul; end
	if not (field or demo) then 
		return nil
	end
	if not param.id then -- "other fields" parameter
		return field
	end
	local tag = param.tag or 'bad'
	if string.sub(tag,1,10) == 'wm-license' then
		tag = mw.message.new( tag ):inLanguage(args.lang):plain() -- label message in args.lang language
	elseif string.match(tag, "^[QP]%d+$") then
		tag = getLabel(tag, args.lang, "-", "ucfirst")
	elseif labels[tag] then
		tag = langSwitch(labels[tag], args.lang)
	end
	local cell1 = string.format('<td id="%s" class="fileinfo-paramfield" lang="%s">%s</td>\n', param.id, args.lang, tag)
	local cell2 = string.format('<td>\n'.. param.wrapper ..'</td>', field or '')
	return string.format('<tr valign="top">\n%s%s\n</tr>\n\n', cell1, cell2)
end

-- ====================================================================
-- === This function is just responsible for producing HTML of the  ===
-- === template. At this stage all the fields are already filed     ===
-- ====================================================================
local function build_html(args, cats)
	-- get text direction
	local dir
	if mw.language.new( args.lang ):isRTL() then
		dir = 'rtl'
	else
		dir = 'ltr'
	end 

	-- original_description row has a different look than other rows
	if args.original_description and (args.original_description_info or args.biased) then
		local tag1, tag2 = "", ""
		if args.original_description_info then
			tag1 = string.format('<div style="background:#dde; font-size:86%%; direction:%s;">%s</div>', dir, args.original_description_info)
		end
		if args.biased then
			tag2 = langSwitch(labels.inaccurate_description, args.lang)
			tag2 = string.format('<div style="padding:0.5ex; margin:0 0 0.5ex 0; border: 1px solid red;">%s: %s</div>', tag2, args.biased)
		end
		args.original_description = tag1 .. tag2 .. args.original_description
	end
	
	-- files with no source will be flagged
	if (not args.source) and (not args.source_) and (args.strict==true) and (args.namespace==6) then
		args.nosource = mw.getCurrentFrame():expandTemplate{ title = 'Source missing' }
	end
	
	local nCol = 2
	if not args.image and args.demo then
		args.image = args.demo_image
	end
	if args.image  then
		nCol = 3
	end
	
	-- Top line 
	local top, results = {}, {}
	if args.name then
		table.insert(top, string.format('<span class="fn" id="artwork"><bdi>%s\n</bdi></span>', args.name ) )
	end
	if args.linkback then -- Wikidata Link
		table.insert(top, string.format('[[File:Blue pencil.svg|15px|%s|link=%s]]', args.linkback, args.linkback) )
	end	
	if args.wikidata then -- Wikidata Link
		table.insert(top, string.format('[[File:Wikidata-logo.svg|20px|wikidata:%s|link=wikidata:%s]]', args.wikidata, args.wikidata) )
	end
	if args.wikisource then --Wikisource link
		table.insert(top, string.format('[[File:Wikisource-logo.svg|15px|%s|link=%s]]', args.wikisource, args.wikisource) )
	end
	if args.wikiquote then --Wikiquote link
		table.insert(top, string.format('[[File:Wikiquote-logo.svg|15px|%s|link=%s]]', args.wikiquote, args.wikiquote) )
	end
	if args.QS then -- quick_statement link to upload missing info to wikidata
		table.insert(top, string.format('%s', args.QS) )
	end
	if #top>0 then
		local line = string.format('<th colspan="%i" style="background-color:#ccf; font-weight:bold; border:1px solid #aaa" text-align="left">%s</th>', nCol, table.concat(top, '&nbsp;')) 
		table.insert(results, string.format('<tr valign="top">\n%s\n</tr>\n', line))
	end
	
	-- Permissions tag
	local tag1 = mw.message.new( "wm-license-information-permission" ):inLanguage(args.lang):plain()
	local tag2 = mw.message.new( "wm-license-information-permission-reusing-link" ):inLanguage(args.lang):plain()
	local tag3 = mw.message.new( "wm-license-information-permission-reusing-text" ):inLanguage(args.lang):plain()
	local permission_tag = string.format("%s<br /><small>([[%s|%s]])</small>", tag1, tag2, tag3)
	
	-- define constants for readability
	-- demo=art+photo+book will show that row in demo mode in {{artwork}, {{Photograph}} and {{Book}} templates
	local none  = 0
	local art   = 1
	local photo = 2
	local book  = 4
	
	-- add other fields
	local param = {
		-- field name                   machine readable tag                         field name i18n approach                     show in demo mode?   field value wrapper
		{field='artist'               , id='fileinfotpl_aut'                       , tag='wm-license-artwork-artist',             demo=art,            wrapper='<div class="fn value">\n%s</div>'},
		{field='author'               , id='fileinfotpl_aut'                       , tag='wm-license-information-author',         demo=          book, wrapper='<div class="fn value">\n%s</div>'},
		{field='editor'               , id='fileinfotpl_book_editor'               , tag='wm-license-book-editor',                demo=          book, wrapper='<div class="fn value">\n%s</div>'},
		{field='translator'           , id='fileinfotpl_book_translator'           , tag='wm-license-book-translator',            demo=          book, wrapper='<div class="fn value">\n%s</div>'},
		{field='illustrator'          , id='fileinfotpl_book_illustrator'          , tag='wm-license-book-illustrator',           demo=          book, wrapper='<div class="fn value">\n%s</div>'},
		{field='architect'            , id='fileinfotpl_aut'                       , tag='Q42973',                                demo=none,           wrapper='<div class="fn value">\n%s</div>'},
		{field='designer'             , id='fileinfotpl_aut'                       , tag='Q5322166',                              demo=none,           wrapper='<div class="fn value">\n%s</div>'},
		{field='photographer'         , id='fileinfotpl_aut'                       , tag='Q33231',                                demo=    photo,      wrapper='<div class="fn value">\n%s</div>'},
		{field='publisher'            , id='fileinfotpl_book_publisher'            , tag='wm-license-book-publisher',             demo=          book, wrapper='<div class="fn value">\n%s</div>'},
		{field='printer'              , id='fileinfotpl_book_printer'              , tag='wm-license-book-printer',               demo=          book, wrapper='<div class="fn value">\n%s</div>'},
		{field='other_fields_1'},
		-- title & desctiption block
		{field='title'                , id='fileinfotpl_art_title'                 , tag='wm-license-artwork-title',              demo=art+photo+book, wrapper='<div class="fn">\n%s</div>'},
		{field='subtitle'             , id='fileinfotpl_book_subtitle'             , tag='wm-license-book-subtitle',              demo=          book, wrapper='%s'},
		{field='series_title'         , id='fileinfotpl_book_series-title'         , tag='wm-license-book-series-title',          demo=          book, wrapper='%s'},
		{field='volume'               , id='fileinfotpl_book_volume'               , tag='wm-license-book-volume',                demo=          book, wrapper='%s'},
		{field='edition'              , id='fileinfotpl_edition'                   , tag='wm-license-book-edition',               demo=          book, wrapper='%s'},
		{field='object_type'          , id='fileinfotpl_art_object_type'           , tag='object_type',                           demo=art,            wrapper='%s'},
		{field='genre'                , id='fileinfotpl_art_genre'                 , tag='Q483394',                               demo=          book, wrapper='%s'},
		{field='original_description' , id='fileinfotpl_desc'                      , tag='original_description',                  demo=    photo,      wrapper='<div class="description">\n%s</div>'},
		{field='description'          , id='fileinfotpl_desc'                      , tag='wm-license-information-description',    demo=art+photo+book, wrapper='<div class="description">\n%s</div>'},
		{field='pageoverview'         , id='fileinfotpl_book-page-overview'        , tag='wm-license-book-page-overview',         demo=none,           wrapper='%s'},
		{field='depicted_people'      , id='fileinfotpl_art_depicted_people'       , tag='depicted_people',                       demo=art+photo,      wrapper='%s'},
		{field='depicted_place'       , id='fileinfotpl_art_depicted_place'        , tag='depicted_place',                        demo=art+photo,      wrapper='%s'},
		{field='depicted_part'        , id='fileinfotpl_art_depicted_part'         , tag='P5961',                                 demo=art+photo+book, wrapper='%s'},
		{field='language'             , id='fileinfotpl_book_language'             , tag='wm-license-book-language',              demo=          book, wrapper='%s'},
		{field='other_fields_2'},
		-- date, object outside description, history, etc.
		{field='date'                 , id='fileinfotpl_date'                      , tag='wm-license-information-date',           demo=art+photo,      wrapper='%s'},
		{field='publication_date'     , id='fileinfotpl_publication_date'          , tag='P577',                                  demo=          book, wrapper='%s'},
		{field='medium'               , id='fileinfotpl_art_medium'                , tag='wm-license-artwork-medium',             demo=art+photo,      wrapper='%s'},
		{field='dimensions'           , id='fileinfotpl_art_dimensions'            , tag='wm-license-artwork-dimensions',         demo=art+photo,      wrapper='%s'},
		{field='institution'          , id='fileinfotpl_art_gallery'               , tag='Q2668072',                              demo=art+photo,      wrapper='%s'},
		{field='department'           , id='fileinfotpl_art_location'              , tag='wm-license-artwork-current-location',   demo=art+photo     , wrapper='<div class="locality">\n%s</div>'},
		{field='id'                   , id='fileinfotpl_art_id'                    , tag='wm-license-artwork-id',                 demo=art+photo,      wrapper='<div class="identifier">\n%s</div>'},
		{field='coordinates'          , id='fileinfo-paramfield'                   , tag='ObjectLocation',                        demo=art+photo,      wrapper='%s'}, 
		{field='place_of_publication' , id='fileinfotpl_book_place-of-publication' , tag='wm-license-book-place-of-publication',  demo=          book, wrapper='%s'},
		{field='place_of_creation'    , id='fileinfotpl_art_creation_place'        , tag='place_of_creation',                     demo=art,            wrapper='%s'},
		{field='place_of_discovery'   , id='fileinfotpl_art_discovery_place'       , tag='place_of_discovery',                    demo=art,            wrapper='%s'},
		{field='object_history'       , id='fileinfotpl_art_object_history'        , tag='wm-license-artwork-object-history',     demo=art,            wrapper='%s'},
		{field='exhibition_history'   , id='fileinfotpl_art_exhibition_history'    , tag='exhibition_history',                    demo=art+photo,      wrapper='%s'},
		{field='credit_line'          , id='fileinfotpl_art_credit_line'           , tag='wm-license-artwork-credit-line',        demo=art,            wrapper='%s'},
		{field='inscriptions'         , id='fileinfotpl_art_inscriptions'          , tag='wm-license-artwork-inscriptions',       demo=art,            wrapper='%s'},
		{field='notes'                , id='fileinfotpl_art_notes'                 , tag='wm-license-artwork-notes',              demo=art+photo,      wrapper='%s'},
		{field='other_fields_3'},
		-- references, and sources
		{field='references'           , id='fileinfotpl_art_references'            , tag='wm-license-artwork-references',         demo=art+photo+book, wrapper='%s'},
		{field='authority'            , id='fileinfotpl_art_authority'             , tag='Q36524',                                demo=none,           wrapper='%s'},
		{field='source'               , id='fileinfotpl_src'                       , tag='wm-license-artwork-source',             demo=art,            wrapper='%s'}, -- source/photographer
		{field='source_'              , id='fileinfotpl_src'                       , tag='wm-license-information-source',         demo=    photo+book, wrapper='%s'}, -- source
		{field='nosource'             , id='fileinfotpl_nosrc'                     , tag='wm-license-information-source',         demo=none,           wrapper='%s'},
		{field='permission'           , id='fileinfotpl_perm'                      , tag=permission_tag,                          demo=art+photo+book, wrapper='%s'},
		{field='other_versions'       , id='fileinfotpl_ver'                       , tag='wm-license-information-other-versions', demo=art+photo+book, wrapper='%s'}, 
		{field='other_fields'},
		{field='camera_coord'},
	}
	for i=1,#param do
		table.insert(results, Build_html_row(param[i], args))
	end
	
	-- add material on the right: image, wikisource icon, etc.
	if args.image then 
		if args.image_page and args.image  then -- page parameter for DjVu and PDF files
			args.image = string.format('%s|page=%i', args.image, args.image_page)
		end
		if args.infobox=='book' then -- page parameter for DjVu and PDF files
			tag1 = mw.message.new( 'wm-license-book-start-this-book' ):inLanguage(args.lang):plain()
			tag2 = string.format('|thumb|[[:File:%s|%s]]', args.image, tag1)
		else
			tag2 = ''
		end
		local field = string.format('[[File:%s|250x250px|alt=%s|class=photo%s]]', args.image, args.name or '', tag2) 
		local nRow = #results -- number of rows below 
		local line = string.format('<td rowspan="%i" style="width:200px; text-align: right;" id="fileinfotpl_creator_image"><span class="wpImageAnnotatorControl wpImageAnnotatorOff">%s</span></td></tr>\n\n', nRow, field) 
		results[2] = mw.ustring.gsub(results[2], "</tr>%s*$", line); -- attach image section to the right side of the table, by attaching to row #2
	end
	
	-- add table and outer layers
	local style = string.format('class="fileinfotpl-type-artwork toccolours vevent mw-content-%s" dir="%s" style="width: 100%%" cellpadding="4"', dir, dir)
	results = string.format('<table %s>\n%s\n</table>\n', style, table.concat(results)) -- combine "results", an array of strings into a single string
	results = string.format('<div class="hproduct commons-file-information-table">\n%s\n</div>\n', results)
	return results
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 
	
	-- add [[Category:Creator templates with unknown parameter]] category, if some parameter not on the following list is used
	local fields = { 'title', 'object_type', 'description', 'date', 'medium', 'permission', 'translator_id', 
			'artist', 'author', 'architect', 'designer', 'illustrator', 'publisher', 'editor', 'translator', 'printer', 'photographer', 'printer_id',
			'dimensions',  'institution', 'department',  'references', 'object_history', 'artist_id', 'author_id', 'institution_id', 'photographer_id', 'editor_id',
			'exhibition_history', 'credit_line', 'other_versions', 'source', 'strict', 'inscriptions', 'notes', 'linkback', 'camera_coord',
			'other_fields', 'other_fields_1', 'other_fields_2', 'other_fields_3',  'demo', 'id', 'wikidata', 'year', 'homecat', 'authority',
			'place_of_creation', 'place_of_discovery', 'source_', 'wikidata_cat', 'namespace', 'lang', 'image', 'url', 'pagename',
			'depicted_people', 'depicted_place', 'original_description_info', 'original_description', 'biased', 'photo_date', 'infobox',
			'place_of_publication', 'publication_date', 'language', 'subtitle', 'series_title', 'volume', 'edition', 'edition_of',
			'illustrator_id', 'pageoverview', 'wikisource', 'wikiquote', 'demo_image', 'image_page', 'depicted_part', 'mimeType', 'num_pages'}
	local set = {}
	for _, field in ipairs(fields) do set[field] = true end
	for field, _ in pairs( args0 ) do 
		if not set[field] then
			local LUT = {artwork='Artwork', photograph='Photograph', book='Book'}
			local infobox = LUT[args0.infobox]
			cats = cats .. '[[Category:Pages using ' .. infobox .. ' template with incorrect parameter]]'
			cats = cats .. string.format('\n;<span style="color:red">Error in [[Template:%s|{{%s}} template]]: unknown parameter "%s".</span>', infobox, infobox, field)
		end
	end 
	
	if args0.namespace==0 and mw.ustring.sub(args0.pagename,1,8) == "Artwork:" then
		cats = cats .. '\n[[Category:Artwork templates]]'
		if args.homecat then
			cats = cats .. '\n[[Category:' .. args.homecat .. ']]'
		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[[Category:Artworks with known accession number| %s]]', 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[[Category:Book templates]]'
		-- add homecat category
		if args0.homecat~='~' then
			local page = {}
			if args0.homecat then
				cats = string.format('%s\n[[Category:%s| ]]',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[[Category:Book templates without home category]]'
			end
		end
	end
		-- add invisible template tags based on book's date of publication
	if args0.infobox=='book' then
		local pubYear = args.publication_year
		if pubYear then
			local curYear = tonumber(os.date('!*t').year) -- current year
			if pubYear<year-95 then
				mw.getCurrentFrame():expandTemplate{ title ='Books published more than 95 years ago' }
			end
			if pubYear<year-200 then
				mw.getCurrentFrame():expandTemplate{ title ='Works created more than 200 years ago' }
			end
		end
	end
	return cats
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 creator template page              ===
-- ===  * 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 TypeLUT = {['tomb']='Graves', ['funeral chapel']='Graves', ['funeral niche']='Graves', ['painting']='Paintings' }
		local oType = TypeLUT[string.lower(args0.object_type or '')] or LUT[args0.infobox]
		cats = string.format('%s\n[[Category:%s without Wikidata item]]', cats, oType)
		return cats, args1
	end
	
	--=======================================================================================================
	--=== Categories and files with {{Artwork}} template linked to Wikidata item below
	--=======================================================================================================
	-- setup QuickStatements 
	local qsTable = {}  -- table to store QuickStatements 
	-- convert QS table to a string
	local today = '+' .. os.date('!%F') .. 'T00:00:00Z/11' -- today's date in QS format
	-- default QuickStatements command 
	local qsCommand1 = '%s|%s|%s|S143|Q565|S813|' .. today
	local qsCommand2 = '%s|%s|S143|Q565|S813|' .. today

	cats = string.format('%s\n[[Category:%s with Wikidata item|%s]]', cats, LUT[args0.infobox], args0.wikidata)

	-- 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
	local groupItem = false
	if data.object_type_id 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( data.object_type_id ) do
			local v = bwLUT[typeId]
			if v==1 then
				bad = true
			elseif v==2 then
				groupItem = true
				cats = string.format('%s\n[[Category:%s with group Wikidata item|%s]]', cats, LUT[args0.infobox], args0.wikidata)
				break			
			elseif v==3 then
				bad = false
				break
			end 
		end
		if bad then
			cats = string.format('%s\n[[Category:%s with wrong Wikidata item|%s]]', cats, LUT[args0.infobox], 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], '%<div style="display: none;"%>Unknown '..field..'<%/div%>') then
			comp[field] = 'missing'
		end
	end
	if not data.id and args0.id then
		comp['accession number'] = 'missing'
		local id = nowiki(args0.id) -- strip any links that might be there
		if args0.institution_id and #id<20 then
			table.insert( qsTable, string.format(qsCommand1, args0.wikidata, 'P217', '"' .. id.. '"|P195|'..args0.institution_id) )
		end
	end
	
	if comp.artist=='missing' and string.match(args0.artist, '%<div style="display: none;"%>Unknown a%w+%<%/div%>') then 
		comp.artist = nil -- ignore {{Unknown|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
	if (not data.image and args0.image ) then -- QS code to help transfer image to Wikidata
		local ext = string.lower(string.sub(args0.image,-4))
		if (ext=='.pdf' or ext =='djvu') and args0.image_page  then
			table.insert( qsTable, string.format(qsCommand1, args0.wikidata, 'P996', '"' .. args0.image.. '"|P4714|'.. tostring(args0.image_page)) )
		elseif (ext=='.pdf' or ext =='djvu') then 
			table.insert( qsTable, string.format(qsCommand1, args0.wikidata, 'P996', '"' .. args0.image.. '"') )
		else 
			table.insert( qsTable, string.format(qsCommand1, args0.wikidata, 'P18', '"' .. args0.image.. '"') )
		end	
		comp.image = 'missing'
	elseif (not data.image and args0.namespace==6 and args0.num_pages>1 and (args0.mimeType=='application/pdf' or args0.mimeType=='image/vnd.djvu')) then -- QS code to help transfer image to Wikidata
		if args0.image_page then 
			table.insert( qsTable, string.format(qsCommand1, args0.wikidata, 'P996', '"' .. args0.pagename.. '"|P4714|'.. tostring(args0.image_page)) )
		else
			table.insert( qsTable, string.format(qsCommand1, args0.wikidata, 'P996', '"' .. args0.pagename.. '"') )
		end
		comp.image = 'missing'
	elseif (not data.image and args0.namespace==6 and args0.num_pages==1 and args0.infobox=='artwork') then -- QS code to help transfer image to Wikidata
		table.insert( qsTable, string.format(qsCommand1, args0.wikidata, 'P18', '"' .. args0.pagename.. '"') )
		comp.image = 'missing'	
	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 string.find(args0['date'], '%<div style="display: none;"%>era QS:([^%<]+)%</div%>') 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 currectly in some templates like institution in book template and are not alowed to create QS
	local fields = {object_type='artwork', era='artwork', medium='artwork', dimensions='artwork', institution='artwork', artist='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 pat = '%<div style="display: none;"%>'..field..' QS:([^%<]+)%</div%>'
			local qs  = string.match(args0[field], pat) -- find hidden tag with QS code
			local _, nMatch = string.gsub(args0[field], pat, "") -- count matches
			if qs and nMatch==1 then -- allow only single matches
				qs = string.gsub(qs, ',', '|')
				for _, v in ipairs( mw.text.split( qs, ';', true ) ) do
					table.insert( qsTable, string.format(qsCommand2, args0.wikidata, v) )
				end
			end
		end
	end
	
	-- add "Commons Category" (P373) if template at category page
	if not data.homecat and args0.namespace==14 then 
		table.insert( qsTable, string.format(qsCommand1, args0.wikidata, 'P373', '"' .. args0.pagename.. '"' ))
	end
	
	-- Special case of QS codes for multilingual labels extracted from {{title}} and {{description}} templates, etc.
	if args0.title then
		local max_title_len = 100
		-- strip titles from title field with {{title}} template: {{title|lang=...|1=...}}
		local pat = '%<div style="display: none;"%>title QS:P1476,((%w+):[^%<]+)%</div%>'
		for qs, lang in mw.ustring.gmatch(args0.title, pat) do
			qs = nowiki_title(qs)
			if not data.title_[lang] and #qs<max_title_len then
				table.insert( qsTable, string.format(qsCommand1, args0.wikidata, 'P1476', qs) )
			end
		end
		-- strip titles from title field with {{title}} templates, with fields like {{en}}, {{es}}, etc.
		pat = '%<div style="display: none;"%>label QS:L(%w+),([^%<]+)%</div%>'
		for lang, qs in mw.ustring.gmatch(args0.title, pat) do
			qs = nowiki_title(qs)
			if not data.labels[lang] and #qs<max_title_len then
				table.insert( qsTable, string.format('%s|%s|%s', args0.wikidata, 'L'..lang, qs) )
			end
		end	
		-- strip titles from title field with {{description}} templates, like {{en}}, {{es}}, etc.
		-- <div class="description mw-content-ltr fr" dir="ltr" lang="fr" style=""><span class="language fr" title="Français">'''Français&nbsp;:'''</span> ''Les bateaux rouges, Argenteuil''</div>
		--pat = '%<div class="description mw-content-[rtl]* %w+" dir="[rtl]*" lang="(%w+)" style=""%>%<span class[^%<]+%</span%> *([^%<]+)%</div%>'
		pat = '%<span class="language (%w+)" [^%<]+%</span%> *([^%<]+)%</div%>'
		for lang, qs in mw.ustring.gmatch(args0.title, pat) do
			qs = nowiki_title(qs)
			if not data.labels[lang] and #qs<max_title_len then
				table.insert( qsTable, string.format('%s|%s|"%s"', args0.wikidata, 'L'..lang, qs) )
			end
		end	
	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[[Category:Artworks with accession number from Wikidata| %s]]', cats, args0.wikidata)
		cats = string.format('%s\n[[Category:Artworks with known accession number| %s]]', cats, data.id_id or 'zzz')
	end

	-- ==================================================
	-- === Create categories based on comp structure ==== 
	-- ==================================================
	for field, outcome in pairs( comp ) do
		cats = string.format('%s\n[[Category:%s with Wikidata item %s %s|%s]]', cats, LUT[args0.infobox], outcome, field, args0.wikidata)
	end
	
	-- ==================================================
	-- === Create QuickStatement codes ================== 
	-- ==================================================
	local QS   = ''     -- quick_statements final string
	if #qsTable>0 and not groupItem then -- do not use QS on group items
		local qsHeader  = 'https://tools.wmflabs.org/quickstatements/index_old.html#v1=%s%%0A'
		local qsWrapper = '&nbsp;[[File:Commons_to_Wikidata_QuickStatements.svg|15px|link=%s]]'
		QS = table.concat( qsTable, '%0A')    -- combine multiple statements into a single command separated by \n (line feed)
		QS = mw.ustring.gsub(QS, '|', "%%09") -- replace some characters with ASCII hex values
		QS = mw.ustring.gsub(QS, '"', "%%22") -- "%%" instead of "%" is because mw.ustring.gsub is a regexp function
		QS = mw.ustring.gsub(QS, ' ', "%%20")
		QS = string.format(qsHeader , QS);    -- create full URL link
		QS = string.format(qsWrapper, QS)     -- use URL as a link accessed by clicking Wikidata icon
		cats = string.format('%s\n[[Category:%s with Wikidata item: quick statements]]', cats, LUT[args0.infobox])
	end
	args1.QS = QS;
	
	return cats, args1
end

-- ===========================================================================
-- === Harvest wikidata properties matching creator template fields        ===
-- === INPUTS:                                                             ===
-- ===  * itemID - item id or a q-code                                     ===
-- ===  * lang  - language id of the desired language                      ===
-- ===  * namespace - namespace number of the page calling the module      ===
-- ===========================================================================
local function harvest_wikidata(itemID, lang, namespace, infobox)
	local data = {} -- structure similar to "args" but filled with wikidata data
	local cats = ''
	local frame = mw.getCurrentFrame()
	local entity = nil
	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('[[Category:%s with bad Wikidata link]]', LUT[infobox])
		elseif entity.id~=itemID then
			 cats = string.format('[[Category:%s with redirected Wikidata link]]', 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
	data.date, data.date_, data.year = d.str, d.iso, d.year

	-- publication date: translated date and year number
	local d = getDate(entity, 'P577' , lang) -- publication date
	data.publication_date, data.publication_date_, data.publication_year = d.str, d.iso, d.year
	
	-- harvest string properties
	local Debug ={}
	local property = {P10='video', P18='image', P996='scan', P4896='model3D', P373='homecat', 
		P2093='authorStr', P393='edition', P4714='image_page', P1957='wikisource_index'}
	for prop, field in pairs( property ) do
		data[field] = getProperty(entity, prop, 'one')
	end
	data.image = data.image or data.scan or data.video or data.model3D
	data.image_page = tonumber(data.image_page)
	
	-- harvest Q-code properties which are than converted from Q-number to labels
	local property = { P189='place_of_discovery', P2079='technique', P123='publisher',  P872='printer', 
	                   P136='genre', P921='subject', P179='series_title', P291='place_of_publication', P407='language', 
					   P629='edition_of'}
	for prop, field in pairs( property ) do
		local id = getProperty(entity, prop, 'one')
		if id then 
			data[field] = getLabel(id, lang, "wikipedia")
		end
	end

	-- get era
	data.era_id = getProperty(entity, 'P2348', 'all')
	if data.era_id then
		local periodSpan = require('Module:Period')._periodSpan
		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, " / ")
	end
	if data.era and data.date then
		data.date = data.date .. "<br/>" .. data.era
	elseif data.era and not data.date then
		data.date = data.era
	end
	
	-- get object_type
	data.object_type_id = getProperty(entity, 'P31', 'all')
	if data.object_type_id then 
		local T = {}
		for _, id in ipairs( data.object_type_id ) do
			table.insert(T, getLabel(id, lang))
		end
		data.object_type = table.concat(T, " / ")
	end

	-- get author and/or author creator template
	local property = { P170='creator', P50='author', P84='architect', P287='designer', P98='editor', P655='translator', P110='illustrator' }   
	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'}
	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] = 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.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 prefered language so grab any label
		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', 'one') then
		local coorFun = require('Module:Coordinates')._LocationTemplateCore
		local coori18n = require('Module:i18n/coordinates')
		labels.ObjectLocation = coori18n.ObjectLocation
		data.coordinates = coorFun({wikidata=entity, lang=lang, globe='earth', namespace=namespace, 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.depicted_people    = Art.get_depicted_people(entity, lang)
	local 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, title_label = nil, nil, nil
	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  
		title_label = data.title or data.label -- create name based on wikidata label
	end

	-- get author wikidata ID based on commons
	if not creator_id then
		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
	end
	
	-- convert wikidata ID to a label
	if creator_id and creator_id:match("^Q%d+$") then
		creator_label = getLabel(creator_id, args0.lang)
	elseif creator_id then
		-- if author was "{{Creator:Meister Theoderich von Prag|circle of}}" than creator_id will be "Q4233718,P1776,Q446631"
		local res = {creator_id:match("^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		
		else 	
			creator_label = creator_id		
		end		
	end
		
	-- get title based on commons
	--if not creator_label and (args0.artist or args0.author) then
	--	creator_label = nowiki(args0.artist or args0.author) 
	--end	
	
	-- get title based on commons
	if not title_label and args0.title then
		title_label = nowiki(args0.title) 
	end
	--title_label = title_label or '(unknown title)'
	--args1.source_ = title_label
	
	-- 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 {{book}} template in file namespace and file of in PDF or DjVu than use it as image
	if args0.infobox=='book' and  args0.namespace==6 then
		if not args1.image and (args0.mimeType=='application/pdf' or args0.mimeType=='image/vnd.djvu')  then
			args1.image = args0.pagename
		end
	end

	return args1
end

-- ===========================================================================
-- === Read input "frame", normalize input parameters (lower case, etc.)   ===
-- === and resolve potential aliases                                       ===
-- === INPUTS:                                                             ===
-- ===  * frame - contains imput parameters passed from the template       ===
-- === OUTPUTS:                                                            ===
-- ===  * args - cleaned up inputs                                         ===
-- ===========================================================================
local function read_input_parameters(frame)
	-- switch to lowercase parameters to make them case independent
	local args = {}
	for name, value in pairs( frame:getParent().args ) do 
		if value ~= '' then -- nuke empty strings
			local name1 = string.gsub( string.lower(name), ' ', '_')
			args[name1] = value
		end
	end
	for name, value in pairs( frame.args ) do 
		if value ~= '' then -- nuke empty strings
			local name1 = string.gsub( string.lower(name), ' ', '_')
			args[name1] = value
		end
	end
	
	if not (args.lang and mw.language.isSupportedLanguage(args.lang)) then 
		args.lang = frame:callParserFunction( "int", "lang" ) -- get user's chosen language
	end
	if args.language and #args.language==2 then 
		args.language = frame:callParserFunction( "#language", { args.language, args.lang } ) -- get  language of the written work
	end
	local page = mw.title.getCurrentTitle()
	args.namespace   = page.namespace   -- get page namespace
	args.url         = page:canonicalUrl()
	args.pagename    = page.text
	if args.namespace==6 then
		args.mimeType  = page.file.mimeType
		args.num_pages = 1
		if page.file.pages then
			args.num_pages = #page.file.pages -- in case of DjVu or PDF files count pages
		end
	end
	args.image_page = tonumber(args.image_page)
	
	-- resolve aliases
	args.permission  = args.permission or args.license
	args.medium      = args.medium or args.technique
	args.date        = args.date or args.year
	args.dimensions  = args.dimensions or args.size
	args.institution = args.institution or args.gallery or args.museum
	args.department  = args.department or args.location
	args.id          = args.accession_number or args.id
	args.object_type = args.object_type or args.type
	args.place_of_creation = args.place_of_creation or args.place_of_origin or args.country
	args.object_history = args.object_history or args.history
	args.technique, args.year, args.size,   args.gallery           = nil, nil, nil, nil
	args.location,  args.type, args.museum, args.accession_number  = nil, nil, nil, nil
	args.place_of_origin, args.country, args.history, args.license = nil, nil, nil, nil
	args.wikidata_cat = yesno(args.wikidata_cat, true)
	args.strict       = yesno(args.strict, true)
	return args
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 = ''          -- categories 
	local str, data
	
	-- ===========================================================================
	-- === Step 1: clean up of template arguments "args0"
	-- ===========================================================================
	if args0.date then 
		args0.year = empty2nil(ISOdate._ISOyear(args0.date)) -- get year
	end
	
	-- for places run them through {{City}} template
	local fields = { 'depicted_people', 'depicted_place', 'place_of_discovery'	}
	for _, field in ipairs( fields ) do 
		if args0[field] and not string.find(args0[field], ' ') then
			args0[field] = City._city(args0[field], lang) -- single word depicted_people will get a link
		end	
	end
	
	-- for tates run them through {{ISOdate}} template and add invisible QS tag if possible
	local fields = { 'date', 'publication_date'}
	for _, field in ipairs( fields ) do 
		if args0[field] then 
			local val  = isodate2timestamp(args0[field])        -- if date is in YYYY, YYYY-MM or YYYY-MM-DD formats than it will be saved
			args0[field] = ISOdate._ISOdate(args0[field], lang) -- apply ISODate to function to date string to convert date in ISO format to translated date string 
			if val then                                         -- if date is in ISO format than add an invisible tag which will be used to potentially add this date to QS used to move it to Wikidata
				args0[field] = string.format('%s<div style="display: none;">date QS:P,%s</div>', args0[field], val)
			end		
		end
	end
	
	-- collapse local {{Creator}} and {{Institution}} templates and extract item ID from them 
	local fields = {author='creator', artist='creator', photographer='creator', architect='creator', printer='creator', 
		designer='creator', editor='creator', translator='creator', illustrator='creator', institution='institution'}
	for field, keyword in pairs( fields ) do 
		if args0[field] then 
			args0[field] = mw.ustring.gsub (args0[field], 'table class="toccolours collapsible%s*"', 'table class="toccolours collapsible collapsed"')
			local pat = '%<div style="display: none;"%>'.. keyword ..' QS:P%d+,([^%<]+)%</div%>'
			--local pat = '%<div style="display: none;"%>'.. keyword ..' QS:P%d+,(Q%d+)%</div%>'
			local _, nMatch = string.gsub(args0[field], pat, "")
			if nMatch==1 then -- if multiple templates than skip
				args0[field..'_id'] = mw.ustring.match(args0[field], pat)
				args0[field]        = string.gsub(args0[field], '%<div style="display: none;"%>'.. keyword ..' QS:P', '<div style="display: none;">'.. field ..' QS:P')
			end
		end
	end
	
	-- in case of invisible QS tags add correct property based on which field and infobox it come from 
	local property = { author='book|creator QS:P170,|author QS:P50,', illustrator='book|creator QS:P170,|illustrator QS:P110,', editor='book|creator QS:P170,|editor QS:P98,', 
		translator='book|creator QS:P170,|translator QS:P655,', printer='book|creator QS:P170,|printer QS:P872,', publication_date='book|date QS:P,|publication_date QS:P577,', 
		date='artwork|date QS:P,|date QS:P571,'}   
	for field, str in pairs( property ) do
		local part = mw.text.split( str, '|' )
		if args0[field] and args0.infobox==part[1] then 	
			args0[field] = string.gsub(args0[field], part[2], part[3]) 
		end
	end
	
	-- ===========================================================================
	-- === Step 2: one by one merge wikidata and creator data
	-- ===========================================================================
	data, cats = harvest_wikidata(args0.wikidata, lang, args0.namespace, args0.infobox)
	-- 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'		
	}
	for _, field in ipairs( fields ) do 
		args[field] = args0[field] or data[field]
	end
	-- copy fields only defined locally
	local fields = { 'wikidata', 'description', 'original_description_info', 'original_description', 'biased', 'camera_coord',
		'other_versions', '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', 'depicted_part'
	}
	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 then
		args.author, args.author_id = nil, nil; -- if artist and author are the same than drop one
		cats = cats .. '[[Category:Artworks with the same artist and author]]\n'
	end
	if args0.infobox=='artwork' and args0.photo_date and args0.photographer then
		cats = cats .. '[[Category:Artworks with photograph information]]\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
	
	-- 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)
	-- If institution namespace than add maintenance categories
	args.QS = nil;
	str, args = add_wikidata_maintenance_categories(args0, args, data)
	cats = cats .. str

	local results = build_html(args, cats)
	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 = read_input_parameters(frame)
	args.infobox = 'artwork'
	args.demo_image = 'Noun project - Mona Lisa - in frame.svg' 
	local results, cats = p.create_infobox(args) -- call the inner "core" function	 
	return results .. cats
end

function p.photograph(frame)
	local args = read_input_parameters(frame)
	args.source_ = args.source or args.source_
	args.source  = nil
	args.infobox = 'photograph'
	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 do not botter
	cats = mw.ustring.gsub(cats,'%\n%[%[Category:Photographs without Wikidata item%]%]','')
	return results .. cats
end

function p.art_photo(frame)
	local args = 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 = {}
	local frame = mw.getCurrentFrame()
	
	-- split input arguments
	local args2 = {}	
	local fields = {['date']='photo_date', description='photo_description', medium='photo_medium', dimensions='photo_dimensions', 
		institution='photo_institution', department='photo_department', id='photo_accession_number', source='source', photographer='photographer', 
		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', 'namespace', 'url', 'pagename'} ) do 
		args2[field] = args[field]
	end
	args.permission = args.artwork_license
	args.artwork_license = nil

	-- create object infobox
	local header = frame:expandTemplate{ title = 'Section header', args = { ["1"]=args.artwork_header or 'Object', lang=args.lang } }
	table.insert(results, "===" .. header .. "===")
	args.infobox = 'artwork'
	args.demo_image = 'Noun project - Mona Lisa - in frame.svg' 
	args.strict  = false
	local infobox, cats1 = p.create_infobox(args) -- call the inner "core" function	
	table.insert(results, infobox)
	
	--create photograph infobox
	header = frame:expandTemplate{ title = 'Section header', args = { ["1"]='Photograph', lang=args.lang } }
	table.insert(results, "===" .. header .. "===")
	args2.infobox = 'photograph'
	args2.demo_image = 'Breezeicons-actions-22-view-preview.svg' 
	args2.wikidata_cat = false
	local infobox, cats2 = p.create_infobox(args2) -- call the inner "core" function	 
	table.insert(results, infobox)
	table.insert(results, cats1)
	table.insert(results, cats2)
 
	return table.concat(results, '\n')
end

function p.book(frame)
	local args = 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
		args.authority = frame:expandTemplate{ title = 'Book authority control', args = { ISBN=args.isbn, LCCN=args.lccn, OCLC=args.oclc, BNF=args.bnf, bare=1 } }
		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