Module:Infobox

De Lagny-sur-Marne Wiki
Révision datée du 16 septembre 2015 à 12:01 par Zolo (discussion)
Aller à : navigation, rechercher

La documentation pour ce module peut être créée à Module:Infobox/doc

local p = {}

-- variables globales alimentées par les paramètres utilisés
item = nil -- l'élément Wikidata lié
moduledata = nil -- la sous-page de module:Infobox demandée
localdata = {}-- données concernant les paramètres passés au modèle
maincolor = '#E1E1E1'
page = { -- données concernant la page où est affichée l'infobox
	name = mw.title.getCurrentTitle().prefixedText,
	namespace =  mw.title.getCurrentTitle().namespace
}

-- l'objet principal à retournerp
local infobox = mw.html.create('div')

-- objes secondaires à retourner
local maintenance = '' -- chaîne retournfoée avec le module : cats de maintenance
local externaltext = '' -- par exemple coordonnées en titre
-- modules importés
local wikidata = require "Module:Interface Wikidata".fromLua
local defaultstyle = require "Module:Infobox/Style"
local mapmod = require "Module:Carte"

local i18n = {
	['see doc'] = 'Documentation du modèle',
	['edit'] = 'modifier',
	['edit code'] = 'modifier le code',
	['edit item'] = 'modifier Wikidata',
	['tracking cat'] = "Page utilisant des données de Wikidata",
	['invalid block type'] = "Bloc de données invalide dans le module d'infobox",
	['default cat'] = "Maintenance des infobox",
}

local function addwikidatacat(prop)
	maintenance = maintenance .. wikidata.addtrackingcat(prop)
end

local function expandquery(query)
	local value, number -- valeur à retourner, nombre de valeurs pour accorder le libellé
	if not query.entity then
		query.entity = item
	end
	if not query.conjtype then
		query.conjtype = 'comma'
	end
	local claims = wikidata.getClaims(query)
		if (not claims) then
		return nil
	end
	return wikidata.formatAndCat(query), #claims -- pour l'accord au pluriel
end

local function getWikidataValue(params, wikidataparam)
	-- Récupère la valeur Wikidata pour la valeur, soit dans le paramètre "wikidata" soit dans le praramètre "property"
	if not item then
		return nil
	end
	local value, number
	
	if not wikidataparam then -- par défaut la valeur wikidata est dans le paramètre "wikidata" mais dans les structures composées comme "title", il y a plusieurs paramètres wikidata
		wikidataparam = 'wikidata'
	end

	if params[wikidataparam] then
		if type(params[wikidataparam]) == 'function' then
			return params[wikidataparam](item)
		elseif type(params[wikidataparam]) == 'table' then
			return expandquery(params[wikidataparam])
		else
			return params[wikidataparam]
		end
	end
end

local function getvalue(val, params)
	if type(val) == 'string' then
		return localdata[val]
	elseif type(val) == 'function' then
		return val(localdata, item, params)
	elseif type(val) == 'table' then
		for i, j in pairs(val) do -- si plusieurs paramètres possibles (legacy de vieux code), prendre le preimeir non bide
			if localdata[j] then
				return localdata[j]
			end
		end
	end
end

local function addmaintenancecat(cat, sortkey)
	if page.namespace ~= 0 then
		return ''
	end
	if cat then
		maintenance = maintenance .. '[[Category:' .. cat .. '|' .. (sortkey or page.name) .. ']]'
	end
end

function p.buildtitle(params)
	local text = getvalue(params.value, params) or params.textdefaultvalue or  getWikidataValue(params) or mw.title.getCurrentTitle().text
	local subtext = getvalue(params.subtitle) or  getWikidataValue(params, 'wikidatasubtitle') or params.subtitledefaultvalue
	if subtext and (subtext ~= text) then
		text = text .. '<br /><small>' .. subtext .. '</small>'
	end
	local icon = params.icon or ''
	if icon ~= '' and not params.large then
		icon = 'icon ' .. icon
	end
	local class = 'entete ' .. icon
	
	-- overwrites with those provided in the module
	local style = {}
	if maincolor then
		style['background-color'] = maincolor
	end
	if params.style then
		for i, j in pairs(params.style) do
			style[i] = j
		end
	end
	local title = mw.html.create('div')
		:addClass(class)
		:css(style)
		:tag('div')
			:wikitext(text)
		:allDone()
	return title
