Module:ArgumentContent
La documentation pour ce module peut être créée à Module:ArgumentContent/doc
-- Module:ArgumentContent (MediaWiki 1.43) — Optimisé (style Module:Debate)
-- Objectif : conserver le rendu, réduire le coût Lua, et remplacer #formlink/#queryformlink par Special:AddData / Special:RunQuery
local p = {}
----------------------------------------------------------------------
-- Accès global + micro-opt
----------------------------------------------------------------------
local mw = mw
local F = mw.getCurrentFrame()
local V = ( mw.ext and mw.ext.VariablesLua ) or nil
local WD_I18N = require( 'Module:WD/I18N' )
local tostring = tostring
local type = type
local tonumber = tonumber
local ipairs = ipairs
local pairs = pairs
local table_concat = table.concat
local string_format = string.format
local string_lower = string.lower
local uri_encode = mw.uri.encode
local uri_decode = mw.uri.decode
local uri_anchorEncode = mw.uri.anchorEncode
local uri_localUrl = mw.uri.localUrl
local t_trim = mw.text.trim
local t_gsplit = mw.text.gsplit
local t_nowiki = mw.text.nowiki
local ITEM_SEP = "⟭"
local FIELD_SEP = "⟬"
local PATH_SEP = "⟭"
local ROOT_SEP = "⟬"
----------------------------------------------------------------------
-- table.insert optimisé (comme Module:Debate)
----------------------------------------------------------------------
local function table_insert( t, a, b )
if b == nil then
return table.insert( t, a )
end
if type( a ) ~= "number" then
if type( b ) == "number" then
return table.insert( t, a )
end
return table.insert( t, b )
end
return table.insert( t, a, b )
end
local function push( t, s )
t[ #t + 1 ] = s
end
----------------------------------------------------------------------
-- VariablesLua wrappers (no-op si extension absente)
----------------------------------------------------------------------
local function vset( name, val )
if V and type( V.vardefine ) == "function" then
pcall( V.vardefine, name, val ~= nil and tostring( val ) or "" )
end
end
local function vget( name )
if V and type( V.var ) == "function" then
local ok, res = pcall( V.var, name )
if ok then return res end
end
return nil
end
----------------------------------------------------------------------
-- Helpers
----------------------------------------------------------------------
local function getArgs( frame )
local parent = frame:getParent()
local A = {}
local function add( k, v )
if v == nil then return end
v = t_trim( tostring( v ) )
if v ~= "" then
A[ k ] = v
end
end
if parent and parent.args then
for k, v in pairs( parent.args ) do
add( k, v )
end
end
if frame.args then
for k, v in pairs( frame.args ) do
add( k, v )
end
end
return A
end
local function toNumber( s, default )
local n = tonumber( s or "" )
return n or default or 0
end
local function wkSplit2( s, sep )
s = tostring( s or "" )
sep = tostring( sep or "" )
if sep == "" then
return s, ""
end
local i = s:find( sep, 1, true )
if not i then
return s, ""
end
return s:sub( 1, i - 1 ), s:sub( i + #sep )
end
local function escapeAttr( s )
s = tostring( s or "" )
return s
:gsub( "[\r\n\t]", " " )
:gsub( "&", "&" )
:gsub( "<", "<" )
:gsub( ">", ">" )
:gsub( '"', """ )
end
----------------------------------------------------------------------
-- PageForms : remplacement #formlink / #queryformlink (comme Module:Debate)
----------------------------------------------------------------------
local function wkDbKey( page )
local t = mw.title.new( page )
if not t then
return tostring( page or "" ):gsub( "%s", "_" )
end
return t.prefixedText:gsub( " ", "_" )
end
local function wkEncodeAddDataSegment( s )
s = tostring( s or "" )
s = s:gsub( "%%", "%%25" )
s = s:gsub( "%?", "%%3F" )
s = s:gsub( "#", "%%23" )
s = s:gsub( "/", "%%2F" )
return s
end
local function wkAddDataPath( formName, pageTitle )
local form = wkEncodeAddDataSegment( wkDbKey( formName ) )
local page = wkEncodeAddDataSegment( wkDbKey( pageTitle ) )
return "Special:AddData/" .. form .. "/" .. page
end
local function addDataLink( formName, pageTitle, linktext, tooltip )
formName = tostring( formName or "" )
pageTitle = tostring( pageTitle or "" )
linktext = tostring( linktext or "" )
tooltip = tostring( tooltip or "" )
if formName == "" or pageTitle == "" then
return ""
end
if linktext == "" then
linktext = " "
end
local target = wkAddDataPath( formName, pageTitle )
local link = string_format( '[[%s|%s]]', target, linktext )
if tooltip ~= "" then
return string_format(
'<span class="wk-adddata-link" data-wk-tooltip="%s">%s</span>',
escapeAttr( tooltip ),
link
)
end
return string_format( '<span class="wk-adddata-link">%s</span>', link )
end
local function wkRunQueryPath( formName )
local form = wkEncodeAddDataSegment( wkDbKey( formName ) )
return "Special:RunQuery/" .. form
end
local function normalizeTooltipText( s )
s = tostring( s or "" )
-- Apostrophes
s = s:gsub( "'", "’" )
-- Guillemets droits → typographiques
s = s:gsub( '"', '“' )
return s
end
local function wkRunQueryHtmlTagButton( formName, label, tooltip, query )
formName = tostring( formName or "" )
label = tostring( label or "" )
query = ( type( query ) == "table" ) and query or {}
tooltip = tostring( tooltip or "" )
tooltip = normalizeTooltipText( tooltip )
if formName == "" then
return ""
end
local title = wkRunQueryPath( formName )
local href = tostring( uri_localUrl( title, query ) )
local content = "+ " .. label
return F:preprocess(
'<htmltag tagname="a"'
.. ' href href="#"'
.. ' data-href="' .. escapeAttr( href ) .. '"'
.. ' class="wk-btn__a wk-js-nav"'
.. ( tooltip ~= "" and ( ' title="' .. escapeAttr( tooltip ) .. '"' ) or "" )
.. '>'
.. content
.. '</htmltag>'
)
end
----------------------------------------------------------------------
-- SMW access (pf #show) + cache VariablesLua
----------------------------------------------------------------------
local LOCAL_CACHE = {}
local function cache_get( key )
local v = LOCAL_CACHE[ key ]
if v ~= nil then
return v
end
local vv = vget( key )
if vv ~= nil and vv ~= "" then
LOCAL_CACHE[ key ] = vv
return vv
end
return nil
end
local function cache_set( key, value )
local v = value or ""
LOCAL_CACHE[ key ] = v
vset( key, v )
end
local function smwShow( frame, page, property )
page = t_trim( tostring( page or "" ) )
property = t_trim( tostring( property or "" ) )
if page == "" or property == "" then
return ""
end
local cacheKey = "WD:pf#show|" .. page .. "|" .. property
local cached = cache_get( cacheKey )
if cached ~= nil then
return cached
end
local out = ""
local ok, res = pcall( function()
return frame:callParserFunction( "#show", { page, "?" .. property } )
end )
if ok then
out = t_trim( tostring( res or "" ) )
end
cache_set( cacheKey, out )
return out
end
----------------------------------------------------------------------
-- SMW batch ask : une seule requête (avec tolérance FR/EN) + caches
----------------------------------------------------------------------
local ARGDATA_CACHE = {}
local PROP_CAND_CACHE = {}
local function getPropCandidates( lang, key )
local byLang = PROP_CAND_CACHE[ lang ]
if not byLang then
byLang = {}
PROP_CAND_CACHE[ lang ] = byLang
end
local cached = byLang[ key ]
if cached then
return cached
end
local out, seen = {}, {}
local function add( v )
v = t_trim( tostring( v or "" ) )
if v ~= "" and not seen[ v ] then
seen[ v ] = true
out[ #out + 1 ] = v
end
end
add( WD_I18N.msg( "ArgumentContent", lang, key ) )
add( WD_I18N.msg( "ArgumentContent", "fr", key ) )
add( WD_I18N.msg( "ArgumentContent", "en", key ) )
byLang[ key ] = out
return out
end
local function pushPrintouts( q, props )
for _, name in ipairs( props ) do
q[ #q + 1 ] = "?" .. name
end
end
local function normalizePageValue( v )
v = t_trim( tostring( v or "" ) )
v = v:gsub( "^%[%[:?(.-)%]%]$", "%1" )
v = v:gsub( "^(.-)|.*$", "%1" )
return t_trim( v )
end
local function firstFieldCandidates( r, candidates, isPage )
for _, key in ipairs( candidates or {} ) do
local v = r[ key ]
if v ~= nil then
if type( v ) == "table" then
v = v[ 1 ]
end
v = t_trim( tostring( v or "" ) )
if v ~= "" then
if isPage then
v = normalizePageValue( v )
end
return v
end
end
end
return ""
end
local function hasSMWAsk()
return type( mw.smw ) == "table" and type( mw.smw.ask ) == "function"
end
local function smwAskSafe( q )
if not hasSMWAsk() then
return nil
end
local ok, res = pcall( function() return mw.smw.ask( q ) end )
return ok and res or nil
end
local function smwGetArgumentData( pageTitle, lang )
pageTitle = t_trim( tostring( pageTitle or "" ) )
if pageTitle == "" then
return {}
end
local cacheKey = "WD:argDataByPage|" .. pageTitle
local cached = ARGDATA_CACHE[ cacheKey ]
if cached then
return cached
end
local data = {
pageTitle = pageTitle,
idArgument = "",
contenuArgument = "",
pageDebatDetaille = "",
pairesJustifsRaw = "",
pairesObjRaw = "",
bandeauxJustifsCsv = "",
bandeauxObjsCsv = "",
}
local P_ID = getPropCandidates( lang, "argument_number_prop" )
local P_CONTENT = getPropCandidates( lang, "argument_content_prop" )
local P_JUSTIFS = getPropCandidates( lang, "justifs_list_prop" )
local P_OBJS = getPropCandidates( lang, "objs_list_prop" )
local P_DETAILED = getPropCandidates( lang, "detailed_debate_prop" )
local P_WARN_J = getPropCandidates( lang, "justifs_warn_prop" )
local P_WARN_O = getPropCandidates( lang, "objs_warn_prop" )
local query = {
"[[ " .. pageTitle .. " ]]",
"mainlabel=page",
"limit=1",
}
pushPrintouts( query, P_ID )
pushPrintouts( query, P_CONTENT )
pushPrintouts( query, P_JUSTIFS )
pushPrintouts( query, P_OBJS )
pushPrintouts( query, P_DETAILED )
pushPrintouts( query, P_WARN_J )
pushPrintouts( query, P_WARN_O )
local res = smwAskSafe( query )
local row = res and res[ 1 ] or nil
if not row then
ARGDATA_CACHE[ cacheKey ] = data
return data
end
data.idArgument = firstFieldCandidates( row, P_ID, false )
data.contenuArgument = firstFieldCandidates( row, P_CONTENT, false )
data.pageDebatDetaille = firstFieldCandidates( row, P_DETAILED, true )
data.pairesJustifsRaw = firstFieldCandidates( row, P_JUSTIFS, false )
data.pairesObjRaw = firstFieldCandidates( row, P_OBJS, false )
data.bandeauxJustifsCsv = firstFieldCandidates( row, P_WARN_J, false )
data.bandeauxObjsCsv = firstFieldCandidates( row, P_WARN_O, false )
ARGDATA_CACHE[ cacheKey ] = data
return data
end
----------------------------------------------------------------------
-- Bandeaux (mémoïsés + template cache)
----------------------------------------------------------------------
local BANDEAUX_CACHE = {}
local BANNER_TPL_CACHE = {}
local function splitCSV( s, sep )
s = t_trim( tostring( s or "" ) )
if s == "" then return {} end
sep = sep or ","
local out = {}
for part in t_gsplit( s, sep, true ) do
part = t_trim( part )
if part ~= "" then
table_insert( out, part )
end
end
return out
end
local function expandBandeaux( frame, csv, lang )
csv = t_trim( tostring( csv or "" ) )
if csv == "" then
return ""
end
local ck = "WD:bandeaux|" .. lang .. "|" .. csv
local c = BANDEAUX_CACHE[ ck ]
if c ~= nil then
return c
end
local tplBanner = BANNER_TPL_CACHE[ lang ]
if not tplBanner then
tplBanner = WD_I18N.msg( "Argument.templates", lang, "banner_any" )
BANNER_TPL_CACHE[ lang ] = tplBanner
end
local html = {}
for _, item in ipairs( splitCSV( csv, "," ) ) do
local ok, res = pcall( function()
return frame:expandTemplate{ title = tplBanner, args = { item } }
end )
if ok and res and res ~= "" then
html[ #html + 1 ] = res
end
end
local out = table_concat( html, " " )
BANDEAUX_CACHE[ ck ] = out
return out
end
----------------------------------------------------------------------
-- Rendu principal
----------------------------------------------------------------------
function p.render( frame )
local args = getArgs( frame )
local lang = WD_I18N.getLangFromArgs( args )
local function msg( key, ... )
return WD_I18N.msg( "ArgumentContent", lang, key, ... )
end
local function file( key )
return WD_I18N.msg( "Common.files", lang, key )
end
-- Messages / props (canon)
local PROP_ARG_CONTENT = msg( "argument_content_prop" )
local TPL_ARG_CONTENT_EVAL = msg( "argument_content_eval_tpl" )
local PROP_ARGS_MAP = msg( "argument_map_prop" )
local TITLE_JUSTIFS = msg( "justifs_title" )
local TITLE_OBJS = msg( "objs_title" )
local NONE_JUS = msg( "none_justifications_msg" )
local NONE_OBJ = msg( "none_objections_msg" )
local NONE_CONTENT = msg( "none_content_msg" )
local EDIT = msg( "edit_label" )
local EDIT_J_TT = msg( "edit_justifs_tt" )
local EDIT_O_TT = msg( "edit_objs_tt" )
local ADD_ARG = msg( "add_argument_label" )
local ADD_OBJ = msg( "add_objection_label" )
-- Params
local pageArgument = uri_decode( t_trim( args.argument or "" ) )
local cheminRaw = t_trim( args.path or args.chemin or "" )
local levelRaw = toNumber( args.level or args.niveau, 0 )
local niveau = levelRaw + 1
local typeArg = t_trim( args.type or "" )
local avertissements = t_trim( args.warnings or args.avertissements or "" )
local pageParam = t_trim( args.page or "" )
local editableRaw = string_lower( t_trim( args.editable or "" ) )
local isEditable = ( editableRaw == "yes" or editableRaw == "oui" or editableRaw == "true" or editableRaw == "1" )
-- Racine depuis chemin (gsplit C)
local racine = ""
if cheminRaw ~= "" and cheminRaw:find( PATH_SEP, 1, true ) then
local last = ""
for part in t_gsplit( cheminRaw, PATH_SEP, true ) do
part = t_trim( part )
if part ~= "" then
last = part
end
end
if last ~= "" and last:find( ROOT_SEP, 1, true ) then
local _, r = wkSplit2( last, ROOT_SEP )
racine = t_trim( tostring( r or "" ) )
end
end
-- Pré-calculs attributs
local nowikiPage = t_nowiki( pageParam or "" )
local nowikiRoot = t_nowiki( racine or "" )
local nowikiPath = t_nowiki( cheminRaw or "" )
local nowikiWarn = t_nowiki( avertissements or "" )
local function uencQ( s )
return uri_encode( s or "", "QUERY" )
end
local function aenc( s )
return uri_anchorEncode( s or "" )
end
-- SMW : une requête
local data = smwGetArgumentData( pageArgument, lang )
local pageTitle = data.pageTitle or pageArgument
local idArgument = data.idArgument or ""
local contenuArgument = data.contenuArgument or ""
local pageDebatDetaille = data.pageDebatDetaille or ""
local pairesJustifsRaw = data.pairesJustifsRaw or ""
local pairesObjRaw = data.pairesObjRaw or ""
local bandeauxJustifsCsv = data.bandeauxJustifsCsv or ""
local bandeauxObjsCsv = data.bandeauxObjsCsv or ""
-- Si aucun contenu
if contenuArgument == "" and pairesJustifsRaw == "" and pairesObjRaw == "" and pageDebatDetaille == "" then
return '<div class="aucun-contenu">' .. NONE_CONTENT .. '</div>__NOTOC__'
end
-- Bandeaux (argument)
local avertHtml = ""
if avertissements ~= "" then
avertHtml = expandBandeaux( frame, avertissements, lang )
end
-- Wrapper éditable (inchangé)
local preOpen, preClose = "", ""
if isEditable and levelRaw == 1 then
preOpen = string_format( '<div class="contenu-argument-%s">', typeArg or "" )
preClose = '</div>'
end
local bodyHtml = {}
push( bodyHtml, string_format( '<div class="contenu-argument">%s</div>', contenuArgument or "" ) )
-- Débat détaillé
if pageDebatDetaille ~= "" then
local carteArgs = smwShow( frame, pageDebatDetaille, PROP_ARGS_MAP )
push( bodyHtml,
string_format(
'<div class="carte-debat-detaille onglet-externe">'
.. '<div class="titre-debat-detaille">%s</div>'
.. '%s'
.. '</div>',
msg( "detailed_debate_title", pageDebatDetaille ),
carteArgs or ""
)
)
else
------------------------------------------------------------------
-- List builder (gsplit + split2) : évite split() Lua imbriqués
------------------------------------------------------------------
local function buildList( pairesRaw, sens, sensInverseOpt )
pairesRaw = t_trim( tostring( pairesRaw or "" ) )
if pairesRaw == "" then
return ""
end
local out = {}
local sensInverse = sensInverseOpt
local typePour = ( typeArg == "pour" )
local tpl = ( isEditable and PROP_ARG_CONTENT ) or TPL_ARG_CONTENT_EVAL
local hasAny = false
for brut in t_gsplit( pairesRaw, ITEM_SEP, true ) do
brut = t_trim( brut )
if brut ~= "" then
local id, titre = wkSplit2( brut, FIELD_SEP )
id = t_trim( id )
titre = t_trim( titre )
if titre == "" then
titre = id
end
if id ~= "" or titre ~= "" then
hasAny = true
local titreEnc = uencQ( titre )
local idEnc = uencQ( id )
local t = sens
if sensInverse then
t = typePour and "contre" or "pour"
end
push( out,
string_format(
'<div class="argument">'
.. '<div id="%s" class="argument-title level-%d level-sup"'
.. ' data-template="%s"'
.. ' data-page="%s"'
.. ' data-argument="%s"'
.. ' data-type="%s"'
.. ' data-level="%d"'
.. ' data-root="%s"'
.. ' data-path="%s⟭%s⟬%s"'
.. ' data-warnings="%s">'
.. '[[File:%s|11px|link=|alt=|class=fleche-deroulante]]'
.. '[[File:%s|17px|link=|alt=|class=pictogramme-h4 mw-no-invert]]'
.. '<span class="argument-label">%s</span>'
.. '</div>'
.. '<div class="argument-wrapper"></div>'
.. '</div>',
aenc( titre ),
niveau,
tpl,
nowikiPage,
escapeAttr( idEnc ),
t_nowiki( t ),
niveau,
nowikiRoot,
nowikiPath,
t_nowiki( t ),
escapeAttr( titreEnc ),
nowikiWarn,
file( "expand" ),
file( ( t == "pour" ) and "arg_pro" or "arg_con" ),
t_nowiki( titre )
)
)
end
end
end
if not hasAny then
return '<div class="aucun-argument">' .. ( sensInverse and NONE_OBJ or NONE_JUS ) .. '</div>'
end
return table_concat( out, "" )
end
-- Forms (noms venant i18n)
local formJust = msg( "justifications_form" )
local formObj = msg( "objections_form" )
local formNew = msg( "new_argument_title_field" )
local formType = msg( "form_type" )
local modJust, modObj, btnJust, btnObj = "", "", "", ""
if isEditable then
-- Remplace #formlink : Special:AddData/Form/Page
modJust = addDataLink( formJust, pageTitle, EDIT, EDIT_J_TT )
modObj = addDataLink( formObj, pageTitle, EDIT, EDIT_O_TT )
-- Remplace #queryformlink : Special:RunQuery/Form + query + _run
local typeJust = msg( "justification_type" )
local typeObj = msg( "objection_type" )
local qJust = {}
qJust[ formNew .. "[" .. formType .. "]" ] = ( typeJust or "" )
qJust[ formNew .. "[ID]" ] = ( idArgument or "" )
qJust[ "_run" ] = "1"
local qObj = {}
qObj[ formNew .. "[" .. formType .. "]" ] = ( typeObj or "" )
qObj[ formNew .. "[ID]" ] = ( idArgument or "" )
qObj[ "_run" ] = "1"
btnJust = wkRunQueryHtmlTagButton(
formNew,
ADD_ARG,
msg( "add_justif_tt", pageArgument or "" ),
qJust
)
btnObj = wkRunQueryHtmlTagButton(
formNew,
ADD_OBJ,
msg( "add_obj_tt", pageArgument or "" ),
qObj
)
end
-- Icons
local iconJustKey = ( typeArg == "pour" ) and "arg_pro" or "arg_con"
local iconObjKey = ( typeArg == "pour" ) and "arg_con" or "arg_pro"
local iconJust = '[[File:' .. file( iconJustKey ) .. '|17px|link=|alt=|class=pictogramme-h3 mw-no-invert]]'
local iconObj = '[[File:' .. file( iconObjKey ) .. '|17px|link=|alt=|class=pictogramme-h3 mw-no-invert]]'
local gauche = string_format(
'<div class="colonne-gauche">'
.. '<div class="NavFramex %s">'
.. '<div class="NavHead"><span class="titre-boite">%s%s</span>%s</div>'
.. '<div class="NavContent">'
.. '<div>%s</div>'
.. '%s'
.. '</div>'
.. '%s'
.. '</div>'
.. '</div>',
( typeArg == "pour" ) and "bordure-argument-pour" or "bordure-argument-contre",
iconJust,
TITLE_JUSTIFS,
( isEditable and ( '<span class="modifier-section">' .. modJust .. '</span>' ) or "" ),
expandBandeaux( frame, bandeauxJustifsCsv, lang ),
( pairesJustifsRaw ~= "" and buildList( pairesJustifsRaw, typeArg, false ) or ( '<div class="aucun-argument">' .. NONE_JUS .. '</div>' ) ),
( isEditable and ( '<div class="NavButton"><div class="bouton-ajouter wk-btn mw-ui-button navigation-not-searchable">' .. btnJust .. '</div></div>' ) or "" )
)
local droite = string_format(
'<div class="colonne-droite">'
.. '<div class="NavFramex %s">'
.. '<div class="NavHead"><span class="titre-boite">%s%s</span>%s</div>'
.. '<div class="NavContent">'
.. '<div>%s</div>'
.. '%s'
.. '</div>'
.. '%s'
.. '</div>'
.. '</div>',
( typeArg == "pour" ) and "bordure-argument-contre" or "bordure-argument-pour",
iconObj,
TITLE_OBJS,
( isEditable and ( '<span class="modifier-section">' .. modObj .. '</span>' ) or "" ),
expandBandeaux( frame, bandeauxObjsCsv, lang ),
( pairesObjRaw ~= "" and buildList( pairesObjRaw, typeArg, true ) or ( '<div class="aucun-argument">' .. NONE_OBJ .. '</div>' ) ),
( isEditable and ( '<div class="NavButton"><div class="bouton-ajouter wk-btn mw-ui-button navigation-not-searchable">' .. btnObj .. '</div></div>' ) or "" )
)
push( bodyHtml, '<div class="colonnes">' )
push( bodyHtml, gauche )
push( bodyHtml, droite )
push( bodyHtml, '</div>' )
end
local html = {}
if avertHtml ~= "" then
push( html, string_format( '<div>%s</div>', avertHtml ) )
end
push( html, preOpen )
push( html, table_concat( bodyHtml, "" ) )
push( html, preClose )
push( html, "__NOTOC__" )
return table_concat( html, "" )
end
return p