Basculer le menu
Changer de menu des préférences
Basculer le menu personnel
Non connecté(e)
Votre adresse IP sera visible au public si vous faites des modifications.

« Module:Infobox/Core » : différence entre les versions

De Nefald
Hiob (discussion | contributions)
Aucun résumé des modifications
Balise : Révoqué
Hiob (discussion | contributions)
m A protégé « Module:Infobox/Core » ([Modifier=Autoriser uniquement les administrateurs] (infini) [Renommer=Autoriser uniquement les administrateurs] (infini))
 
(Une version intermédiaire par le même utilisateur non affichée)
Ligne 2 : Ligne 2 :
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs


-- Fonction pour créer l'HTML de l'infobox
-- Fonction de validation du champ "type"
function p.createInfobox(config, args)
local function validateTypeField(args, config)
     local root = mw.html.create('div')
     local typeValue = args.type or args['type']
         :addClass('infobox')
   
         :attr('role', 'region')
    -- Si pas de configuration pour le type, pas de validation
         :attr('aria-label', 'Infobox')
    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


     -- En-tête avec titre
-- Fonction pour gérer la catégorisation automatique
     local header = root:tag('div'):addClass('infobox-header')
local function addCategories(args, config, frame)
     -- Vérifier qu'on est dans le namespace principal (0)
     local title = mw.title.getCurrentTitle()
    if title.namespace ~= 0 then
        return ''
    end
      
      
    -- Conteneur pour l'icône et le titre
     local categoryString = ''
     local headerContent = header:tag('div'):addClass('infobox-header-content')
      
      
     -- Gestion de l'icône
     -- Nouvelles catégories configurables (fonction categories)
    if config.icone then
    if type(config.categories) == 'function' then
        local iconSpan = headerContent:tag('span'):addClass('infobox-icon')
        local cats = config.categories(args, config, frame)
       
        if cats then
        if type(config.icone) == 'table' then
             for _, cat in ipairs(cats) do
            -- Icône FontAwesome
                categoryString = categoryString .. '[[Catégorie:' .. cat .. ']]'
            local iconType = config.icone.type or 'fas'
            end
             local iconName = config.icone.nom or 'info-circle'
            iconSpan:addClass(iconType):addClass('fa-' .. iconName)
        elseif type(config.icone) == 'string' then
            -- Image personnalisée
            iconSpan:wikitext(string.format('[[Fichier:%s|20px|link=]]', config.icone))
         end
         end
     end
     end
      
      
     -- Titre
    return categoryString
     local titre = type(config.titre) == 'function' and config.titre(args) or config.titre
