Module:Information
Jump to navigation
Jump to search
This module is rated as ready for general use. It has reached a mature form and is thought to be bug-free and ready for use wherever appropriate. To reduce server load and bad output, it should be improved by sandbox testing rather than repeated trial-and-error editing. |
This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
Note: This module is used on a great lot of pages. In order not to put too much load on the servers, edits should be kept to a bare minimum. Please discuss proposed changes on the talk page first.
|
This message box is using an invalid "type=query" parameter and needs fixing.
Module providing back-end for {{Information}} template.
Examples
Code | Render | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Test simple case:
{{#invoke:Information|information |
description = description | date = 2024-12-09 | source = {{own}} | author = author | permission = permission | other versions = <gallery>File:Image-x-generic - black.svg</gallery>
}} |
| ||||||||||||
Test case with missing source, author and description:
{{#invoke:Information|information |
description = | date = 2024-12-09 | source = | author =
}} |
|
--[[
__ __ _ _ ___ __ _ _
| \/ | ___ __| |_ _| | ___ _|_ _|_ __ / _| ___ _ __ _ __ ___ __ _| |_(_) ___ _ __
| |\/| |/ _ \ / _` | | | | |/ _ (_)| || '_ \| |_ / _ \| '__| '_ ` _ \ / _` | __| |/ _ \| '_ \
| | | | (_) | (_| | |_| | | __/_ | || | | | _| (_) | | | | | | | | (_| | |_| | (_) | | | |
|_| |_|\___/ \__,_|\__,_|_|\___(_)___|_| |_|_| \___/|_| |_| |_| |_|\__,_|\__|_|\___/|_| |_|
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 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='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', 'strict'}
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
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
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 = 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
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 = ' (' .. 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)
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