Module:Information

    From Commons

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

    --[[  
      __  __           _       _        ___        __                            _   _             
     |  \/  | ___   __| |_   _| | ___ _|_ _|_ __  / _| ___  _ __ _ __ ___   __ _| |_(_) ___  _ __  
     | |\/| |/ _ \ / _` | | | | |/ _ (_)| || '_ \| |_ / _ \| '__| '_ ` _ \ / _` | __| |/ _ \| '_ \ 
     | |  | | (_) | (_| | |_| | |  __/_ | || | | |  _| (_) | |  | | | | | | (_| | |_| | (_) | | | |
     |_|  |_|\___/ \__,_|\__,_|_|\___(_)___|_| |_|_|  \___/|_|  |_| |_| |_|\__,_|\__|_|\___/|_| |_|
                                                                                                   
    This module is intended to be the engine behind "Template:Information".
    
    Please do not modify this code without applying the changes first at
    "Module:Information/sandbox" and testing at "Module:Information/testcases".
    
    Authors and maintainers:
    * User:Jarekt - original version 
    ]]
    -- =======================================
    -- === Dependencies ======================
    -- =======================================
    require('Module:No globals') -- used for debugging purposes as it detects cases of unintended global variables
    local ISOdate = require('Module:ISOdate')._ISOdate -- date localization
    local core    = require('Module:Core')
    
    -- ==================================================
    -- === Internal functions ===========================
    -- ==================================================
    
    local function langWrapper(text, textLang) 
    -- code equivalent to https://commons.wikimedia.org/wiki/Template:Description
    	local language = mw.language.new( textLang )
    	local dir      = language:getDir()  
    	local LangName = language:ucfirst(mw.language.fetchLanguageName( textLang, textLang))
    	local str = mw.ustring.format('<span class="language %s"><b>%s:</b></span>', textLang, LangName)
        return mw.ustring.format('<div class="description mw-content-%s" dir="%s" lang="%s">%s %s</div>', dir, dir, textLang, str, text)
    end
    
    -------------------------------------------------------------------------------
    local function message(name, lang)
    	return mw.message.new( 'wm-license-information-'..name ):inLanguage(lang):plain()
    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)
    	local lang = args.lang -- user's language
    	local dir  = mw.language.new( lang ):getDir()    -- get text direction
    	local desTag = mw.ustring.format('<span class="summary fn" style="display:none">%s</span>', args.pagename)
    	local prmTag = mw.ustring.format("<br /><small>([[%s|%s]])</small>", message('permission-reusing-link', lang), 
    								 message('permission-reusing-text', lang))
    
    	-- field specific preferences
    	local params = {
    		{field='description'    , id='fileinfotpl_desc', tag2=desTag, td='class="description"'},
    		{field='other_fields_1'},
    		{field='date'           , id='fileinfotpl_date', td=mw.ustring.format('lang="%s"', lang)},
    		{field='source'         , id='fileinfotpl_src'}, 
    		{field='author'         , id='fileinfotpl_aut'},
    		{field='permission'     , id='fileinfotpl_perm', tag2=prmTag },
    		{field='other_versions' , id='fileinfotpl_ver',  tag='other-versions'}, 
    		{field='other_fields'},
    	}
    	local results = {}
    	for _, param in ipairs(params) do
    		local field, tag, cell1, cell2, id
    		field = args[param.field]
    		if param.id then -- skip "other fields" parameter
    			if type(field) == 'string' then  -- add "id" to first <td> cell only if the field is present
    				id = mw.ustring.format('id="%s" ', param.id)
    			elseif type(field) == 'table' then
    				-- the field was initially not present, it contains only our
    				-- warning text; flatten it so that mw.ustring.format() gets a string
    				field = field.missing
    			end
    			if field or (args.demo and param.tag) then  -- skip the row if still no field
    				tag   = message(param.tag or param.field, lang) .. (param.tag2 or '')
    				cell1 = mw.ustring.format('<td %sclass="fileinfo-paramfield" lang="%s">%s</td>\n', id or '', lang, tag)
    				cell2 = mw.ustring.format('<td %s>\n%s</td>', param.td or '', field or '')
    				field = mw.ustring.format('<tr style="vertical-align: top">\n%s%s\n</tr>\n\n', cell1, cell2)
    			end
    		end
    		table.insert(results, field)
    	end
    
    	-- add table and outer layers
    	local style = mw.ustring.format('class="fileinfotpl-type-information toccolours vevent '..
    		'mw-content-%s" style="width: 100%%; direction: %s;" cellpadding="4"', dir, dir)
    	results = mw.ustring.format('<table %s>\n\n%s\n</table>\n', style, table.concat(results))
    	results = mw.ustring.format('<div class="hproduct commons-file-information-table">\n%s\n</div>', results)
    	return results
    end
    
    -- ==================================================
    -- === External functions ===========================
    -- ==================================================
    local p = {}
    
    -- ===========================================================================
    -- === Version of the function to be called from other LUA codes
    -- ===========================================================================
    
    -------------------------------------------------------------------------------
    -- _information function creates a wikicode for {{Information}} template based on
    -- passed arguments (through "args") and data extracted from SDC. Allowed fields of 
    -- "args" are : 'description', 'date', 'permission', 'author', 'other_versions',  
    -- 'source','other_fields', 'other_fields_1', 'demo' and 'lang'
    -------------------------------------------------------------------------------
    -- Dependencies: p._SDC_Description, p._SDC_Source, p._SDC_Author, p._SDC_Date, 
    --    Build_html, Module:ISOdate (_date)
    -------------------------------------------------------------------------------
    function p._information(args)
    
    	local cats = ''
    	
    	-- ============================================================================================
    	-- === add [[Category:Pages using Information template with incorrect parameter]] if needed ===
    	-- ============================================================================================
    	local page = mw.title.getCurrentTitle()
    	local lang = args.lang
    	local namespace = page.namespace   -- get page namespace
    	if namespace==6 or namespace==10 then
    		local allowedFields = {'description', 'date', 'permission', 'author', 'other_versions',  
    				'source','other_fields', 'other_fields_1', 'demo', 'lang'}
    		local set, badField = {}, {}
    		for _, field in ipairs(allowedFields) do set[field] = true end
    		for field, _ in pairs( args ) do 
    			if not set[field] then
    				table.insert(badField, field)
    			end
    		end
    		if #badField>0 then
    			cats = mw.ustring.format('\n;<span style="color:red">Error in [[Template:Information|{{Information}}'..
    				' template]]: unknown parameter "%s".</span>',  table.concat(badField,'", "'))
    			cats = cats .. '\n[[Category:Pages using Information template with incorrect parameter]]'
    		end
    	end
    	if args.date then
    		-- apply ISODate to function to date string to convert date in ISO format to translated date string
    		args.date = ISOdate(args.date, lang, '', 'dtstart', '100-999')      
    	end 
    	args.pagename = page.text
    
    
    	-- ====================================================
    	-- === harvest structured data                      === 
    	-- ====================================================
    	local entity = mw.wikibase.getEntity()
    	if namespace==6 and entity then -- file namespace
    		-- call SDC functions only when needed
    		local icon = true
    		-- local field is missing -> get it from SDC
    		args.description = args.description or p._SDC_Description(entity, lang, icon)  
    		args.source      = args.source      or p._SDC_Source(entity, lang, icon)
    		args.author      = args.author      or p._SDC_Author(entity, lang, icon) 
    		args.date        = args.date        or p._SDC_Date(entity, lang, icon)
    	end
    	
    	-- ====================================================
    	-- === add tracking templates and categories        === 
    	-- ====================================================
    	-- add the template tag (all official infoboxes transclude {{Infobox template tag}} so files without that tag do not have an infobox
    	mw.getCurrentFrame():expandTemplate{ title = 'Infobox template tag' } 
    
    	-- files are required to have at least the 3 fields below
    	local reqFields = {description='Media lacking a description', author='Media lacking author information', source='Images without source'}
    	for field, errCat in pairs(reqFields) do 
    		if args[field] and mw.ustring.match(args[field],"^[%s%p]+$") then 
    			args[field]=nil; 
    		end -- ignore punctuation only fields
    		if not args[field] then
    			-- code equivalent to Template:Source missing, Template:Author missing, Template:Description missing
    			local tag1 = 'class="boilerplate metadata" id="cleanup" style="text-align: center; background: #ffe; '..
    				'margin: .75em 15%; padding: .5em; border: 1px solid #e3e3b0;'
    			local tag2 = message(field..'-missing', lang)
    			local tag3 = message(field..'-missing-request', lang)
    			local dir  = mw.language.new( lang ):getDir()    -- get text direction
    			args[field] = {missing =  mw.ustring.format('<div %s direction: %s;" lang="%s">%s\n%s\n</div>', tag1, dir, lang, tag2, tag3)}
    			cats = cats .. '\n[[Category:'.. errCat ..']]'
    		end
    	end
    	if namespace~=6  then 
    		cats = '' -- categories are added only to files
    	end
    
    	return Build_html(args) .. cats
    end
    
    -------------------------------------------------------------------------------
    -- interface for other Lua codes to 5 functions for extracting description, source,
    -- author, date and location information from SDC. 
    -- INPUTS:
    --  - "entity" - structure created by mw.wikibase.getEntity function
    --  - "lang"  - users language
    -------------------------------------------------------------------------------
    -- Dependencies: langWrapper
    -------------------------------------------------------------------------------
    function p._SDC_Description(entity, lang, icon)
    	-- create {{en|1=...}} template with SDC's caption
    	local description, _
    	if entity and entity.labels then -- get label in users language or one of that language fallback list
    		local label = core.langSwitch(entity.labels, lang) 
    		local labels, D = {}, {}
    		if label then -- show either matching language
    			labels[lang] = label
    		else -- or if missing then show all
    			labels = entity.labels
    		end
    		for _, label in pairs(labels) do -- add {{en|1=....}} like wrapper
    			if icon and #D==0 then -- add editAtSDC icon to the first description
    				label.value = label.value .. core.editAtSDC('ooui-php-4', lang)
    			end
    			table.insert(D, langWrapper(label.value, label.language, lang))
    		end 
    		description = table.concat(D, '\n') 
    	end
    	return description
    end
    
    -------------------------------------------------------------------------------
    -- Dependencies: Module:Wikidata_date "_date" function, Module:ISOdate "_ISOdate" function
    -------------------------------------------------------------------------------
    function p._SDC_Date(entity, lang, icon)
    	-- get creation date from  P571 (inception)
    	-- Code can handle YYYY-MM-DD, YYYY-MM, and YYYY dates without any additional resources
    	-- But can load [[Module:Wikidata date]] if needed
    	local Date
    	if entity and entity.claims and entity.claims.P571 then
    		local snak = entity.claims.P571[1].mainsnak
    		if (snak.snaktype == "value") then 
    			local v = snak.datavalue.value
    			if v and (v.calendarmodel=='http://www.wikidata.org/entity/Q1985727') and (mw.ustring.sub(v.time,1,1)=='+') then 
    				if v.precision >= 11 then            -- day
    					Date = mw.ustring.sub(v.time,2,11)   -- date in YYYY-MM-DD format
    				elseif v.precision == 10 then        -- month
    					Date = mw.ustring.sub(v.time,2,8)    -- date in YYYY-MM format
    				elseif v.precision == 9 then         -- year
    					Date = mw.ustring.sub(v.time,2,5)    -- date in YYYY format
    				end
    				if Date then -- translate
    					Date = ISOdate(Date, lang, '', 'dtstart', '100-999')
    				end
    			end
    		end
    		if entity.claims.P571[1].qualifiers then                  -- non-trivial case: call heavy cavalery
    			local getDate = require("Module:Wikidata date")._date -- lazy loading: load only if needed
    			local result  = getDate(entity, 'P571', lang)         -- display the date in user's language
    			Date = result.str
    		end
    	end
    	if icon and Date then
    		Date = Date .. core.editAtSDC('P571', lang)
    	end
    	return Date
    end
    
    -------------------------------------------------------------------------------
    -- Dependencies: none
    -------------------------------------------------------------------------------
    function p._SDC_Source(entity, lang, icon)
    	-- get source from P7482 (source of file)
    	-- Code can handle {{Own}} template and URLs 
    	local source, label
    	if entity and entity.claims and entity.claims.P7482 then
    		local statement = entity.claims.P7482[1]
    		-- get URL is source  is " file available on the internet (Q74228490) " 
    		if statement.mainsnak.datavalue.value.id=='Q74228490' and statement.qualifiers and statement.qualifiers.P973 then
    			source = statement.qualifiers.P973[1].datavalue.value -- described at URL
    			if statement.qualifiers.P137 then -- "operator"
    				local id = statement.qualifiers.P137[1].datavalue.value.id
    				label = mw.wikibase.getLabelByLang(id, lang)
    				source  = '[' .. source ..' ' .. label ..']'
    			end
    		end
    		-- add {{tl|own}} if source is "original creation by uploader (Q66458942)" 
    		if statement.mainsnak.datavalue.value.id=='Q66458942' then
    			label = mw.message.new( 'Wm-license-own-work'):inLanguage(lang):plain()
    			source = mw.ustring.format('<span class="int-own-work" lang="%s">%s</span>',lang, label)
    		end
    		-- add {{tl|Own work by the original uploader}} if source is " Own work by the original uploader (Q87402110)" 
    		if statement.mainsnak.datavalue.value.id=='Q87402110' then
    			label  = mw.wikibase.getLabelByLang('Q87402110', lang)
    			source = mw.ustring.format('<span class="int-own-work" lang="%s">%s</span>',lang, label)
    		end
    	end
    	if icon and source then
    		source = source .. core.editAtSDC('P7482', lang)
    	end
    	return source
    end
    
    -------------------------------------------------------------------------------
    -- Dependencies: Module:Core "getLabel" function
    -------------------------------------------------------------------------------
    function p._SDC_Author(entity, lang, icon)
    	-- get author from P170 (creator)
    	-- Code can handle usuall cases of "[[User:Example|Example]]" as well as users with Wikidata Item IDs
    	local author
    	if entity and entity.claims and entity.claims.P170 then
    		local creators = {}
    		for _,statement in ipairs(entity.claims.P170) do
    			if statement.mainsnak.snaktype == "value" then  -- Creator has item ID    
    				local val = statement.mainsnak.datavalue.value.id
    				table.insert(creators, core.getLabel(val, lang))
    			elseif statement.mainsnak.snaktype == "somevalue" then -- Creator defined by username
    				if statement.qualifiers then -- author name string (P2093) 
    				    local qual = {}
    					local properties = {P2093='authorStr', P4174='username', P3831='role', P2699='url'}
    					for prop, field in pairs( properties ) do
    						if statement.qualifiers[prop] then
    							qual[field] = statement.qualifiers[prop][1].datavalue.value
    						end
    					end
    					local role = ''
    					if qual.role and entity.claims.P170[2] then -- add role only is multiple creators
    						role = '&nbsp;(' .. core.getLabel(qual.role.id, lang) .. ')'
    					end
    					if qual.username and qual.authorStr then         --    author name string (P2093) & Wikimedia username (P4174) 
    						table.insert(creators, '[[User:'..qual.username..'|'..qual.authorStr..']]'..role)
    					elseif qual.username and not qual.authorStr then -- no author name string (P2093) & Wikimedia username (P4174) 
    						table.insert(creators, '[[User:'..qual.username..'|'..qual.username..']]'..role)
    					elseif qual.url and qual.authorStr then          --    author name string (P2093) & URL (P2699) 
    						table.insert(creators, '['..qual.url..' '..qual.authorStr..']'..role)
    					elseif qual.url and not qual.authorStr then      -- no author name string (P2093) & URL (P2699) 
    						table.insert(creators, qual.url..role)
    					elseif  qual.authorStr then                      --    author name string (P2093) 
    						table.insert(creators, qual.authorStr..role)
    					end
    				end
    			end
    		end -- end for
    		author = table.concat(creators, ', ')
    	end
    	if icon and author then
    		author = author .. core.editAtSDC('P170', lang)
    	end
    	return author
    end
    
    -------------------------------------------------------------------------------
    -- Dependencies: Module:Code "getLabel" function
    -------------------------------------------------------------------------------
    function p._SDC_Location(entity, lang, icon)
    	-- get location  P276 (location)
    	local location, prop
    	if entity and entity.claims and entity.claims.P1071 then
    		local snak = entity.claims.P1071[1].mainsnak
    		if (snak.snaktype == "value") then 
    			location = core.getLabel(snak.datavalue.value.id, lang)
    			prop = 'P1071'
    		end
    	end
    	if entity and entity.claims and entity.claims.P276 then
    		local snak = entity.claims.P276[1].mainsnak
    		if (snak.snaktype == "value") then 
    			location = core.getLabel(snak.datavalue.value.id, lang)
    			prop = 'P276'
    		end
    	end
    	if icon and location then
    		location = location .. core.editAtSDC(prop, lang)
    	end
    	return location
    end
    
    -- ===========================================================================
    -- === Version of the functions to be called from template namespace
    -- ===========================================================================
    
    -------------------------------------------------------------------------------
    -- information function creates a wikicode for {{Information}} template based on
    -- passed arguments (through "frame") and data extracted from SDC. Allowed template
    -- arguments are : 'description', 'date', 'permission', 'author', 'other_versions',  
    -- 'source','other_fields', 'other_fields_1', 'demo' and 'lang'. All inputs do not 
    -- depend on capitalization and all "_" can be replaced with spaces.
    -------------------------------------------------------------------------------
    -- Dependencies: p._information
    -------------------------------------------------------------------------------
    function p.information(frame)
    	return p._information(core.getArgs(frame))	
    end
    
    -------------------------------------------------------------------------------
    -- interface for templates to 5 functions for extracting description, source,
    -- author, date and location information from SDC. 
    -- INPUTS (templaate parameters):
    --  * "mid"   - pageID defining a file. Optional, defaulting to the current file.
    --  * "lang"  - users language. Optional defaulting to the language of the user
    --  * "icon"  - add "Edit this at Wikidata" icon? boolean ( 'true'/'false', 'yes'/'no', 1/0
    -------------------------------------------------------------------------------
    -- Dependencies: getEntity
    -------------------------------------------------------------------------------
    local function parseFrame(frame)
    	local args = core.getArgs(frame)
    	local entity = mw.wikibase.getEntity( args.mid )
    	local icon   = core.yesno(args.icon, true)
    	return {entity, args.lang, icon}
    end
    
    function p.SDC_Description(frame)
    	return p._SDC_Description(unpack(parseFrame(frame)))
    end
    
    function p.SDC_Source(frame)
    	return p._SDC_Source(unpack(parseFrame(frame)))
    end
    
    function p.SDC_Author(frame)
    	return p._SDC_Author(unpack(parseFrame(frame)))
    end
    
    function p.SDC_Date(frame)
    	return p._SDC_Date(unpack(parseFrame(frame)))
    end
    
    function p.SDC_Location(frame)
    	return p._SDC_Location(unpack(parseFrame(frame)))
    end
    
    return p
    
    -------------------------------------------------------------------------------
    -- List of exported functions
    -------------------------------------------------------------------------------
    -- information
    -- SDC_Description
    -- SDC_Source
    -- SDC_Author
    -- SDC_Date
    -- SDC_Location