Module:Information/sandbox

Lua

CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules

Examples edit

Code Render
Test simple case:

{{#invoke:Information/sandbox|information
|description    = description
|date           = 2024-04-25
|source         = {{own}}
|author         = author
|permission     = permission     
|other versions = <gallery>File:Image-x-generic - black.svg</gallery>
}}
Description description
Date
Source Own work
Author author
Permission
(Reusing this file)
permission
Other versions
Test case with missing source, author and description:

{{#invoke:Information/sandbox|information
|description    = 
|date           = 2024-04-25   
|source         = 
|author         = 
}}
Description
Date
Source
Author
Test case with missing source, author and description:

{{#invoke:Information/sandbox|information
|description    = {{gnuplot}}
|date           = {{gnuplot}}  
|source         = Q98140374/Q98057938
|author         = {{Waarneming.nlUser|4190|Tello Neckheim}} 
|permission     = {{PD-old-100-expired}} 
}}
Description
 
This plot was created with Gnuplot.
Date
 
This plot was created with Gnuplot.
Source Q98140374/Q98057938
Author
This image is created by user Tello Neckheim at Waarneming.nl, a source of nature observations in the Netherlands.
Permission
(Reusing this file)
Public domain

This work is in the public domain in its country of origin and other countries and areas where the copyright term is the author's life plus 100 years or fewer.


This work is in the public domain in the United States because it was published (or registered with the U.S. Copyright Office) before January 1, 1929.

Test case for SDC based template on File:Indoor Climbing Kid.jpg page:

{{#invoke:Information/sandbox|information}}

In a file only {{Information}} will be needed

Description
English: A five year old hanging around bouldering wall in Sportrock climbing gym in Alexandria, Virginia, USA Edit this at Structured Data on Commons
Date  Edit this at Structured Data on Commons
Source Own work Edit this at Structured Data on Commons
Author Jarek Tuszyński Edit this at Structured Data on Commons

Code

--[[  
  __  __           _       _        ___        __                            _   _             
 |  \/  | ___   __| |_   _| | ___ _|_ _|_ __  / _| ___  _ __ _ __ ___   __ _| |_(_) ___  _ __  
 | |\/| |/ _ \ / _` | | | | |/ _ (_)| || '_ \| |_ / _ \| '__| '_ ` _ \ / _` | __| |/ _ \| '_ \ 
 | |  | | (_) | (_| | |_| | |  __/_ | || | | |  _| (_) | |  | | | | | | (_| | |_| | (_) | | | |
 |_|  |_|\___/ \__,_|\__,_|_|\___(_)___|_| |_|_|  \___/|_|  |_| |_| |_|\__,_|\__|_|\___/|_| |_|
                                                                                               
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('strict') -- 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 getBareLabel(id, userLang) 
-- code equivalent to require("Module:Wikidata label")._getLabel with Wikidata=- option
	local label, link
	-- build language fallback list
	local langList = mw.language.getFallbacksFor(userLang)
	table.insert(langList, 1, userLang)
	for _, lang in ipairs(langList) do  -- loop over language fallback list looking for label in the specific language
		label = mw.wikibase.getLabelByLang(id, lang)
		if label then break end                    -- label found and we are done
	end	
	return label or id
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='other_fields_0'},
		{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>\n%s%s\n</tr>\n\n', cell1, cell2)
			end
		end
		table.insert(results, field)
	end
	
	-- To save space in the templatelinks table, skip templatestyles in the File namespace on Commons;
	-- files get a copy of these styles via [[MediaWiki:Filepage.css]] instead.
	-- See [[Module talk:Information/styles.css]].
	local templatestyles = ''
	if args.namespace ~= 6 or not(mw.site.server:match('//commons.wikimedia.org$')) then
		templatestyles = mw.getCurrentFrame():extensionTag{
			name = 'templatestyles', args = { src = 'Module:Information/styles.css' }
		}
	end

	-- add table and outer layers
	local style = mw.ustring.format('class="fileinfotpl-type-information vevent '..
		'mw-content-%s" style="direction: %s;"', 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%s\n</div>', templatestyles, 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_0', '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_0', 'other_fields_1', 'demo', 'lang', 'strict','sdc'}
		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
	args.namespace = namespace

	-- ====================================================
	-- === harvest structured data                      === 
	-- ====================================================
	local entity = mw.wikibase.getEntity()
	if namespace==6 then 
		entity = mw.wikibase.getEntity()
	elseif args.sdc then
		entity = mw.wikibase.getEntity(args.sdc)
	end
	
	if (namespace==6 or args.sdc) 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
	if args.strict~=false then
		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
	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
		for _,statement in ipairs(entity.claims.P7482) do
			if statement.mainsnak.datavalue then
				-- 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 = getBareLabel(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  = getBareLabel('Q87402110', lang)
					source = mw.ustring.format('<span class="int-own-work" lang="%s">%s</span>',lang, label)
				end
				if source then
					break
				end
			end
		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_0', 'other_fields_1', 'demo', 'sdc' and 'lang'. 
-- All inputs do not depend on capitalization and all "_" can be replaced with spaces.
-------------------------------------------------------------------------------
-- Dependencies: p._information
-------------------------------------------------------------------------------
function p.information(frame)
	local args = core.getArgs(frame)
	args.strict = true
	return p._information(args)	
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