« Module:Infobox/Core » : différence entre les versions
De Nefald
Autres actions
Annulation des modifications 5085 de Hiob (discussion) Balise : Annulation |
Port depuis codex.nefald.fr : masquage des sections vides, champs sans label pleine largeur, icône configurable par fonction (via update-page on MediaWiki MCP Server) |
||
| (Une version intermédiaire par le même utilisateur non affichée) | |||
| Ligne 72 : | Ligne 72 : | ||
-- Fonction pour gérer la catégorisation automatique | -- Fonction pour gérer la catégorisation automatique | ||
local function addCategories(args, config, frame) | local function addCategories(args, config, frame) | ||
-- Vérifier qu'on est dans le namespace principal (0) | -- Vérifier qu'on est dans le namespace principal (0) ou namespace plugin (3000) | ||
local title = mw.title.getCurrentTitle() | local title = mw.title.getCurrentTitle() | ||
if title.namespace ~= 0 then | if title.namespace ~= 0 and title.namespace ~= 3000 then | ||
return '' | return '' | ||
end | end | ||
| Ligne 152 : | Ligne 152 : | ||
for _, section in ipairs(config.sections or {}) do | for _, section in ipairs(config.sections or {}) do | ||
-- Les lignes de la section sont accumulées ici et intégrées au body seulement | |||
-- si au moins une a du contenu — sinon le section-title reste orphelin (ex. une | |||
-- section "Sous-pages" ou "Compatibilité" entièrement vide sur cette page). | |||
local sectionRows = {} | |||
for _, champ in ipairs(section.champs or {}) do | for _, champ in ipairs(section.champs or {}) do | ||
| Ligne 174 : | Ligne 172 : | ||
rowDiv:node(labelDiv):node(valueDiv) | rowDiv:node(labelDiv):node(valueDiv) | ||
table.insert(sectionRows, rowDiv) | |||
coordinatesDisplayed = true | coordinatesDisplayed = true | ||
| Ligne 195 : | Ligne 193 : | ||
:addClass('infobox-row') | :addClass('infobox-row') | ||
local valueDiv = mw.html.create('div') | local valueDiv = mw.html.create('div') | ||
:addClass('infobox-value') | :addClass('infobox-value') | ||
-- Champ sans label (ex. liste pleine largeur type Sous-pages) : | |||
-- pas de colonne label, la valeur devient seule enfant flex du | |||
-- row et occupe donc naturellement 100% de la largeur. | |||
if champ.label and champ.label ~= '' then | |||
local labelDiv = mw.html.create('div') | |||
:addClass('infobox-label') | |||
:wikitext(champ.label) | |||
rowDiv:node(labelDiv) | |||
end | |||
-- Formatage spécial pour le champ "type" | -- Formatage spécial pour le champ "type" | ||
| Ligne 209 : | Ligne 213 : | ||
end | end | ||
rowDiv | rowDiv:node(valueDiv) | ||
table.insert(sectionRows, rowDiv) | |||
end | end | ||
end | end | ||
end | |||
end | |||
if #sectionRows > 0 then | |||
if section.titre then | |||
local sectionDiv = mw.html.create('div') | |||
:addClass('infobox-section-title') | |||
:wikitext(section.titre) | |||
body:node(sectionDiv) | |||
end | |||
for _, rowDiv in ipairs(sectionRows) do | |||
body:node(rowDiv) | |||
end | end | ||
end | end | ||
| Ligne 223 : | Ligne 239 : | ||
if config.icone then | if config.icone then | ||
local | local iconeConfig = config.icone | ||
local iconeNom = | if type(iconeConfig) == 'function' then | ||
iconeConfig = iconeConfig(args) or {} | |||
end | |||
local iconeType = iconeConfig.type or 'fas' | |||
local iconeNom = iconeConfig.nom | |||
-- Utiliser mw.getCurrentFrame() si frame n'est pas disponible | -- Utiliser mw.getCurrentFrame() si frame n'est pas disponible | ||
Dernière version du 3 août 2026 à 09:32
La documentation pour ce module peut être créée à Module:Infobox/Core/doc
local p = {}
local getArgs = require('Module:Arguments').getArgs
-- Fonction pour générer le lien de carte dynamique
local function buildMapLink(args)
-- Vérifier les champs obligatoires
if not args.x or args.x == '' then return nil end
if not args.y or args.y == '' then return nil end
if not args.z or args.z == '' then return nil end
if not args.worldname or args.worldname == '' then return nil end
-- Valeurs par défaut pour les champs optionnels
local mapname = args.mapname or 'flat'
local zoom = args.zoom or '4'
-- Construction de l'URL
local url = string.format(
'https://map.nefald.fr/?worldname=%s&mapname=%s&zoom=%s&x=%s&y=%s&z=%s',
mw.uri.encode(args.worldname, 'PATH'),
mw.uri.encode(mapname, 'PATH'),
zoom,
args.x,
args.y,
args.z
)
-- Retourner le lien formaté
return string.format('[%s %s, %s, %s]', url, args.x, args.y, args.z)
end
-- Fonction de validation du champ "type"
local function validateTypeField(args, config)
local typeValue = args.type or args['type']
-- Si pas de configuration pour le type, pas de validation
if not config.typeField then
return { valid = true }
end
-- Si le champ est requis et vide
if config.typeField.required and (not typeValue or typeValue == '') then
return {
error = 'Le champ "type" est obligatoire.',
valid = false
}
end
-- Si pas de valeur, mais pas requis, c'est valide
if not typeValue or typeValue == '' then
return { valid = true }
end
-- Nettoyer la valeur
local cleanType = mw.text.trim(mw.ustring.lower(typeValue))
-- Vérifier si la valeur est dans la liste autorisée
if config.typeField.allowedValues and not config.typeField.allowedValues[cleanType] then
local errorMsg = config.typeField.errorMessage or 'Valeur de type non autorisée : ' .. typeValue
return {
error = errorMsg,
valid = false
}
end
return {
valid = true,
cleanValue = cleanType,
displayValue = config.typeField.allowedValues and config.typeField.allowedValues[cleanType] and config.typeField.allowedValues[cleanType].display or typeValue
}
end
-- Fonction pour gérer la catégorisation automatique
local function addCategories(args, config, frame)
-- Vérifier qu'on est dans le namespace principal (0) ou namespace plugin (3000)
local title = mw.title.getCurrentTitle()
if title.namespace ~= 0 and title.namespace ~= 3000 then
return ''
end
local categoryString = ''
-- Nouvelles catégories configurables (fonction categories)
if type(config.categories) == 'function' then
local cats = config.categories(args, config, frame)
if cats then
for _, cat in ipairs(cats) do
categoryString = categoryString .. '[[Catégorie:' .. cat .. ']]'
end
end
end
return categoryString
end
function p.build(args, config, frame)
if not args.nom or args.nom == '' then
return '<div class="error">Nom requis pour l\'infobox</div>'
end
-- Validation du champ type
local typeValidation = validateTypeField(args, config)
if not typeValidation.valid then
return '<div class="error">' .. typeValidation.error .. '</div>'
end
local root = mw.html.create('div')
:addClass('infobox-table')
:attr('role', 'table')
-- En-tête avec nom
local header = mw.html.create('div')
:addClass('infobox-header')
if args.image and args.image ~= '' then
local imageDiv = mw.html.create('div')
:addClass('infobox-image')
local imageSize = args.tailleimage or '280px'
local imageCode = string.format('[[Fichier:%s|%s|center', args.image, imageSize)
if args.legende and args.legende ~= '' then
imageCode = imageCode .. '|' .. args.legende
end
imageCode = imageCode .. ']]'
imageDiv:wikitext(imageCode)
header:node(imageDiv)
end
-- Titre sans icône (icône déplacée en bas à droite)
local titleDiv = mw.html.create('div')
:addClass('infobox-title')
:wikitext(args.nom)
header:node(titleDiv)
if args.soustitre and args.soustitre ~= '' then
local subtitleDiv = mw.html.create('div')
:addClass('infobox-subtitle')
:wikitext(args.soustitre)
header:node(subtitleDiv)
end
root:node(header)
-- Corps avec les champs
local body = mw.html.create('div')
:addClass('infobox-body')
-- Vérifier si on a des coordonnées complètes
local mapLink = buildMapLink(args)
local coordinatesDisplayed = false
for _, section in ipairs(config.sections or {}) do
-- Les lignes de la section sont accumulées ici et intégrées au body seulement
-- si au moins une a du contenu — sinon le section-title reste orphelin (ex. une
-- section "Sous-pages" ou "Compatibilité" entièrement vide sur cette page).
local sectionRows = {}
for _, champ in ipairs(section.champs or {}) do
-- Gestion spéciale pour les coordonnées
if mapLink and not coordinatesDisplayed and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' or champ.cle == 'worldname' or champ.cle == 'coordonnees') then
local rowDiv = mw.html.create('div')
:addClass('infobox-row')
local labelDiv = mw.html.create('div')
:addClass('infobox-label')
:wikitext('Coordonnées')
local valueDiv = mw.html.create('div')
:addClass('infobox-value')
:wikitext(mapLink)
rowDiv:node(labelDiv):node(valueDiv)
table.insert(sectionRows, rowDiv)
coordinatesDisplayed = true
-- Traitement normal des autres champs (ignorer x, y, z, worldname s'ils sont déjà affichés comme coordonnées)
elseif not (mapLink and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' or champ.cle == 'worldname' or champ.cle == 'mapname' or champ.cle == 'zoom')) then
local rawValue = args[champ.cle]
-- Si le champ a une valeur OU une fonction process qui peut générer une valeur
if (rawValue and rawValue ~= '') or champ.process then
local finalValue = rawValue
-- Appliquer la fonction process si elle existe
if champ.process and type(champ.process) == 'function' then
finalValue = champ.process(rawValue, args, frame)
end
-- N'afficher que si on a une valeur finale
if finalValue and finalValue ~= '' then
local rowDiv = mw.html.create('div')
:addClass('infobox-row')
local valueDiv = mw.html.create('div')
:addClass('infobox-value')
-- Champ sans label (ex. liste pleine largeur type Sous-pages) :
-- pas de colonne label, la valeur devient seule enfant flex du
-- row et occupe donc naturellement 100% de la largeur.
if champ.label and champ.label ~= '' then
local labelDiv = mw.html.create('div')
:addClass('infobox-label')
:wikitext(champ.label)
rowDiv:node(labelDiv)
end
-- Formatage spécial pour le champ "type"
if champ.cle == 'type' and typeValidation.displayValue then
valueDiv:wikitext(typeValidation.displayValue)
else
valueDiv:wikitext(finalValue)
end
rowDiv:node(valueDiv)
table.insert(sectionRows, rowDiv)
end
end
end
end
if #sectionRows > 0 then
if section.titre then
local sectionDiv = mw.html.create('div')
:addClass('infobox-section-title')
:wikitext(section.titre)
body:node(sectionDiv)
end
for _, rowDiv in ipairs(sectionRows) do
body:node(rowDiv)
end
end
end
root:node(body)
-- Traiter l'icône APRÈS avoir construit tout le HTML
local finalResult = tostring(root)
if config.icone then
local iconeConfig = config.icone
if type(iconeConfig) == 'function' then
iconeConfig = iconeConfig(args) or {}
end
local iconeType = iconeConfig.type or 'fas'
local iconeNom = iconeConfig.nom
-- Utiliser mw.getCurrentFrame() si frame n'est pas disponible
local currentFrame = frame or mw.getCurrentFrame()
local iconeContent = '{{#' .. iconeType .. ':' .. iconeNom .. '}}'
if currentFrame then
iconeContent = currentFrame:preprocess(iconeContent)
end
-- Créer la div d'icône séparément et l'ajouter au résultat
local iconeHtml = string.format(
'<div class="infobox-icon-bottom">%s</div>',
iconeContent
)
-- Injecter l'icône juste avant la fermeture de la div principale
finalResult = finalResult:gsub('</div>$', iconeHtml .. '</div>')
end
-- Ajouter les catégories automatiques
local categories = addCategories(args, config, frame)
return finalResult .. categories
end
return p