Jump to content

Module:Information

From wikishia

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('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.wikishia.net/common/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" dir="%s" lang="%s">%s %s</div>', dir, textLang, str, text)
end

-------------------------------------------------------------------------------
local function getBareLabel(id, userLang)
	-- code equivalent to require("Module:Wikidata label")._getLabel with Wikidata=- option
	local label
	
	-- Guard clause: Do nothing if wikibase is not available
	if not mw.wikibase then
		return id
	end
	
	-- 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
	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.wikishia.net$')) 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" dir="%s"', 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
-------------------------------------------------------------------------------
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
	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:var(--color-error,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 = nil -- Initialize entity as nil

	-- Only try to access wikibase if the library exists AND we are in the correct context
	if mw.wikibase and (namespace == 6 or args.sdc) then
		if args.sdc then
			entity = mw.wikibase.getEntity(args.sdc)
		else
			-- For namespace 6, get the entity for the current page
			entity = mw.wikibase.getEntity()
		end
	end
	
	-- If we successfully got an entity, try to populate fields from it
	if entity then
		local icon = true
		-- if a field is missing in args, 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    ===
	-- ====================================================
	mw.getCurrentFrame():expandTemplate{ title = 'Infobox template tag' }

	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; color: inherit;background: var(--background-color-warning-subtle,#ffe); ' ..
					'margin: .75em 15%; padding: .5em; border: 1px solid var(--border-color-content-removed,#e3e3b0);'
				local tag2 = message(field .. '-missing', lang)
				local tag3 = message(field .. '-missing-request', lang)
				local dir = mw.language.new(lang):getDir()
				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

-------------------------------------------------------------------------------
-- SDC functions
-------------------------------------------------------------------------------
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

function p._SDC_Date(entity, lang, icon)
	-- get creation date from P571 (inception)
	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)
				elseif v.precision == 10 then -- month
					Date = mw.ustring.sub(v.time, 2, 8)
				elseif v.precision == 9 then -- year
					Date = mw.ustring.sub(v.time, 2, 5)
				end
				if Date then -- translate
					Date = ISOdate(Date, lang, '', 'dtstart', '100-999')
				end
			end
		end
		if entity.claims.P571[1].qualifiers then
			local getDate = require("Module:Wikidata date")._date -- lazy loading
			local result = getDate(entity, 'P571', lang)
			Date = result.str
		end
	end
	if icon and Date then
		Date = Date .. core.editAtSDC('P571', lang)
	end
	return Date
end

function p._SDC_Source(entity, lang, icon)
	-- get source from P7482 (source of file)
	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

function p._SDC_Author(entity, lang, icon)
	-- get author from P170 (creator)
	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 if multiple creators
						role = '&nbsp;(' .. core.getLabel(qual.role.id, lang) .. ')'
					end
					if qual.username and qual.authorStr then
						table.insert(creators, '[[User:' .. qual.username .. '|' .. qual.authorStr .. ']]' .. role)
					elseif qual.username and not qual.authorStr then
						table.insert(creators, '[[User:' .. qual.username .. '|' .. qual.username .. ']]' .. role)
					elseif qual.url and qual.authorStr then
						table.insert(creators, '[' .. qual.url .. ' ' .. qual.authorStr .. ']' .. role)
					elseif qual.url and not qual.authorStr then
						table.insert(creators, qual.url .. role)
					elseif qual.authorStr then
						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

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
-- ===========================================================================
function p.information(frame)
	local args = core.getArgs(frame)
	args.strict = true
	return p._information(args)	
end

-------------------------------------------------------------------------------
-- interface for templates to SDC functions
-------------------------------------------------------------------------------
local function parseFrame(frame)
	local args = core.getArgs(frame)
	local entity = nil
	if mw.wikibase then
		entity = mw.wikibase.getEntity(args.mid)
	end
	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