end
     headerContent:tag('span')
 
         :addClass('infobox-title')
 
         :wikitext(titre)
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')


     -- Image si définie
     -- En-tête avec nom
     if config.image then
     local header = mw.html.create('div')
         local imageData = type(config.image) == 'function' and config.image(args) or config.image
         :addClass('infobox-header')
         if imageData and imageData.nom then
   
            local imageDiv = root:tag('div'):addClass('infobox-image')
    if args.image and args.image ~= '' then
            local taille = imageData.taille or '300px'
         local imageDiv = mw.html.create('div')
            local legende = imageData.legende
            :addClass('infobox-image')
           
       
            -- Construction du wikitext pour l'image
        local imageSize = args.tailleimage or '280px'
            local imageWikitext = string.format('[[Fichier:%s|%s|center', imageData.nom, taille)
        local imageCode = string.format('[[Fichier:%s|%s|center', args.image, imageSize)
            if legende and legende ~= '' then
        if args.legende and args.legende ~= '' then
                imageWikitext = imageWikitext .. '|' .. legende
            imageCode = imageCode .. '|' .. args.legende
            end
            imageWikitext = imageWikitext .. ']]'
           
            imageDiv:wikitext(imageWikitext)
         end
         end
        imageCode = imageCode .. ']]'
       
        imageDiv:wikitext(imageCode)
        header:node(imageDiv)
     end
     end


     -- Corps de l'infobox avec les sections
     -- Titre sans icône (icône déplacée en bas à droite)
     local body = root:tag('div'):addClass('infobox-body')
     local titleDiv = mw.html.create('div')
        :addClass('infobox-title')
        :wikitext(args.nom)
      
      
     -- Variable pour suivre si les coordonnées ont été affichées
     header:node(titleDiv)
     local coordinatesDisplayed = false
 
      
     if args.soustitre and args.soustitre ~= '' then
     if config.sections then
        local subtitleDiv = mw.html.create('div')
         for _, section in ipairs(config.sections) do
            :addClass('infobox-subtitle')
             -- Créer la section seulement si elle a des champs à afficher
            :wikitext(args.soustitre)
            local hasVisibleFields = false
        header:node(subtitleDiv)
            for _, champ in ipairs(section.champs) do
    end
                 if args[champ.cle] and args[champ.cle] ~= '' then
 
                     hasVisibleFields = true
    root:node(header)
                     break
 
     -- Corps avec les champs
     local body = mw.html.create('div')
         :addClass('infobox-body')
 
    for _, section in ipairs(config.sections or {}) do
        if section.titre then
             local sectionDiv = mw.html.create('div')
                :addClass('infobox-section-title')
                :wikitext(section.titre)
            body:node(sectionDiv)
        end
 
        -- Vérifier si on doit afficher les coordonnées automatiquement
        local hasCoordinates = args.x and args.x ~= '' and args.y and args.y ~= '' and args.z and args.z ~= ''
        local coordinatesDisplayed = false
 
        for _, champ in ipairs(section.champs or {}) do
            -- Détection automatique des coordonnées
            if hasCoordinates and not coordinatesDisplayed and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' or champ.cle == 'spawn' 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')
               
                -- Construction du lien vers la carte si le paramètre monde existe
                 if args.monde and args.monde ~= '' then
                     local mapUrl = string.format('[https://map.nefald.fr/?worldname=%s&mapname=surface&zoom=4&x=%s&y=%s&z=%s %s, %s, %s]',
                        args.monde, args.x, args.y, args.z, args.x, args.y, args.z)
                     valueDiv:wikitext("''" .. mapUrl .. "''")
                else
                    -- Affichage simple sans lien
                    valueDiv:wikitext(string.format('%s, %s, %s', args.x, args.y, args.z))
                 end
                 end
            end
           
            -- Vérifier aussi les coordonnées groupées
            if not coordinatesDisplayed and args.x and args.y and args.z then
                for _, champ in ipairs(section.champs) do
                    if champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' then
                        hasVisibleFields = true
                        break
                    end
                end
            end
           
            if hasVisibleFields then
                local sectionDiv = body:tag('div'):addClass('infobox-section')
                  
                  
                 -- Titre de section
                 rowDiv:node(labelDiv):node(valueDiv)
                if section.titre then
                body:node(rowDiv)
                    sectionDiv:tag('div')
                 coordinatesDisplayed = true
                        :addClass('infobox-section-title')
                        :wikitext(section.titre)
                 end
                  
                  
                -- Vérifier si cette section contient les coordonnées (x, y, z)
            -- *** NOUVELLE LOGIQUE POUR TRAITEMENT DES CHAMPS ***
                local hasCoordinates = false
            elseif not (hasCoordinates and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z')) then
                if not coordinatesDisplayed and args.x and args.y and args.z then
                local rawValue = args[champ.cle]
                    for _, champ in ipairs(section.champs) do
                        if champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' then
                            hasCoordinates = true
                            break
                        end
                    end
                end
                  
                  
                 -- Si la section contient les coordonnées, les afficher groupées
                 -- Si le champ a une valeur OU une fonction process qui peut générer une valeur
                 if hasCoordinates and not coordinatesDisplayed then
                 if (rawValue and rawValue ~= '') or champ.process then
                    -- Vérifier si on peut créer un lien (worldname obligatoire)
                     local finalValue = rawValue
                    local canCreateLink = args.worldname and args.worldname ~= ''
                   
                    local labelDiv = mw.html.create('div')
                        :addClass('infobox-label')
                        :wikitext("'''Coordonnées'''")
                   
                     local valueDiv = mw.html.create('div')
                        :addClass('infobox-value')
                      
                      
                     if canCreateLink then
                     -- Appliquer la fonction process si elle existe
                        -- Construction du lien avec worldname
                    if champ.process and type(champ.process) == 'function' then
                        local mapname = args.mapname and args.mapname ~= '' and args.mapname or 'flat'
                         finalValue = champ.process(rawValue, args, frame)
                        local zoom = args.zoom and args.zoom ~= '' and args.zoom or '4'
                       
                        local mapUrl = string.format(
                            'https://map.nefald.fr/?worldname=%s&mapname=%s&zoom=%s&x=%s&y=%s&z=%s',
                            mw.uri.encode(args.worldname),
                            mw.uri.encode(mapname),
                            mw.uri.encode(zoom),
                            mw.uri.encode(args.x),
                            mw.uri.encode(args.y),
                            mw.uri.encode(args.z)
                        )
                       
                        local displayText = string.format('%s, %s, %s', args.x, args.y, args.z)
                         valueDiv:wikitext(string.format('[%s %s]', mapUrl, displayText))
                    else
                        -- Affichage simple sans lien si worldname manque
                        local displayText = string.format('%s, %s, %s', args.x, args.y, args.z)
                        valueDiv:wikitext(displayText)
                     end
                     end
                      
                      
                     local rowDiv = mw.html.create('div'):addClass('infobox-row')
                     -- N'afficher que si on a une valeur finale
                    rowDiv:node(labelDiv):node(valueDiv)
                     if finalValue and finalValue ~= '' then
                    sectionDiv:node(rowDiv)
                         local rowDiv = mw.html.create('div')
                    coordinatesDisplayed = true
                            :addClass('infobox-row')
                end
 
               
                         local labelDiv = mw.html.create('div')
                -- Champs de la section
                for _, champ in ipairs(section.champs) do
                    local value = args[champ.cle]
                   
                    -- Ignorer les coordonnées individuelles si elles ont été affichées groupées
                    if coordinatesDisplayed and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z') then
                        -- Ne rien faire, on a déjà affiché les coordonnées groupées
                     elseif value and value ~= '' then
                         local rowDiv = sectionDiv:tag('div'):addClass('infobox-row')
                       
                         -- Label
                        rowDiv:tag('div')
                             :addClass('infobox-label')
                             :addClass('infobox-label')
                             :wikitext("'''" .. champ.label .. "'''")
                             :wikitext(champ.label)
                          
                          
                        -- Valeur
                         local valueDiv = mw.html.create('div')
                         local valueDiv = rowDiv:tag('div'):addClass('infobox-value')
                            :addClass('infobox-value')
                          
                          
                         -- Traiter la valeur (fonction ou chaîne)
                         -- Formatage spécial pour le champ "type"
                         if type(champ.format) == 'function' then
                         if champ.cle == 'type' and typeValidation.displayValue then
                             valueDiv:wikitext(champ.format(value, args))
                             valueDiv:wikitext(typeValidation.displayValue)
                         else
                         else
                             valueDiv:wikitext(value)
                             valueDiv:wikitext(finalValue)
                         end
                         end
                        rowDiv:node(labelDiv):node(valueDiv)
                        body:node(rowDiv)
                     end
                     end
                 end
                 end
Ligne 174 : Ligne 200 :
     end
     end


     return tostring(root)
     root:node(body)
end
 
-- Fonction pour valider le type
function p.validateType(args, config)
    if not config.typeField then
        return true, nil
    end
      
      
     local typeValue = args.type
     -- Traiter l'icône APRÈS avoir construit tout le HTML
     if not typeValue or typeValue == '' then
     local finalResult = tostring(root)
        if config.typeField.required then
            return false, "Le champ 'type' est obligatoire"
        end
        return true, nil
    end
      
      
     -- Vérifier si le type est dans les valeurs autorisées
     if config.icone then
    if config.typeField.allowedValues then
        local iconeType = config.icone.type or 'fas'
         local typeConfig = config.typeField.allowedValues[typeValue:lower()]
        local iconeNom = config.icone.nom
         if not typeConfig then
       
             return false, config.typeField.errorMessage or "Type non reconnu"
        -- 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
         end
         return true, typeConfig
          
    end
        -- Créer la div d'icône séparément et l'ajouter au résultat
   
        local iconeHtml = string.format(
    return true, nil
            '<div class="infobox-icon-bottom">%s</div>',
end
            iconeContent
 
        )
-- Fonction pour générer les catégories
       
function p.generateCategories(args, config, typeConfig)
        -- Injecter l'icône juste avant la fermeture de la div principale
    if not config.categories then
        finalResult = finalResult:gsub('</div>$', iconeHtml .. '</div>')
        return ''
    end
   
    return config.categories(args, typeConfig)
end
 
-- Fonction principale appelée par {{#invoke:}}
function p.main(frame)
    local args = getArgs(frame, {parentFirst = true})
   
    -- Récupérer le type d'infobox depuis les arguments du frame
    local infoboxType = frame.args[1] or frame.args.type
    if not infoboxType or infoboxType == '' then
        return '<div class="error">Type d\'infobox requis</div>'
     end
     end
      
      
    -- Charger la configuration appropriée
     -- Ajouter les catégories automatiques (ancien + nouveau système)
    local configModule = 'Module:Infobox/Configs/' .. infoboxType
     local categories = addCategories(args, config, frame)
    local success, config = pcall(require, configModule)
   
    if not success then
        return '<div class="error">Configuration introuvable pour : ' .. infoboxType .. '</div>'
    end
   
    -- Valider le type si nécessaire
    local isValid, typeConfig = p.validateType(args, config)
    if not isValid then
        return '<div class="error">' .. typeConfig .. '</div>'
    end
   
    -- Générer l'infobox
    local infobox = p.createInfobox(config, args)
   
     -- Ajouter les catégories
     local categories = p.generateCategories(args, config, typeConfig)
      
      
     return infobox .. categories
     return finalResult .. categories
end
end


return p
return p

Dernière version du 24 octobre 2025 à 06:36

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

local p = {}
local getArgs = require('Module:Arguments').getArgs

-- 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)
    local title = mw.title.getCurrentTitle()
    if title.namespace ~= 0 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')

    for _, section in ipairs(config.sections or {}) do
        if section.titre then
            local sectionDiv = mw.html.create('div')
                :addClass('infobox-section-title')
                :wikitext(section.titre)
            body:node(sectionDiv)
        end

        -- Vérifier si on doit afficher les coordonnées automatiquement
        local hasCoordinates = args.x and args.x ~= '' and args.y and args.y ~= '' and args.z and args.z ~= ''
        local coordinatesDisplayed = false

        for _, champ in ipairs(section.champs or {}) do
            -- Détection automatique des coordonnées
            if hasCoordinates and not coordinatesDisplayed and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z' or champ.cle == 'spawn' 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')
                
                -- Construction du lien vers la carte si le paramètre monde existe
                if args.monde and args.monde ~= '' then
                    local mapUrl = string.format('[https://map.nefald.fr/?worldname=%s&mapname=surface&zoom=4&x=%s&y=%s&z=%s %s, %s, %s]',
                        args.monde, args.x, args.y, args.z, args.x, args.y, args.z)
                    valueDiv:wikitext("''" .. mapUrl .. "''")
                else
                    -- Affichage simple sans lien
                    valueDiv:wikitext(string.format('%s, %s, %s', args.x, args.y, args.z))
                end
                
                rowDiv:node(labelDiv):node(valueDiv)
                body:node(rowDiv)
                coordinatesDisplayed = true
                
            -- *** NOUVELLE LOGIQUE POUR TRAITEMENT DES CHAMPS ***
            elseif not (hasCoordinates and (champ.cle == 'x' or champ.cle == 'y' or champ.cle == 'z')) 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 labelDiv = mw.html.create('div')
                            :addClass('infobox-label')
                            :wikitext(champ.label)
                        
                        local valueDiv = mw.html.create('div')
                            :addClass('infobox-value')
                        
                        -- 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(labelDiv):node(valueDiv)
                        body:node(rowDiv)
                    end
                end
            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 iconeType = config.icone.type or 'fas'
        local iconeNom = config.icone.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 (ancien + nouveau système)
    local categories = addCategories(args, config, frame)
    
    return finalResult .. categories
end

return p