end

function p.buildnavbox(params)
	local class = "overflow nav " .. (params.class or '')
	local style = params.style or {['border-width'] = '1px'}
	if not style['border-color'] then
		style['border-color'] = maincolor
	end
	local previousval = localdata[params.previousparameter]	or getWikidataValue(params, 'previouswikidata')
	if not previousval and params['previousproperty'] then
		previousval = wikidata.formatAndCat{entity = item, property = params['previousproperty']}
	end
	local nextval = localdata[params.nextparameter] or getWikidataValue(params, 'nextwikidata')
	if not nextval and params['nextproperty'] then
		nextval = wikidata.formatAndCat{entity = item, property = params['nextproperty']}
	end
	local navbox =
	mw.html.create('p')
		:addClass(class)
		:css(style)
		:tag('span')
			:addClass('prev_bloc')
			:wikitext(previousval)
			:done()
		:tag('span')
			:addClass('next_bloc')
			:wikitext(nextval)
			:done()
		:done()
		return navbox
end

function p.buildimages(params)
	local images = {}
	local size, link, caption
	if type(params.imageparameters) == 'string' then
		params.imageparameters = {params.imageparameters}
	end
	if not params.imageparameters then -- s'il n'y a pa de paramètre image, continuer, peut-être y-a-t-il une image par défaut définie dans le module d'infobox
		params.imageparameters = {}
	end
	for j, k in ipairs(params.imageparameters) do
		table.insert(images, localdata[k])
	end
	
	-- Images de Wikidata
	if #images == 0 and item then
		if params.property then
			images = wikidata.getClaims{entity = item, property = params.property} or {}
			if #images > 0 then
				addwikidatacat(params.property)
			end
		end
		if params.wikidata then
			images = params.wikidata()
			if type(images) == 'string' then
				return images
			end -- c'est probablement une erreur dans la requête => afficher le message
		end
	end
	if type(images[1]) == 'table' then
		for i, j in pairs(images) do
			if j.mainsnak.snaktype ~= 'value' then
				return
			end
			if i > (params.numval) then
				images[i] = nil
			else
				images[i] = j.mainsnak.datavalue.value
			end
		end
	end

	-- Images par défaut
	local isdefault = false
	if #images == 0 then
		if params.maintenancecat then
			local maintenancecat = getvalue(params.maintenancecat, params)
			addmaintenancecat(maintenancecat, params.sortkey)
		end
		if params.defaultimages then
			images = params.defaultimages
			if type(images) == 'string' then
				images = {images}
			end
		end
		size, caption, link = params.defaultimagesize, params.defaultimagecaption, params.defaultimagelink
	end
	if #images == 0 then
		return nil
	end
	
	size = size or getvalue(params.sizeparameter) or params.defaultsize or '280x400px'
	link = link or getvalue(params.linkparameter) or params.defaultlink
	caption = caption or getvalue(params.captionparameter) or params.defaultcaption or ''

	if type(size) == 'function' then
		size = size(localdata)
	end
	if type(link) == 'function' then
		link = link()
	end
	if type(caption) == 'function' then
		caption = caption(localdata)
	end

	-- Vérifie que les images ne sont pas > 280 px
	local numsize = size:gsub('px', '')
	numsize = mw.ustring.gsub(numsize, 'x.*', '')
	numsize = tonumber(numsize)
	if type(numsize) ~= 'number' or numsize > 280 then
		addmaintenancecat("taille d'image invalide")
	end
	if link then
		link = '|link=' .. link
	else
		link = ''
	end
	local style = params.style or {padding ='2px 0',}

	-- Partie image

	local imagesString = ''
	for i,image in pairs(images) do
		if image == '-' then
			return
		end
		imagesString = imagesString ..  '[[Fichier:' .. image .. link .. '|frameless|' .. size
		if #images == 1 then
			imagesString = imagesString .. '|center'
		end
		if image.upright then
			imagesString = imagesString .. '|upright=' .. image.upright
		else
			imagesString = imagesString .. '|upright=' .. ( 1 / #images )
		end
		imagesString = imagesString .. ']]'
	end

	local image = mw.html.create('div')
		:addClass("images")
		:css(style)
		:wikitext(imagesString)

	-- Partie légende
	local captionobj = mw.html.create('p')
	if caption then
		captionobj
			:wikitext(caption)
			:css(params.style or {})
			:addClass("legend")
			:done()
	end
	
	-- séparateur
	local separator
	if params.separator then
		local separatorStyle = params['separator style'] or {}
		separatorStyle.height = separatorStyle.height or '2px'
		separatorStyle['background-color'] = separatorStyle['background-color'] or maincolor
		separator = mw.html.create('hr')
		separator:css( separatorStyle )
	end
	return mw.html.create('div'):node(image):node(captionobj):node(separator):done()
end

function p.buildtext(params)
	local class = params.class or ''
	local style = defaultstyle['boldline']
	if params.style then
		for i, j in pairs(params.style) do
			style[i] = j
		end
	end
	local text = getvalue(params.value, params) or getWikidataValue(params) or params.defaultvalue
	if text == '-' then
		return
	end
	if not text then
		addmaintenancecat(params.maintenancecat, params.sortkey)
		return nil
	end
	local formattedtext = mw.html.create('p')
		:addClass(class)
		:css(style)
		:wikitext(text)
		:done()
	return formattedtext
end

function p.buildrow(params)
	local class = params.class or ''
	local style = params.style or {}
	local value, number =  getvalue(params.value, params)
	if not value then
		value, number =  getWikidataValue(params, 'wikidata')
	end
	if (not value) and (params.property) then
		value, number = expandquery({property = params.property})  -- the property key takes a string value,  that would not work well using the wikidata key 
	end
	if not value then
		value = params.defaultvalue
	end
	if value == '-' then
		return nil
	end
	if not number then
		number = 0 -- == indéfini
	end
	if not value then
		if params.maintenancecat then
			local maintenancecat = getvalue(params.maintenancecat, params)
			addmaintenancecat(maintenancecat, params.sortkey)
		end
		return nil
	end

	local label = params.label
	if number > 1 and (params.plurallabel) then
		label = params.plurallabel
	elseif number == 1 and (params.singularlabel) then
		label = params.singularlabel
	end
	if type(label) == 'function' then
			label = label(localdata, item)
	end

	-- format
	local formattedvalue = mw.html.create('div')
		:wikitext('\n' .. value) -- Le '\n' est requis lorsque value est une liste commençant par '*' ou '#'
		
	if (params.hidden == true)then
		formattedvalue
			:attr({class="NavContent", style="display: none; text-align: left;"})
		formattedvalue = mw.html.create('div')
			:attr({class="NavFrame", title="[Afficher]/[Masquer]", style="border: none; padding: 0;"})
			:node(formattedvalue)
	end
	formattedvalue =  mw.html.create('td')
			:node(formattedvalue)
			:allDone()
	
	local formattedlabel
	if label then
		formattedlabel = mw.html.create('th')
			:attr('scope', 'row')
			:wikitext(label)
			:done()
	end
	local row = mw.html.create('tr')
		:addClass(class)
		:css(style)
		:node(formattedlabel)
		:node(formattedvalue)
		:done()
	
	return row
end

function p.buildtable(params)
	local tab = mw.html.create('table'):css(params.style or {})

	-- CREATE ROWS
	local rows = {}
		for k, l in pairs(params.rows) do
		if type(l) == 'table' and l.type == 'multi' then -- when a single function is used for return several rows
			table.remove(params.rows, k)
			local count = 0
			for m, n in pairs(l.rows) do
				table.insert(params.rows, k + count, n)
				count = count + 1
			end
			l = params.rows[k]
		end

		if type(l) == 'function' then --accepte les fonctions qui retournent des tables
			l = l(localdata, item)
		end
		if type(l) == 'nil' then
			--ne rien faire (quand la valeur est originellemenet une fonctin elle peut retourner nil)
		elseif type(l) ~= 'table' then
			return error('les lignes d\'infobox ("rows") doivent être des tables, est ' .. type(l))
		else
			local row = p.buildblock(l)
			table.insert(rows, row)
		end
	end
	if #rows == 0 then
		return nil
	end

	-- ADD TITLE
	local title
	if params.title or params.singulartitle or params.pluraltitle then
		if #rows > 1 and params.pluraltitle then
			title = pluraltitle
		elseif #rows == 1 and params.singulartitle then
			title = pluraltitle
		else
			title = params.title
		end
		local text = params.title
		local style = {['text-align'] = 'center', color = '000000'}
		if type(text) == 'table' then
			text = text.value
			style = text.style or style
		end
		
		if not style['background-color'] then
			style['background-color'] = maincolor
		end
		local colspan = params.title.colspan or '2'
		title = mw.html.create('caption')
			:attr({colspan = 2})
			:css(style)
			:wikitext(text)
			:done()
	end

	if title then
		tab:node(title)
	end
	for i, j in pairs (rows) do
		tab:node(j)
	end
	tab:allDone()
	return tab
end

function p.buildinvalidblock(args)
	addmaintenancecat(defaultcat)
	local text = ''
	if type(args) ~= 'table' then
		text = "Les blocs d'infobox doivent être des tables"
	else
		text = i18n["invalid block type"] .. ' : ' .. (args.type or '??')
	end
	return text
end

function p.buildmap(params)-- TODO  : gestion de plusieurs points
	local maplist = getvalue(params.maps)
	local pointtype = params.pointtype
	local maptype = params.maptype -- choisit le type de carte le plus approprié (relief, administratif, etc.)	
	if type(maplist) == 'function' then
		maplist = maplist(localdata, item)
	end
	local width = tonumber(params.width) or 280
	if width > 280 then
		addmaintenancecat("Erreur d'Infobox/Image trop grande")
		return 'image trop grande, la largeur doit être inférieure ou égale à 280px'
	end
	if type(params.latitude) == 'function' then
		latitude, longitude = params.latitude(localdata, item), params.longitude(localdata, item)
	else
		latitude, longitude = localdata[params.latitude], localdata[params.longitude]
	end
	if (not latitude or not longitude) and params.wikidata then
		local query = params.wikidata
		if type(query) == 'function' then
			query = query()
		end
		if not query then
			return nil
		end
		query.excludespecial = true
		query.entity = query.entity or item
		local claims = wikidata.getClaims(query)
		if not claims then
			return nil
		end
		local val = claims[1].mainsnak.datavalue.value
		latitude, longitude = val.latitude, val.longitude
	end
	if not latitude or not longitude then
		return nil
	end
	local newparams = {maplist = maplist, pointtype = pointtype, maptype = maptype, width = width, item = item, latitude = latitude, longitude = longitude}
	if params.params and type(params.params) == 'table' then -- paramètres additionnels
		for i, j in pairs(params.params) do
			newparams[i] = j
		end
	end
	return mapmod.multimap(newparams)
end

function p.buildfooter(params)
	if not params then
		params = {}
	end
	
	local class = 'navbar noprint ' .. (params.class or '')
	local style = params.style or {}
	style['border-top'] = style['border-top'] or '1px solid ' .. maincolor
	
	local backlinkstr = '[' .. tostring( mw.uri.fullUrl( page.name, '&veaction=edit&section=0' ) ) .. ' ' .. i18n['edit'] .. ']'
		.. ' - [' .. tostring( mw.uri.fullUrl( page.name, '&action=edit&section=0' ) ) .. ' ' .. i18n['edit code'] .. ']'

	local itemlinkstr
	if item then
		itemlinkstr = '[[d:' .. item.id .. '|' .. i18n['edit item'] .. ']]'
	end
	local editstr = backlinkstr
	if itemlinkstr then
		editstr = editstr .. ' - ' .. itemlinkstr
	end
	local editlinkspan =  mw.html.create('span')
		:css({['text-align'] = "left"})
		:addClass('plainlinks')
		:wikitext(editstr)
		:done()
	local doclinkstr = '[[Image:Gtk-dialog-info.svg|12px|link=' .. localdata.templatename .. '|' .. i18n['see doc'] .. ']]'
	-- si ce lien ne marche pas toujours, il faut ajouter un variable pour le nom de l'infobox récupéré par le frame
	local doclinkspan = mw.html.create('span')
		:css({['text-align'] = "right"})
		:wikitext(doclinkstr)
		:done()
	
	local footer = mw.html.create('p')
		:addClass(class)
		:css(style)
		:node(editlinkspan)
		:node(doclinkspan)
	return footer
end

function p.buildblock(block)
	if type(block) == 'function' then
		block = block()
	end

	local blocktypes = { -- list of functions for block buildings
		['invalid'] = p.buildinvalidblock,
		['external text'] = p.buildexternaltext,
		['footer'] = p.buildfooter,
		['images'] = p.buildimages,
		['map']= p.buildmap,
		['mixed'] = p.buildrow,
		['navbox'] = p.buildnavbox,
		['table'] = p.buildtable,
		['row'] = p.buildrow,
		['text'] = p.buildtext,
		['title'] = p.buildtitle,
	}
	if type(block) ~= 'table' or (not block.type) or (not blocktypes[block.type]) then
		return blocktypes['invalid'](block)
	end
	return blocktypes[block.type](block) 
end

function p._build(modulename, params, templatename)

	-- fill-up global variable localdata using params
	for i, j in pairs(params) do
		if j and mw.text.trim(j) ~= '' then -- empty parameters are ignored
			localdata[i] = j
		end
	end
	localdata.templatename = templatename or modulename

	-- load wikidata item as a global variable:
	if localdata.wikidata == '-' then
		item = nil
	else
		item = wikidata.getEntity(localdata.wikidata)
	end
	
	-- assign rank to the infobox, "secondary" means special formatting like no displaytitle for coordinates
	local infoboxrank = 'main' -- main infobox of the page, with coordinates displayed in title etc.
	if page.namespace ~= 0 then
		infoboxrank = 'secondary'
	end
	-- if infobox is linked to another item: rank = secondary
	if item then
		local itemlink = mw.wikibase.sitelink(item.id)
		local pagetitle = mw.title.getCurrentTitle().text
		if (itemlink or '') ~= pagetitle then
			infoboxrank = 'secondary'
		end
	end
	localdata.infoboxrank = infoboxrank

	-- load infobox module page
	moduledata = require('Module:Infobox/' .. modulename)
	moduledata.name = modulename
	-- defines main color
	maincolor = localdata['couleur infobox'] or localdata['couleur boîte'] or moduledata.maincolor or maincolor
	if maincolor:match( '^%x%x%x%x%x%x$' ) or maincolor:match( '^%x%x%x$' ) then
		maincolor = '#' .. maincolor
	end
	
	-- class
	local class = 'infobox_v3'
	if moduledata.class then
		class = class .. ' ' .. moduledata.class
	end
	
	-- style
	local style = moduledata.style or {}
	if not style['max-width'] then
		style['max-width'] = '300px'
	end
	
	-- build infobox
	infobox	:addClass(class)
			:css(style)
	for i, j in pairs( moduledata.parts ) do
		infobox:node( p.buildblock(j) )
	end
	infobox	:node(p.buildfooter(moduledata.footer))
			:done()
			
	return tostring(infobox) .. externaltext, maintenance
end

function p.build(frame)
	local name =  frame.args.nom
	local parent = frame:getParent()
	local templatename = parent:getTitle() -- au cas où il soit différent du nom du module
	local params = parent.args
	return p._build(name, params, templatename) 
end

return p