Module:Argument
La documentation pour ce module peut être créée à Module:Argument/doc
-- Module:Argument (i18n multi-lang) — Optimisé / Purifié
-- Objectif : rendu du modèle « Argument » (MediaWiki 1.43) fidèle au rendu existant
local p = {}
----------------------------------------------------------------------
-- Accès global au frame + VariablesLua (accélération sans changer le rendu)
----------------------------------------------------------------------
local mw = mw
local F = mw.getCurrentFrame()
local V = (mw.ext and mw.ext.VariablesLua) or nil
local WD_I18N = require('Module:WD/I18N')
-- Locaux (micro-optis)
local tostring = tostring
local type = type
local tonumber = tonumber
local ipairs = ipairs
local pairs = pairs
local table_concat = table.concat
local table_insert = table.insert
local table_remove = table.remove
local table_sort = table.sort
local t_trim = mw.text.trim
local t_gsplit = mw.text.gsplit
local t_nowiki = mw.text.nowiki
local t_jsonEncode = mw.text.jsonEncode
local t_jsonDecode = mw.text.jsonDecode
local html_create = mw.html.create
local mw_title_new = mw.title.new
local mw_uri_encode = mw.uri.encode
local mw_uri_decode = mw.uri.decode
local mw_uri_fullUrl = mw.uri.fullUrl
local mw_uri_localUrl = mw.uri.localUrl
local uri_localUrl = mw.uri.localUrl
local mw_util = mw.util
local mw_util_getUrl = (mw_util and mw_util.getUrl) or nil
local mw_hash = (mw.hash and type(mw.hash.hashValue) == "function") and mw.hash or nil
local string_format = string.format
local gsub = string.gsub
local ITEM_SEP = "⟭"
local FIELD_SEP = "⟬"
local QUOTE_ITEM_SEP = "⟭⟭⟭"
local QUOTE_FIELD_SEP = "⟬⟬⟬"
----------------------------------------------------------------------
-- Anchor intégré (remplace Module:Anchor)
----------------------------------------------------------------------
local function anchorId( s )
s = tostring( s or "" )
-- Normalisation des apostrophes HTML éventuelles
s = gsub( s, "'", "'" )
s = gsub( s, "'", "'" )
s = gsub( s, "'", "'" )
-- Nettoyage minimal
s = gsub( s, "%s+", " " )
s = t_trim( s )
-- Espaces → underscore
s = gsub( s, " ", "_" )
-- Suppression caractères cassants (HTML / wiki)
s = gsub( s, '[%[%]{}|#<>"]', "" )
return s
end
----------------------------------------------------------------------
-- wrappers VariablesLua (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
local function detectLang(args)
return WD_I18N.getLangFromArgs(args or {})
end
----------------------------------------------------------------------
-- I18N : cache local (par rendu)
----------------------------------------------------------------------
local I18N_DOMAIN = {}
local I18N_MSG = {}
local function L(lang, section, key, ...)
lang = tostring(lang or "fr")
section = tostring(section or "")
key = tostring(key or "")
local domain = I18N_DOMAIN[section]
if not domain then
domain = "Argument." .. section
I18N_DOMAIN[section] = domain
end
if select("#", ...) == 0 then
local k = lang .. "\n" .. domain .. "\n" .. key
local hit = I18N_MSG[k]
if hit ~= nil then
return hit
end
local text = WD_I18N.msg(domain, lang, key)
I18N_MSG[k] = text
return text
end
return WD_I18N.msg(domain, lang, key, ...)
end
----------------------------------------------------------------------
-- Caches (bornage mémoire) — Patch #6
----------------------------------------------------------------------
local function cacheMake(max)
return { data = {}, order = {}, max = max or 200 }
end
local function cacheGet(C, k)
return C.data[k]
end
local function cachePut(C, k, v)
if C.data[k] ~= nil then
C.data[k] = v
return
end
C.data[k] = v
table_insert(C.order, k)
if #C.order > (C.max or 200) then
local old = table_remove(C.order, 1)
if old ~= nil then
C.data[old] = nil
end
end
end
-- Mémoïsation expandTemplate (bandeaux, schemas, etc.)
local EXPAND_CACHE = cacheMake(250)
----------------------------------------------------------------------
-- Helpers
----------------------------------------------------------------------
----------------------------------------------------------------------
-- Tooltip normalize (apostrophes / guillemets)
----------------------------------------------------------------------
local function normalizeTooltipText(s)
s = tostring(s or "")
if s == "" then return "" end
s = s:gsub("'", "’")
s = s:gsub('"', "“")
return s
end
local canonicalTitle
local function fastParamsKey(params)
if type(params) ~= "table" then
return ""
end
local n = 0
for _ in pairs(params) do
n = n + 1
if n > 2 then
return nil
end
end
if n == 1 and params[1] ~= nil then
return "1=" .. tostring(params[1])
end
if n == 2 then
local k1, k2 = nil, nil
for k in pairs(params) do
if not k1 then
k1 = k
else
k2 = k
end
end
local a, b = tostring(k1), tostring(k2)
if a > b then
k1, k2 = k2, k1
end
return tostring(k1) .. "=" .. tostring(params[k1]) .. "&" .. tostring(k2) .. "=" .. tostring(params[k2])
end
return nil
end
local function safeJsonEncode(obj)
local ok, s = pcall(t_jsonEncode, obj)
if ok and type(s) == "string" then
return s
end
return tostring(obj or "")
end
local function expand(frame, title, params)
local ok, res = pcall(function()
return frame:expandTemplate{ title = title, args = params or {} }
end)
return ok and (res or "") or ""
end
local function expandCached(frame, title, params)
local pk = fastParamsKey(params)
local k
if pk then
k = tostring(title or "") .. "\n" .. pk
else
k = tostring(title or "") .. "\n" .. safeJsonEncode(params or {})
end
local hit = cacheGet(EXPAND_CACHE, k)
if hit ~= nil then
return hit
end
local out = expand(frame, title, params)
cachePut(EXPAND_CACHE, k, out)
return out
end
local function hasSMW()
return type(mw.smw) == "table" and type(mw.smw.set) == "function"
end
local function escapeAttr(s)
s = tostring(s or "")
return s
:gsub("[\r\n\t]", " ")
:gsub("&", "&")
:gsub("<", "<")
:gsub(">", ">")
:gsub('"', """)
end
local function getArgs(frame)
local A = {}
local function addArgs(src)
if not src then return end
for k, v in pairs(src) do
if type(v) == "string" and v ~= "" then
A[k] = v
end
end
end
local parent = frame:getParent()
if parent and parent.args then
addArgs(parent.args)
end
if frame and frame.args then
addArgs(frame.args)
end
return A
end
local function splitCSV(s, sep)
if not s or s == "" then return {} end
sep = sep or ","
if not s:find(sep, 1, true) then
local one = t_trim(s)
if one ~= "" then
return { one }
end
return {}
end
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
-- Patch #1 : split littéral multi-caractères (ex: "⟭")
local function splitByLiteral(s, sep, maxParts)
s = tostring(s or "")
sep = tostring(sep or "")
if s == "" or sep == "" then
return { s }
end
local out = {}
local i = 1
local n = 0
local seplen = #sep
while true do
local j = s:find(sep, i, true)
if not j then
n = n + 1
out[n] = s:sub(i)
break
end
n = n + 1
out[n] = s:sub(i, j - 1)
i = j + seplen
if maxParts and n >= maxParts then
n = n + 1
out[n] = s:sub(i)
break
end
end
return out
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 wkExtractItemsFromSerializedData( raw )
raw = tostring( raw or "" )
raw = t_trim( raw )
if raw == "" then
return {}
end
-- Heuristique: si aucun séparateur, pas notre format
if not raw:find( ITEM_SEP, 1, true ) then
return {}
end
local items = {}
local blocks = splitByLiteral( raw, ITEM_SEP )
for _, block in ipairs( blocks ) do
block = t_trim( block )
if block ~= "" then
local page, rest = wkSplit2( block, FIELD_SEP )
local title, warnings = wkSplit2( rest, FIELD_SEP )
page = t_trim( page )
if page ~= "" then
title = t_trim( title )
warnings = t_trim( warnings )
if title == "" then
title = page
end
table_insert( items, {
page = page,
title = title,
warnings = warnings
} )
end
end
end
return items
end
local function pf_escape(v)
if not v then return "" end
v = tostring(v)
v = v:gsub("|", "|"):gsub("=", "=")
return v
end
local TITLE_CACHE = cacheMake(600)
local function titleNewCached(s)
s = tostring(s or "")
local hit = cacheGet(TITLE_CACHE, s)
if hit ~= nil then
return (hit ~= false) and hit or nil
end
local t = mw_title_new(s)
cachePut(TITLE_CACHE, s, t or false)
return t
end
local function wkDbKey(page)
page = tostring(page or "")
if page == "" then
return ""
end
if not page:find(":", 1, true) and not page:find("[%[%]{}|#<>]") then
return page:gsub(" ", "_")
end
local t = titleNewCached(page)
if not t then
return page: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 "")
if formName == "" or pageTitle == "" then
return ""
end
local target = wkAddDataPath(formName, pageTitle)
local lt = linktext or " "
local tt = normalizeTooltipText(tostring(tooltip or ""))
if tt ~= "" then
return string.format(
'<span class="wk-adddata-link" data-wk-tooltip="%s">[[%s|%s]]</span>',
escapeAttr(tt),
target,
lt
)
end
return string.format('[[%s|%s]]', target, lt)
end
local function wkEncodeRunQuerySegment(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 wkRunQueryPath(formName)
local form = wkEncodeRunQuerySegment(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="#"'
.. ' data-href="' .. escapeAttr( href ) .. '"'
.. ' class="wk-btn__a wk-js-nav"'
.. ( tooltip ~= "" and ( ' title="' .. escapeAttr( tooltip ) .. '"' ) or "" )
.. '>'
.. content
.. '</htmltag>'
)
end
local function absUrlFromTitle(title, q)
local ok, url = pcall(mw_uri_fullUrl, title, q)
if ok and url then
return tostring(url)
end
local server = ""
if mw.site and mw.site.server then
server = tostring(mw.site.server or "")
end
if mw_util_getUrl then
return server .. tostring(mw_util_getUrl(title, q))
end
return server .. tostring(mw_uri_localUrl(title, q))
end
local function runQueryLink(opts)
opts = opts or {}
local formName = tostring(opts.form or "")
local fieldName = tostring(opts.field or "")
local value = tostring(opts.value or "")
if formName == "" or fieldName == "" or value == "" then
return ""
end
local title = tostring(opts._title or "")
if title == "" then
title = wkRunQueryPath(formName)
end
local q = {}
local qkey = tostring(opts._qkey or "")
if qkey == "" then
qkey = formName .. "[" .. fieldName .. "]"
end
q[qkey] = value
local extra = opts.extra
if type(extra) == "table" then
for k, v in pairs(extra) do
local kk = tostring(k or "")
if kk ~= "" and v ~= nil then
local vv = tostring(v)
if vv ~= "" then
if kk:find("%[", 1, true) then
q[kk] = vv
else
q[formName .. "[" .. kk .. "]"] = vv
end
end
end
end
end
q["_run"] = "1"
local url = absUrlFromTitle(title, q)
local label = tostring(opts.label or value)
local tt = normalizeTooltipText(tostring(opts.tooltip or ""))
local link = "[" .. url .. " " .. label .. "]"
if tt ~= "" then
return string.format(
'<span class="wk-adddata-link masquer-externe" data-wk-tooltip="%s">%s</span>',
escapeAttr(tt),
link
)
end
return string.format('<span class="wk-adddata-link">%s</span>', link)
end
local function pf_queryformlink(opts)
opts = opts or {}
local parts = { "#queryformlink:" }
table_insert(parts, string.format("form=%s", pf_escape(opts.form or "")))
if opts.link_text and opts.link_text ~= "" then
table_insert(parts, string.format("link text=%s", pf_escape(opts.link_text)))
end
if opts.tooltip and opts.tooltip ~= "" then
table_insert(parts, string.format("tooltip=%s", pf_escape(normalizeTooltipText(opts.tooltip))))
end
table_insert(parts, string.format("link type=%s", pf_escape(opts.link_type or "post button")))
if opts.target and opts.target ~= "" then
if tostring(opts.target):find("{{", 1, true) then
table_insert(parts, "target=" .. opts.target)
else
table_insert(parts, string.format("target=%s", pf_escape(opts.target)))
end
end
if opts.query_string and opts.query_string ~= "" then
table_insert(parts, "query string=" .. opts.query_string)
end
local src = "{{" .. table_concat(parts, "|") .. "}}"
local ok, out = pcall(F.preprocess, F, src)
return (ok and out) or ""
end
local function listBreaker()
return "\n\n<!--__WD_LIST_BREAK__-->"
end
----------------------------------------------------------------------
-- SMW : props refs (i18n via PF) — comme Debate
----------------------------------------------------------------------
local function pfProp( PF, k, fallback )
local v = PF and PF[ k ] or ""
v = t_trim( tostring( v or "" ) )
if v ~= "" then
return v
end
return fallback or ""
end
local function smwPropRef( PF, key )
-- Utilise PF.prop_* (i18n), fallback sur tes noms existants
if key == "authors" then
return pfProp( PF, "prop_author" )
end
if key == "article_name" then
return pfProp( PF, "prop_article_name" )
end
if key == "work_name" then
return pfProp( PF, "prop_work_name" )
end
if key == "publishing_house" then
return pfProp( PF, "prop_publishing_house" )
end
if key == "place_of_publication" then
return pfProp( PF, "prop_place_of_publication" )
end
return ""
end
----------------------------------------------------------------------
-- SMW batch : queue + flush unique
----------------------------------------------------------------------
local function smwQueueAdd(pending, key, val, multi)
if not pending or not key or key == "" or val == nil then
return
end
if type(val) == "string" then
val = t_trim(val)
if val == "" then
return
end
end
local cur = pending[key]
if multi then
if cur == nil then
pending[key] = (type(val) == "table") and val or { val }
return
end
if type(cur) ~= "table" then
cur = { cur }
pending[key] = cur
end
if type(val) == "table" then
for _, x in ipairs(val) do
if x ~= nil then
cur[#cur + 1] = x
end
end
else
cur[#cur + 1] = val
end
return
end
pending[key] = val
end
----------------------------------------------------------------------
-- Minibandeaux (summary-warnings) — rendu natif (sans modèle)
----------------------------------------------------------------------
local JUSTIFICATION_WARNING_BANNER_KEYS = {
"justifications_to_reorganize",
}
local OBJECTION_WARNING_BANNER_KEYS = {
"objections_to_reorganize",
}
local QUOTE_WARNING_BANNER_KEYS = {
"quote_too_short",
"quote_too_long",
"quote_ref_incomplete",
}
local REFERENCE_WARNING_BANNER_KEYS = {
"reference_quality_insufficient",
"reference_incomplete",
"dead_link",
}
local function subMsg( lang, key, ... )
return L( lang, "subsection_banners", key, ... ) or ""
end
local SUMMARY_WARNING_BANNER_KEYS = {
"summary_disadvantageous",
"summary_to_be_written",
"summary_unclear",
"summary_too_long",
"summary_to_separate",
"summary_style_to_review",
}
local SUMMARY_WARNING_ALIASES = nil
local function buildSummaryAliases( lang )
local a = {}
for _, key in ipairs( SUMMARY_WARNING_BANNER_KEYS ) do
local alias = subMsg( lang, key .. "_alias" )
alias = t_trim( tostring( alias or "" ) )
if alias ~= "" then
for _, one in ipairs( splitCSV( alias, "," ) ) do
one = t_trim( tostring( one or "" ) )
if one ~= "" then
a[ one ] = key
end
end
end
end
return a
end
local function buildSummaryLabelLookup( lang, keys )
local lookup = {}
for _, key in ipairs( keys or {} ) do
local lab = t_trim( subMsg( lang, key .. "_label" ) )
if lab ~= "" then
lookup[ lab ] = key
end
end
return lookup
end
-- Résolveur générique : label -> key, alias -> key, ou key directe si autorisée
local function resolveKeyFromLookup(raw, lookup, allowedKeys, aliases)
raw = t_trim(tostring(raw or ""))
if raw == "" then
return ""
end
-- 1) Match par label i18n
local k = (lookup and lookup[raw]) or ""
if k ~= "" then
return k
end
-- 2) Match par alias (optionnel)
if aliases and aliases[raw] then
return aliases[raw]
end
-- 3) Match direct si l'utilisateur a mis la key brute
if allowedKeys then
for _, kk in ipairs(allowedKeys) do
if raw == kk then
return kk
end
end
end
return ""
end
local function renderSubsectionBanner( pv, lang, key, opts, cats )
key = tostring( key or "" )
if key == "" then
return ""
end
opts = opts or {}
local isReorg = opts.reorg == true
local icon = tostring( opts.icon or "" )
local iconAlt = tostring( opts.icon_alt or "" )
local linkHtml = ""
local title = ""
local text = ""
-- Lien de réorganisation (optionnel) — AddData (même source que le H2)
if isReorg then
local formName = tostring( opts.form or "" )
local tooltip = tostring( opts.tooltip or "" )
local linkLabel = tostring( opts.link_label or "" )
if linkLabel == "" then
linkLabel = L( lang, "text", "reorganize_link_label" )
end
if formName ~= "" then
linkHtml = addDataLink( formName, pv.rawTitle, linkLabel, tooltip )
end
end
-- Icon defaults (i18n)
if icon == "" then
icon = isReorg
and L( lang, "files", "reorganize_banner_icon" )
or L( lang, "files", "summary_banner_icon" )
end
if iconAlt == "" then
iconAlt = isReorg
and L( lang, "text", "reorganize_banner_icon_alt" )
or L( lang, "text", "summary_banner_icon_alt" )
end
-- Titre/texte (mêmes messages i18n que tes fonctions actuelles)
if isReorg and linkHtml ~= "" then
title = subMsg( lang, key .. "_title" )
title = t_trim( tostring( title or "" ) )
if title ~= "" then
title = title .. " " .. linkHtml
else
title = linkHtml
end
else
title = subMsg( lang, key .. "_title", pv.rawTitle )
end
text = subMsg( lang, key .. "_text", pv.rawTitle )
if title == "" and text == "" then
return ""
end
local infoIcon = L( lang, "files", "info_icon" )
local infoAlt = L( lang, "text", "info_icon_alt" )
local html =
'<div class="bandeau-avertissement">'
.. '<span style="margin-right: 0.5em; position: relative; bottom: 1px;">'
.. '[[File: ' .. icon .. ' | 18px | link= | alt=' .. escapeAttr( iconAlt ) .. ']]'
.. '</span>'
.. title
.. '<span class="smw-highlighter smwttinline" data-state="inline">'
.. '[[File: ' .. infoIcon .. ' | 13px | link= | alt=' .. escapeAttr( infoAlt ) .. ' | class=logo-aide mw-no-invert]]'
.. '<div class="smwttcontent">' .. text .. '</div>'
.. '</span>'
.. '</div>'
local cat = subMsg( lang, key .. "_category" )
if cats and cat and cat ~= "" then
table_insert( cats, "[[Category:" .. cat .. "]]" )
end
return html
end
local function wkParseBiblioMarkers(raw)
raw = tostring(raw or "")
raw = t_trim(raw)
if raw == "" then
return {}
end
if not raw:find(FIELD_SEP, 1, true) and not raw:find(ITEM_SEP, 1, true) then
return {}
end
local items = {}
local blocks = splitByLiteral(raw, ITEM_SEP)
for _, block in ipairs(blocks) do
block = t_trim(block)
if block ~= "" then
local f = {}
local parts = splitByLiteral(block, FIELD_SEP)
for _, part in ipairs(parts) do
f[#f + 1] = t_trim(tostring(part or ""))
end
for i = #f + 1, 12 do
f[i] = ""
end
items[#items + 1] = {
authors = f[1],
article = f[2],
work = f[3],
volume = f[4],
number = f[5],
location = f[6],
page = f[7],
publisher = f[8],
place = f[9],
date = f[10],
link = f[11],
warnings = f[12]
}
end
end
return items
end
local function wkParseWeblioMarkers(raw)
raw = tostring(raw or "")
raw = t_trim(raw)
if raw == "" then
return {}
end
if not raw:find(FIELD_SEP, 1, true) and not raw:find(ITEM_SEP, 1, true) then
return {}
end
local items = {}
local blocks = splitByLiteral(raw, ITEM_SEP)
for _, block in ipairs(blocks) do
block = t_trim(block)
if block ~= "" then
local f = {}
local parts = splitByLiteral(block, FIELD_SEP)
for _, part in ipairs(parts) do
f[#f + 1] = t_trim(tostring(part or ""))
end
for i = #f + 1, 6 do
f[i] = ""
end
items[#items + 1] = {
page = f[1],
site = f[2],
link = f[3],
authors = f[4],
date = f[5],
warnings = f[6]
}
end
end
return items
end
local function wkParseVideoMarkers(raw)
raw = tostring(raw or "")
raw = t_trim(raw)
if raw == "" then
return {}
end
if not raw:find(FIELD_SEP, 1, true) and not raw:find(ITEM_SEP, 1, true) then
return {}
end
local items = {}
local blocks = splitByLiteral(raw, ITEM_SEP)
for _, block in ipairs(blocks) do
block = t_trim(block)
if block ~= "" then
local f = {}
local parts = splitByLiteral(block, FIELD_SEP)
for _, part in ipairs(parts) do
f[#f + 1] = t_trim(tostring(part or ""))
end
for i = #f + 1, 4 do
f[i] = ""
end
items[#items + 1] = {
title = f[1],
link = f[2],
authors = f[3],
warnings = f[4]
}
end
end
return items
end
local function renderReferenceItemBanners(itemWarnings, pv, lang, cats, out)
local warnsRef = t_trim(tostring(itemWarnings or ""))
if warnsRef == "" then
return
end
local lookup = buildSummaryLabelLookup(lang, REFERENCE_WARNING_BANNER_KEYS)
for _, x in ipairs(splitCSV(warnsRef, ",")) do
local key = resolveKeyFromLookup(x, lookup, REFERENCE_WARNING_BANNER_KEYS)
if key ~= "" then
table_insert(out, renderSubsectionBanner(pv, lang, key, nil, cats))
end
end
end
local function wkRenderOneBiblioLi( item, pv, lang, cats, PF, smwPending, args )
local out = {}
local authors = t_trim( item.authors or "" )
if authors == "" then
authors = PF.unknown_author
end
out[ #out + 1 ] = authors
do
local k = smwPending and smwPropRef( PF, "authors" ) or ""
if k ~= "" then
smwQueueAdd( smwPending, k, authors, true )
end
end
local article = t_trim( item.article or "" )
local work = t_trim( item.work or "" )
local link = t_trim( item.link or "" )
do
if smwPending then
local ka = smwPropRef( PF, "article_name" )
if ka ~= "" and article ~= "" then
smwQueueAdd( smwPending, ka, article, true )
end
local kw = smwPropRef( PF, "work_name" )
if kw ~= "" and work ~= "" then
smwQueueAdd( smwPending, kw, work, true )
end
end
end
if article ~= "" then
if link ~= "" then
out[ #out + 1 ] = ", « [" .. link .. " " .. article .. "] »"
else
out[ #out + 1 ] = ", « " .. article .. " »"
end
end
if work ~= "" then
if article ~= "" then
out[ #out + 1 ] = ", ''" .. work .. "''"
else
if link ~= "" then
out[ #out + 1 ] = ", ''[" .. link .. " " .. work .. "]''"
else
out[ #out + 1 ] = ", ''" .. work .. "''"
end
end
end
local volume = t_trim( item.volume or "" )
if volume ~= "" then
out[ #out + 1 ] = ", " .. volume
end
local number = t_trim( item.number or "" )
if number ~= "" then
out[ #out + 1 ] = ", n°" .. number
end
local location = t_trim( item.location or "" )
if location ~= "" then
out[ #out + 1 ] = ", " .. location
end
local page = t_trim( item.page or "" )
if page ~= "" then
out[ #out + 1 ] = ", p." .. page
end
local publisher = t_trim( item.publisher or "" )
if publisher ~= "" then
out[ #out + 1 ] = ", " .. publisher
do
local kp = smwPending and smwPropRef( PF, "publishing_house" ) or ""
if kp ~= "" then
smwQueueAdd( smwPending, kp, publisher, true )
end
end
end
local place = t_trim( item.place or "" )
if place ~= "" then
out[ #out + 1 ] = ", " .. place
do
local kp = smwPending and smwPropRef( PF, "place_of_publication" ) or ""
if kp ~= "" then
smwQueueAdd( smwPending, kp, place, true )
end
end
end
local date = t_trim( item.date or "" )
if date ~= "" then
out[ #out + 1 ] = ", " .. date
end
renderReferenceItemBanners(item.warnings, pv, lang, cats, out)
return "<li>" .. table_concat( out ) .. "</li>"
end
local function wkRenderOneWeblioLi( item, pv, lang, cats, PF, smwPending, args )
local out = {}
local page = t_trim( item.page or "" )
local site = t_trim( item.site or "" )
local link = t_trim( item.link or "" )
local date = t_trim( item.date or "" )
local authors = t_trim( item.authors or "" )
do
local k = smwPending and smwPropRef( PF, "authors" ) or ""
if k ~= "" and authors ~= "" then
smwQueueAdd( smwPending, k, authors, true )
end
end
do
local label = ""
if page ~= "" then
label = page
elseif site ~= "" then
label = site
end
if link ~= "" and label ~= "" then
out[ #out + 1 ] = "[" .. link .. " " .. label .. "]"
else
out[ #out + 1 ] = (page ~= "" and page) or site
end
end
if site ~= "" and page ~= "" then
out[ #out + 1 ] = ", ''" .. site .. "''"
end
if authors ~= "" then
out[ #out + 1 ] = ", " .. authors
end
if date ~= "" then
out[ #out + 1 ] = ", " .. date
end
renderReferenceItemBanners(item.warnings, pv, lang, cats, out)
return "<li>" .. table_concat( out ) .. "</li>"
end
local function wkRenderOneVideoLi( item, pv, lang, cats, PF, smwPending, args )
local out = {}
local title = t_trim( item.title or "" )
local link = t_trim( item.link or "" )
local authors = t_trim( item.authors or "" )
do
local k = smwPending and smwPropRef( PF, "authors" ) or ""
if k ~= "" and authors ~= "" then
smwQueueAdd( smwPending, k, authors, true )
end
end
if link ~= "" and title ~= "" then
out[ #out + 1 ] = "[" .. link .. " " .. title .. "]"
elseif title ~= "" then
out[ #out + 1 ] = title
end
if authors ~= "" then
out[ #out + 1 ] = ", " .. authors
end
renderReferenceItemBanners(item.warnings, pv, lang, cats, out)
return "<li>" .. table_concat( out ) .. "</li>"
end
local function wkRenderReferencesFromRaw( rawB, rawW, rawV, pv, lang, cats, PF, smwPending, args )
local b, w, v = {}, {}, {}
for _, it in ipairs( wkParseBiblioMarkers( rawB ) ) do
b[ #b + 1 ] = wkRenderOneBiblioLi( it, pv, lang, cats, PF, smwPending, args )
end
for _, it in ipairs( wkParseWeblioMarkers( rawW ) ) do
w[ #w + 1 ] = wkRenderOneWeblioLi( it, pv, lang, cats, PF, smwPending, args )
end
for _, it in ipairs( wkParseVideoMarkers( rawV ) ) do
v[ #v + 1 ] = wkRenderOneVideoLi( it, pv, lang, cats, PF, smwPending, args )
end
if #b == 0 and #w == 0 and #v == 0 then
return ""
end
local out = table_concat( {
table_concat( b, "\n" ),
table_concat( w, "\n" ),
table_concat( v, "\n" )
}, "\n" )
out = t_trim( out )
return out
end
----------------------------------------------------------------------
-- htmltag : générateur (mode qui marche)
----------------------------------------------------------------------
local function tagHtmlTag(tagName, content, attrs)
local src = "{{#tag:htmltag|" .. (content or "") .. "|tagname=" .. tostring(tagName)
if attrs then
local keys = {}
local n = 0
for k in pairs(attrs) do
n = n + 1
keys[n] = k
end
if n > 0 then
table_sort(keys)
for i = 1, n do
local k = keys[i]
src = src .. "|" .. tostring(k) .. "=" .. tostring(attrs[k])
end
end
end
src = src .. "}}"
return F:preprocess(src)
end
local function neutralizeAutoLinksInJSON(json)
json = tostring(json or "")
json = json:gsub("https://", "https<nowiki/>://")
json = json:gsub("http://", "http<nowiki/>://")
return json
end
----------------------------------------------------------------------
-- SMW : cache fin (par rendu) — bornés (Patch #6)
----------------------------------------------------------------------
local SMW_ASK_CACHE = cacheMake(250)
local SMW_TITLES_CACHE = cacheMake(250)
-- Patch #2 : clé cache non-collidable (length-prefix)
local function smwKeyFromArray(q)
if type(q) ~= "table" then
local s = tostring(q or "")
return tostring(#s) .. ":" .. s
end
local idx = {}
local n = 0
for k in pairs(q) do
if type(k) == "number" then
n = n + 1
idx[n] = k
end
end
if n == 0 then
return ""
end
table_sort(idx)
local parts = {}
for i = 1, n do
local v = tostring(q[idx[i]] or "")
parts[i] = tostring(#v) .. ":" .. v
end
return table_concat(parts, "\n")
end
local function smwSetSafe(props)
if not mw.smw or type(mw.smw.set) ~= "function" then
return
end
pcall(mw.smw.set, props)
end
-- Cache négatif (sentinelle false)
local function smwAskSafe(q)
if not hasSMW() or type(mw.smw.ask) ~= "function" then return nil end
local k = smwKeyFromArray(q)
local hit = cacheGet(SMW_ASK_CACHE, k)
if hit ~= nil then
return (hit ~= false) and hit or nil
end
local ok, res = pcall(function() return mw.smw.ask(q) end)
if ok then
cachePut(SMW_ASK_CACHE, k, res)
return res
end
cachePut(SMW_ASK_CACHE, k, false)
return nil
end
----------------------------------------------------------------------
-- Bloc d’en-tête / variables de page (cache key-safe)
----------------------------------------------------------------------
local function computePageVars()
local title = mw.title.getCurrentTitle()
local rawTitle = title.prefixedText
local encoded = mw_uri_encode(rawTitle, "WIKI")
local pageUrl = ""
do
local ok, u = pcall(mw_uri_fullUrl, title.prefixedText)
pageUrl = (ok and u) and tostring(u) or ""
end
local pageId = tostring(title.id or "")
return {
title = title,
rawTitle = rawTitle,
encoded = encoded,
pageUrl = pageUrl,
pageId = pageId
}
end
local function getPageCreation(pv, args)
-- 1) Priorité : paramètre PageForms / modèle
local v = args and args["creation-date"]
v = (type(v) == "string") and t_trim(v) or ""
if v ~= "" then
return v
end
local title = pv and pv.title
local page = title and title.prefixedText or ""
if page == "" then
return nil
end
-- 2) Sinon : SMW ?Date de création
if hasSMW() and type(mw.smw.ask) == "function" then
local res = smwAskSafe({
"[[" .. page .. "]]",
"?Creation date#-F[Y-m-d\\TH:i:s\\Z]=cd",
"limit=1",
"link=none"
})
if type(res) == "table" and res[1] then
local cd = res[1].cd
return cd
end
end
end
----------------------------------------------------------------------
-- Cache SMW (parents) + cartes pré-rendues
----------------------------------------------------------------------
local function makeSMWCache()
return {
debatsPour = nil,
debatsContre = nil,
justifs = nil,
objs = nil,
debatsTousFinal = nil,
debatCardsTous = nil,
debatCardsPour = nil,
debatCardsContre = nil,
argCardsJustifs = nil,
argCardsObjs = nil
}
end
----------------------------------------------------------------------
-- Breadcrumb JSON-LD (i18n) — Accueil → Arguments → Page
----------------------------------------------------------------------
local function renderBreadcrumbJSONLD(pv, lang)
local homeUrl = L(lang, "urls", "home")
local homeLabel = L(lang, "text", "breadcrumb_home_label")
local argsUrl = L(lang, "urls", "breadcrumb_arguments")
local argsLabel = L(lang, "text", "breadcrumb_arguments_label")
local data = {
[ "@context" ] = "https://schema.org",
[ "@type" ] = "BreadcrumbList",
itemListElement = {
{
[ "@type" ] = "ListItem",
position = 1,
item = { [ "@id" ] = homeUrl, name = homeLabel }
},
{
[ "@type" ] = "ListItem",
position = 2,
item = { [ "@id" ] = argsUrl, name = argsLabel }
},
{
[ "@type" ] = "ListItem",
position = 3,
item = { [ "@id" ] = pv.pageUrl, name = pv.rawTitle }
}
}
}
local json = safeJsonEncode(data)
json = neutralizeAutoLinksInJSON(json)
return tagHtmlTag("script", json, {
type = "application/ld+json",
["class"] = "navigation-not-searchable"
})
end
----------------------------------------------------------------------
-- Titre : avertissements + catégories (legacy)
----------------------------------------------------------------------
local function renderTitleWarnings(pv, cats, lang)
local title = pv.rawTitle or ""
if title == "" then return "" end
local out = {}
local n = 0
for w in t_gsplit(title, "%s+") do
w = t_trim(w)
if w ~= "" then
n = n + 1
if n >= 3 then break end
end
end
if n > 0 and n < 3 then
table_insert(out,
'<div class="warningbox cdx-message cdx-message--block cdx-message--warning navigation-not-searchable">\'\'\''
.. L(lang, "text", "title_too_short_msg")
.. ' [[Special:MovePage/' .. pv.encoded .. '|' .. L(lang, "text", "rename_page_label") .. ']]'
.. ' ' .. L(lang, "text", "if_needed_msg")
.. '\'\'\'</div>'
)
table_insert(cats, "[[Category:" .. L(lang, "categories", "title_very_short") .. "]]")
end
if title:sub(-1) == "." and title:sub(-4) ~= "etc." then
table_insert(out,
'<div class="warningbox cdx-message cdx-message--block cdx-message--warning navigation-not-searchable">\'\'\''
.. L(lang, "text", "title_ends_with_dot_msg")
.. ' [[Special:MovePage/' .. pv.encoded .. '|' .. L(lang, "text", "rename_page_label") .. ']]'
.. '\'\'\'</div>'
)
table_insert(cats, "[[Category:" .. L(lang, "categories", "title_ends_with_dot") .. "]]")
end
if lang == "fr" then
local prefix3 = title:sub(1, 3)
local prefix4 = title:sub(1, 4)
local prefix5 = title:sub(1, 5)
local prefix6 = title:sub(1, 6)
local prefix10 = title:sub(1, 10)
local prefix13 = title:sub(1, 13)
if prefix3 == "Ça" then
table_insert(cats, "[[Category:Titres d'arguments commençant par \"Cela\"]]")
elseif prefix3 == "Sa" then
table_insert(cats, "[[Category:Titres d'arguments commençant par \"Sa\"]]")
end
local map4 = {
["Ces"] = "Titres d'arguments commençant par \"Ces\"",
["Cet"] = "Titres d'arguments commençant par \"Cet\"",
["Ils"] = "Titres d'arguments commençant par \"Ils\"",
["Ses"] = "Titres d'arguments commençant par \"Ses\"",
["Son"] = "Titres d'arguments commençant par \"Son\""
}
if map4[prefix4] then
table_insert(cats, "[[Category:" .. map4[prefix4] .. "]]")
end
local map5 = {
["Cela"] = "Titres d'arguments commençant par \"Cela\"",
["Elle"] = "Titres d'arguments commençant par \"Elle\"",
["Leur"] = "Titres d'arguments commençant par \"Leur\"",
["Leurs"] = "Titres d'arguments commençant par \"Leurs\"",
["Non,"] = "Titres d'arguments commençant par \"Non\"",
["Nous"] = "Titres d'arguments commençant par \"Nous\"",
["Oui,"] = "Titres d'arguments commençant par \"Oui\""
}
if map5[prefix5] then
table_insert(cats, "[[Category:" .. map5[prefix5] .. "]]")
end
local map6 = {
["Cette"] = "Titres d'arguments commençant par \"Cette\"",
["Elles"] = "Titres d'arguments commençant par \"Elles\""
}
if map6[prefix6] then
table_insert(cats, "[[Category:" .. map6[prefix6] .. "]]")
end
local map10 = {
["C'est faux"] = "Titres d'arguments commençant par \"C'est faux\"",
["Hors-sujet"] = "Titres d'arguments commençant par \"Hors-sujet\""
}
if map10[prefix10] then
table_insert(cats, "[[Category:" .. map10[prefix10] .. "]]")
end
if prefix13 == "Au contraire," then
table_insert(cats, "[[Category:Titres d'arguments commençant par \"Au contraire\"]]")
end
end
return table_concat(out)
end
----------------------------------------------------------------------
-- SMW : helpers (avec cache fin)
----------------------------------------------------------------------
local function uniqList(list)
local out, seen = {}, {}
for _, x in ipairs(list or {}) do
x = t_trim(tostring(x or ""))
if x ~= "" and not seen[x] then
seen[x] = true
table_insert(out, x)
end
end
return out
end
local function smwAskTitles(conditions, limit)
limit = tonumber(limit) or 200
local q = {}
for _, c in ipairs(conditions or {}) do
table_insert(q, c)
end
table_insert(q, "limit=" .. tostring(limit))
table_insert(q, "link=none")
local ck = "TITLES\n" .. smwKeyFromArray(q)
local hit = cacheGet(SMW_TITLES_CACHE, ck)
if hit ~= nil then
return hit
end
local res = smwAskSafe(q)
if type(res) ~= "table" then
cachePut(SMW_TITLES_CACHE, ck, {})
return {}
end
local out, seen = {}, {}
for _, row in ipairs(res) do
local ft = row.fulltext or row.page or row[1]
if type(ft) == "string" and ft ~= "" and not seen[ft] then
seen[ft] = true
table_insert(out, ft)
end
end
cachePut(SMW_TITLES_CACHE, ck, out)
return out
end
----------------------------------------------------------------------
-- (Parents) : ask unifié + robuste + fallback
----------------------------------------------------------------------
local function normTitle(s)
s = tostring(s or "")
s = s:gsub("_", " ")
return t_trim(s)
end
canonicalTitle = function(s)
s = t_trim(tostring(s or ""))
if s == "" then return "" end
s = s:gsub("^:%s*", "")
s = s:gsub("^%[%[%s*", ""):gsub("%s*%]%]$", "")
s = (s:match("^([^%]|]+)%|") or s)
s = t_trim(s)
local t = titleNewCached(s)
return t and t.prefixedText or s
end
-- Patch #5 : sécuriser les titres utilisés dans les requêtes SMW
local function smwSafeTitle(s)
s = canonicalTitle(s)
if s == "" then
return ""
end
s = s:gsub("#.*$", "")
s = t_trim(s)
if s == "" then
return ""
end
if s:find("[%[%]{}|<>]") then
return ""
end
return s
end
local function containsTitle(val, target)
target = normTitle(target)
if target == "" or val == nil then
return false
end
local function checkOne(x)
if x == nil then return false end
if type(x) == "string" then
return normTitle(x) == target
end
if type(x) == "table" then
local ft = x.fulltext or x.page or x.name or x.title
if type(ft) == "string" and normTitle(ft) == target then
return true
end
for _, y in ipairs(x) do
if checkOne(y) then
return true
end
end
end
return false
end
return checkOne(val)
end
local function ensureDebateParents(pv, cache, PF)
if not cache or cache.debatsPour ~= nil then
return
end
local argTitle = smwSafeTitle(pv.rawTitle)
local propPro = tostring(PF.prop_arg_pro or "")
local propCon = tostring(PF.prop_arg_con or "")
if propPro == "" or propCon == "" or argTitle == "" then
cache.debatsPour = {}
cache.debatsContre = {}
return
end
local q = {
"([[" .. propPro .. "::" .. argTitle .. "]] OR [[" .. propCon .. "::" .. argTitle .. "]] )",
"?" .. propPro .. "=pro",
"?" .. propCon .. "=con",
"limit=200",
"link=none"
}
local res = smwAskSafe(q)
local debatsPour, debatsContre = {}, {}
local seenP, seenC = {}, {}
if type(res) == "table" then
for _, row in ipairs(res) do
local ft = row.fulltext or row.page or row[1]
if type(ft) == "string" and ft ~= "" then
if containsTitle(row.pro, argTitle) and not seenP[ft] then
seenP[ft] = true
table_insert(debatsPour, ft)
end
if containsTitle(row.con, argTitle) and not seenC[ft] then
seenC[ft] = true
table_insert(debatsContre, ft)
end
end
end
end
if #debatsPour == 0 and #debatsContre == 0 then
debatsPour = uniqList(smwAskTitles({ "[[" .. propPro .. "::" .. argTitle .. "]]" }, 200))
debatsContre = uniqList(smwAskTitles({ "[[" .. propCon .. "::" .. argTitle .. "]]" }, 200))
end
cache.debatsPour = debatsPour
cache.debatsContre = debatsContre
end
local function ensureArgumentParents(pv, cache, PF)
if not cache or cache.justifs ~= nil then
return
end
local argTitle = smwSafeTitle(pv.rawTitle)
local propJustif = tostring(PF.prop_justif or "")
local propObj = tostring(PF.prop_obj or "")
if propJustif == "" or propObj == "" or argTitle == "" then
cache.justifs = {}
cache.objs = {}
return
end
local q = {
"([[" .. propJustif .. "::" .. argTitle .. "]] OR [[" .. propObj .. "::" .. argTitle .. "]] )",
"?" .. propJustif .. "=justif",
"?" .. propObj .. "=obj",
"limit=200",
"link=none"
}
local res = smwAskSafe(q)
local justifs, objs = {}, {}
local seenJ, seenO = {}, {}
if type(res) == "table" then
for _, row in ipairs(res) do
local ft = row.fulltext or row.page or row[1]
if type(ft) == "string" and ft ~= "" then
if containsTitle(row.justif, argTitle) and not seenJ[ft] then
seenJ[ft] = true
table_insert(justifs, ft)
end
if containsTitle(row.obj, argTitle) and not seenO[ft] then
seenO[ft] = true
table_insert(objs, ft)
end
end
end
end
if #justifs == 0 and #objs == 0 then
justifs = uniqList(smwAskTitles({ "[[" .. propJustif .. "::" .. argTitle .. "]]" }, 200))
objs = uniqList(smwAskTitles({ "[[" .. propObj .. "::" .. argTitle .. "]]" }, 200))
end
cache.justifs = justifs
cache.objs = objs
end
----------------------------------------------------------------------
-- Fil d’Ariane : longueur (byte-safe, sans Unicode libs)
----------------------------------------------------------------------
local function breadcrumbLenByteSafe(ch)
if type(ch) ~= "string" or ch == "" then
return 0
end
local sep = "⟭"
local seplen = 3
local n = 0
local i = 1
local Ls = #ch
local function trimAscii(s)
return (tostring(s or ""):match("^%s*(.-)%s*$") or "")
end
while i <= Ls + 1 do
local j = ch:find(sep, i, true)
local part
if j then
part = ch:sub(i, j - 1)
i = j + seplen
else
part = ch:sub(i)
i = Ls + 2
end
if trimAscii(part) ~= "" then
n = n + 1
if n >= 6 then
return n
end
end
end
return n
end
----------------------------------------------------------------------
-- Débats parents : calcul final
----------------------------------------------------------------------
local function ensureDebateParentsFinal(pv, cache, PF)
if not cache or cache.debatsTousFinal ~= nil then
return
end
local final = {}
ensureDebateParents(pv, cache, PF)
for _, d in ipairs(cache.debatsPour or {}) do
table_insert(final, canonicalTitle(d))
end
for _, d in ipairs(cache.debatsContre or {}) do
table_insert(final, canonicalTitle(d))
end
if hasSMW() then
ensureArgumentParents(pv, cache, PF)
local propBreadcrumb = tostring(PF.prop_breadcrumb or "")
local propParentDebate = tostring(PF.prop_parent_debate or "")
if propBreadcrumb ~= "" and propParentDebate ~= "" then
local parents = {}
for _, a in ipairs(cache.justifs or {}) do table_insert(parents, canonicalTitle(a)) end
for _, a in ipairs(cache.objs or {}) do table_insert(parents, canonicalTitle(a)) end
parents = uniqList(parents)
if #parents > 0 then
local conds = {}
for _, t in ipairs(parents) do
local safe = smwSafeTitle(t)
if safe ~= "" then
table_insert(conds, "[[" .. safe .. "]]")
end
end
if #conds > 0 then
local q = {
"(" .. table_concat(conds, " OR ") .. ")",
"?" .. propBreadcrumb .. "=chain",
"?" .. propParentDebate .. "=pdeb",
"limit=200",
"link=none"
}
local res = smwAskSafe(q)
if type(res) == "table" then
for _, row in ipairs(res) do
local chain = row.chain
local okLen = false
if type(chain) == "string" then
local n = breadcrumbLenByteSafe(chain)
okLen = (n > 0 and n < 6)
elseif type(chain) == "table" then
for _, ch in ipairs(chain) do
if type(ch) == "string" then
local n = breadcrumbLenByteSafe(ch)
if n > 0 and n < 6 then
okLen = true
break
end
end
end
end
if okLen then
local pdeb = row.pdeb
if type(pdeb) == "string" and t_trim(pdeb) ~= "" then
table_insert(final, canonicalTitle(pdeb))
elseif type(pdeb) == "table" then
for _, x in ipairs(pdeb) do
if type(x) == "string" and t_trim(x) ~= "" then
table_insert(final, canonicalTitle(x))
elseif type(x) == "table" then
local ft = x.fulltext or x.page or x[1]
if type(ft) == "string" and t_trim(ft) ~= "" then
table_insert(final, canonicalTitle(ft))
end
end
end
end
end
end
end
end
end
end
end
cache.debatsTousFinal = uniqList(final)
end
----------------------------------------------------------------------
-- Parents : cartes (normalisation + rendu 100% Lua) + cache (borné)
----------------------------------------------------------------------
local function normalizeTitleForCard(s)
s = t_trim(tostring(s or ""))
if s == "" then
return ""
end
if s:byte(1) ~= 58 and not s:find("[%[<&]") then
return s
end
s = s:gsub("^:%s*", "")
do
local target = s:match("^%[%[([^%]|]+)%|.-%]%]$")
or s:match("^%[%[([^%]]+)%]%]$")
if target and target ~= "" then
s = target
end
end
s = t_trim(s):gsub("^:%s*", "")
if s:find("&", 1, true) then
s = s
:gsub(" ", " ")
:gsub(" ", " ")
:gsub("&", "&")
:gsub(""", '"')
:gsub("'", "'")
:gsub("<", "<")
:gsub(">", ">")
end
return t_trim(s)
end
local URLENC_CACHE = cacheMake(2000)
local function mwUrlEncodePF(s)
s = tostring(s or "")
local hit = cacheGet(URLENC_CACHE, s)
if hit ~= nil then return hit end
local out = mw_uri_encode(s, "QUERY")
out = out:gsub("%%20", "+")
cachePut(URLENC_CACHE, s, out)
return out
end
local function argumentMapCardLua(targetTitle, currentArgTitle, typeValue)
local t = normalizeTitleForCard(targetTitle)
local a = normalizeTitleForCard(currentArgTitle)
local p2 = mwUrlEncodePF(a)
local dataAttr = ''
if typeValue and typeValue ~= '' then
dataAttr = ' data-' .. typeValue .. '="' .. escapeAttr( p2 ) .. '"'
end
return
'<span class="hover-map"'
.. dataAttr
.. '>'
.. '[[' .. t .. ']]'
.. '</span>'
end
local CARD_CACHE = cacheMake(600)
local function cardRendered(kind, targetTitle, currentArgTitle, typeValue)
local key =
kind .. "\n"
.. tostring(targetTitle or "") .. "\n"
.. tostring(currentArgTitle or "") .. "\n"
.. tostring(typeValue or "")
local cached = cacheGet(CARD_CACHE, key)
if cached ~= nil then
return cached
end
local rendered = argumentMapCardLua(targetTitle, currentArgTitle, typeValue)
cachePut(CARD_CACHE, key, rendered)
return rendered
end
local function cardDebateRendered(debateTitle, currentArgTitle, typeValue)
return cardRendered("debate", debateTitle, currentArgTitle, typeValue)
end
local function cardArgumentRendered(argTitle, currentArgTitle, typeValue)
return cardRendered("argument", argTitle, currentArgTitle, typeValue)
end
local function ensureDebateCards(pv, cache, lang, PF)
if not cache or cache.debatCardsTous ~= nil then
return
end
ensureDebateParentsFinal(pv, cache, PF)
local argTitle = pv.rawTitle
local function debateKey(d)
return canonicalTitle(d)
end
local linked = {}
for _, d in ipairs(cache.debatsPour or {}) do
local k = debateKey(d)
if k ~= "" then
linked[k] = true
end
end
for _, d in ipairs(cache.debatsContre or {}) do
local k = debateKey(d)
if k ~= "" then
linked[k] = true
end
end
local cardsTous = {}
for _, d in ipairs(cache.debatsTousFinal or {}) do
local k = debateKey(d)
local typeDebate = (k ~= "" and linked[k]) and "debate" or ""
table_insert(cardsTous, cardDebateRendered(d, argTitle, typeDebate))
end
cache.debatCardsTous = cardsTous
local cardsPour = {}
for _, d in ipairs(cache.debatsPour or {}) do
table_insert(cardsPour, cardDebateRendered(d, argTitle, "debate"))
end
cache.debatCardsPour = cardsPour
local cardsContre = {}
for _, d in ipairs(cache.debatsContre or {}) do
table_insert(cardsContre, cardDebateRendered(d, argTitle, "debate"))
end
cache.debatCardsContre = cardsContre
end
local function ensureArgumentCards(pv, cache, PF)
if not cache or cache.argCardsJustifs ~= nil then
return
end
ensureArgumentParents(pv, cache, PF)
local argTitle = pv.rawTitle
local typeArgument = "argument"
local cj = {}
for _, a in ipairs(cache.justifs or {}) do
table_insert(cj, cardArgumentRendered(a, argTitle, typeArgument))
end
cache.argCardsJustifs = cj
local co = {}
for _, a in ipairs(cache.objs or {}) do
table_insert(co, cardArgumentRendered(a, argTitle, typeArgument))
end
cache.argCardsObjs = co
end
----------------------------------------------------------------------
-- Parents : bandeaux rendus en Lua
----------------------------------------------------------------------
local function fileTag(file, size, alt, noInvert)
local cls = noInvert and " | class=mw-no-invert" or ""
alt = tostring(alt or "")
alt = alt:gsub("|", "|"):gsub("%[", "["):gsub("%]", "]")
return string.format("[[File: %s | %spx | link= | alt=%s%s]]", file, tostring(size or 16), alt, cls)
end
local function ensureDot(s)
s = tostring(s or "")
s = t_trim(s)
if s == "" then return s end
if s:sub(-1) == "." then return s end
return s .. "."
end
local function renderParentBanner(iconWikitext, texte)
return
'<div class="bandeau-en-tete searchaux" style="font-style: italic">'
.. '<span style="margin-right: 0.5em">'
.. (iconWikitext or "")
.. '</span>'
.. ensureDot(texte)
.. '</div>'
end
----------------------------------------------------------------------
-- Bandeaux : rendu natif + résolution via label i18n (aligné Debate)
----------------------------------------------------------------------
local function bannerMsg( lang, key, ... )
return WD_I18N.msg( "Argument.banners", lang, key, ... ) or ""
end
local function renderMetaWarningHtml( params )
local color = tostring( params.color or "" )
local icon = tostring( params.icon or "" )
local size = tostring( params.size or "40px" )
local alt = tostring( params.alt or "" )
local title = tostring( params.title or "" )
local text = tostring( params.text or "" )
return
'<div class="bandeau bandeau-' .. color .. ' navigation-not-searchable">'
.. '<table style="background-color:transparent">'
.. '<tr>'
.. '<td class="bandeau-icone">'
.. '<div style="width:60px; text-align:center">[[File: ' .. icon .. '|' .. size .. '|alt=' .. escapeAttr( alt ) .. '|link=]]</div>'
.. '</td>'
.. '<td style="width: 100%;">'
.. '<div class="bandeau-titre"><strong>' .. title .. '</strong></div><!--'
.. '--><div class="bandeau-texte">' .. text .. '</div>'
.. '</td>'
.. '</tr>'
.. '</table><!--'
.. '--></div>'
end
local function renderBannerByKey( pv, lang, key, cats )
key = tostring( key or "" )
if key == "" then
return ""
end
local color = bannerMsg( lang, key .. "_color" )
local icon = bannerMsg( lang, key .. "_icon" )
local size = bannerMsg( lang, key .. "_size" )
local alt = bannerMsg( lang, key .. "_alt" )
local title = bannerMsg( lang, key .. "_title", pv.rawTitle )
local text = bannerMsg( lang, key .. "_text", pv.rawTitle )
if color == "" and icon == "" and title == "" and text == "" then
return ""
end
local html = renderMetaWarningHtml{
color = color,
icon = icon,
size = size ~= "" and size or "40px",
alt = alt,
title = title,
text = text
}
local cat = bannerMsg( lang, key .. "_category" )
if cats and cat and cat ~= "" then
table_insert( cats, "[[Category:" .. cat .. "]]" )
end
return html
end
local function buildBannerLabelLookup( lang, keys )
local lookup = {}
for _, key in ipairs( keys or {} ) do
local lab = t_trim( bannerMsg( lang, key .. "_label" ) )
if lab ~= "" then
lookup[ lab ] = key
end
end
return lookup
end
local function resolveBannerKeyFromLookup( raw, lookup )
raw = t_trim( tostring( raw or "" ) )
if raw == "" then
return ""
end
return ( lookup and lookup[ raw ] ) or ""
end
local function renderBannersFromArgList( rawList, lookup, pv, lang, cats )
local html = {}
for _, x in ipairs( splitCSV( rawList, "," ) ) do
local key = resolveBannerKeyFromLookup( x, lookup )
if key ~= "" then
table_insert( html, renderBannerByKey( pv, lang, key, cats ) )
end
end
return table_concat( html )
end
local TITLE_WARNING_BANNER_KEYS = {
"title_incomplete",
"title_disadvantageous",
"title_unclear",
"title_too_long",
}
local ARGUMENT_WARNING_BANNER_KEYS = {
"argument_sensitive",
"argument_fanciful",
"argument_potentially_illegal",
}
----------------------------------------------------------------------
-- Survol des cartes d'arguments
----------------------------------------------------------------------
local function joinCards(cards, lang)
local n = (cards and #cards) or 0
if n == 0 then
return ""
end
if n == 1 then
return cards[1] or ""
end
local conj = L(lang, "text", "list_conjunction_text")
conj = tostring(conj or " et ")
conj = conj:gsub("%s+", " ")
if n == 2 then
return (cards[1] or "") .. conj .. (cards[2] or "")
end
local head = table_concat(cards, ", ", 1, n - 1)
return head .. conj .. (cards[n] or "")
end
local function renderDebateParentsBoxes(pv, cats, lang, cache, PF)
if not hasSMW() then return "" end
ensureDebateParentsFinal(pv, cache, PF)
ensureDebateCards(pv, cache, lang, PF)
local out = {}
local iconDebat = fileTag(PF.file_parent_debate, 16, PF.parent_banner_debate_alt, false)
local iconPro = fileTag(PF.file_arg_pro, 16, PF.parent_banner_pro_alt, true)
local iconCon = fileTag(PF.file_arg_con, 16, PF.parent_banner_con_alt, true)
local nTous = #(cache.debatsTousFinal or {})
if nTous > 0 then
local msgKey = (nTous == 1) and "parent_used_in_debate_1" or "parent_used_in_debate_n"
local texte = string.format(L(lang, "text", msgKey), joinCards(cache.debatCardsTous, lang))
table_insert(out, renderParentBanner(iconDebat, texte))
end
if cache.debatsPour and #cache.debatsPour > 0 then
local msgKey = (#cache.debatsPour == 1) and "parent_is_pro_in_1" or "parent_is_pro_in_n"
local texte = string.format(L(lang, "text", msgKey), joinCards(cache.debatCardsPour, lang))
table_insert(out, renderParentBanner(iconPro, texte))
end
if cache.debatsContre and #cache.debatsContre > 0 then
local msgKey = (#cache.debatsContre == 1) and "parent_is_con_in_1" or "parent_is_con_in_n"
local texte = string.format(L(lang, "text", msgKey), joinCards(cache.debatCardsContre, lang))
table_insert(out, renderParentBanner(iconCon, texte))
end
return table_concat(out)
end
local function renderArgumentParentsBoxes(pv, cats, lang, cache, PF)
if not hasSMW() then return "" end
ensureArgumentParents(pv, cache, PF)
ensureArgumentCards(pv, cache, PF)
local out = {}
local iconJustif = fileTag(PF.file_arg_pro, 16, PF.parent_banner_pro_alt, true)
local iconObj = fileTag(PF.file_arg_con, 16, PF.parent_banner_con_alt, true)
if cache.justifs and #cache.justifs > 0 then
local texte = string.format(PF.parent_is_justification_of, joinCards(cache.argCardsJustifs, lang))
table_insert(out, renderParentBanner(iconJustif, texte))
end
if cache.objs and #cache.objs > 0 then
local texte = string.format(PF.parent_is_objection_to, joinCards(cache.argCardsObjs, lang))
table_insert(out, renderParentBanner(iconObj, texte))
end
return table_concat(out)
end
local function renderDebatesParentsBottom(pv, lang, cache, PF)
if not hasSMW() then return "" end
ensureDebateParentsFinal(pv, cache, PF)
ensureDebateCards(pv, cache, lang, PF)
local n = #(cache.debatsTousFinal or {})
if n == 0 then
return ""
end
local title = (n == 1) and PF.parent_debates_title_1 or PF.parent_debates_title_n
local icon = '[[File:' .. PF.file_parent_debate .. ' | 20px | link= | class=ajustement | alt=' .. title .. ']]'
local lis = {}
for _, card in ipairs(cache.debatCardsTous or {}) do
table_insert(lis,
'<li style="position: relative;">'
.. card
.. '</li>'
)
end
return
'<h2 id="Parent_debates"><span style="margin-right: 0.45em;">' .. icon .. '</span>' .. title .. '</h2>'
.. '<ul class="hover-top navigation-not-searchable">'
.. table_concat(lis)
.. '</ul>'
end
----------------------------------------------------------------------
-- INITIALIZATION : bloc (si page “vide” / non initialisée)
----------------------------------------------------------------------
local computeArgumentMapsFromData
local function renderInitializationBlock(args, pv, cats, lang, cache, PF)
-- Détection “contenu local”
local hasResume = (args["summary"] ~= nil and t_trim(args["summary"] or "") ~= "")
local hasQuotes = (args["quotes"] ~= nil and t_trim(args["quotes"] or "") ~= "")
local hasRefs =
(args["bibliography"] ~= nil and t_trim(args["bibliography"] or "") ~= "")
or (args["webliography"] ~= nil and t_trim(args["webliography"] or "") ~= "")
or (args["videography"] ~= nil and t_trim(args["videography"] or "") ~= "")
-- Détection “enfants” via données sérialisées (⟬⟭)
local hasChildren = false
do
local maps = computeArgumentMapsFromData(args, PF)
local jn = ((maps.justif and maps.justif.items) and #maps.justif.items) or 0
local on = ((maps.obj and maps.obj.items) and #maps.obj.items) or 0
if jn > 0 or on > 0 then
hasChildren = true
end
end
-- Détection “parents” (SMW) : si la page est déjà reliée à quelque chose, on la considère “initialisée”
local hasParents = false
if hasSMW() then
ensureDebateParentsFinal(pv, cache, PF)
ensureArgumentParents(pv, cache, PF)
if (cache.debatsTousFinal and #cache.debatsTousFinal > 0)
or (cache.justifs and #cache.justifs > 0)
or (cache.objs and #cache.objs > 0)
then
hasParents = true
end
end
-- Statut
local isInitialized = (hasResume or hasQuotes or hasRefs or hasChildren or hasParents)
if isInitialized then
if PF.cat_initialized and PF.cat_initialized ~= "" then
table_insert(cats, "[[Category:" .. PF.cat_initialized .. "]]")
end
return ""
end
-- Non initialisé
if PF.cat_uninitialized and PF.cat_uninitialized ~= "" then
table_insert(cats, "[[Category:" .. PF.cat_uninitialized .. "]]")
end
-- Bandeau “orphan” éventuel (si tu as un template dédié)
local orphan = ""
do
local tpl = tostring(PF.tpl_orphan_banner or "")
if tpl ~= "" then
orphan = expandCached(F, tpl, { page = "argument" }) or ""
end
end
-- Liens : “terminer” = envoyer vers une édition utile (résumé par défaut)
local finishLink = ""
do
if PF.form_edit_summary and PF.form_edit_summary ~= "" then
finishLink = addDataLink(
PF.form_edit_summary,
pv.rawTitle,
PF.init_yes_finish_label or PF.edit,
PF.init_yes_tt or ""
)
end
end
-- Lien renommer (MovePage)
local renameLink = ""
do
if PF.init_no_rename_label and PF.init_no_rename_label ~= "" then
renameLink =
'[[Special:MovePage/'
.. pv.encoded
.. '| '
.. PF.init_no_rename_label
.. ']]'
end
end
-- Bloc warning (ne dépend d’aucun JS)
local out = {}
if orphan ~= "" then
table_insert(out, orphan)
end
table_insert(out,
'<div class="warningbox cdx-message cdx-message--block cdx-message--warning navigation-not-searchable">'
.. '<div style="font-weight: bold; margin-bottom: 0.35em;">'
.. (PF.init_warning_title or "")
.. '</div>'
.. '<div style="margin-bottom: 0.5em;">'
.. (PF.init_warning_intro or "")
.. '</div>'
)
-- Actions
local actions = {}
if finishLink ~= "" then
table_insert(actions,
'<span class="wk-btn wk-btn--primary noprint" style="margin-right: 0.5em;">'
.. finishLink
.. '</span>'
)
end
if renameLink ~= "" then
table_insert(actions,
'<span class="wk-btn wk-btn--secondary noprint">'
.. renameLink
.. '</span>'
)
end
if #actions > 0 then
table_insert(out,
'<div class="navigation-not-searchable noprint" style="margin-top: 0.25em;">'
.. table_concat(actions, "")
.. '</div>'
)
end
-- Résumés “suggestion” (texte simple, pour guider sans imposer)
do
local hints = {}
if PF.init_summary_pro and PF.init_summary_pro ~= "" then
table_insert(hints, '<li>' .. PF.init_summary_pro .. '</li>')
end
if PF.init_summary_con and PF.init_summary_con ~= "" then
table_insert(hints, '<li>' .. PF.init_summary_con .. '</li>')
end
if PF.init_summary_justif and PF.init_summary_justif ~= "" then
table_insert(hints, '<li>' .. PF.init_summary_justif .. '</li>')
end
if PF.init_summary_obj and PF.init_summary_obj ~= "" then
table_insert(hints, '<li>' .. PF.init_summary_obj .. '</li>')
end
if #hints > 0 then
table_insert(out,
'<ul style="margin: 0.5em 0 0 1.25em;">'
.. table_concat(hints, "")
.. '</ul>'
)
end
end
table_insert(out, '</div>')
return table_concat(out, "")
end
----------------------------------------------------------------------
-- Fil d’Ariane : 1 seule requête SMW
----------------------------------------------------------------------
local function renderAndSetBreadcrumb(pv, cats, lang, PF)
if not hasSMW() or type(mw.smw.ask) ~= "function" then
return nil
end
lang = lang or "fr"
local title = smwSafeTitle(pv.rawTitle)
if not title or title == "" then
return nil
end
local propBreadcrumb = tostring(PF.prop_breadcrumb or "")
local propDebateName = tostring(PF.prop_debate_name or "")
local propPro = tostring(PF.prop_arg_pro or "")
local propCon = tostring(PF.prop_arg_con or "")
local propJustif = tostring(PF.prop_justif or "")
local propObj = tostring(PF.prop_obj or "")
if propBreadcrumb == "" or propPro == "" or propCon == "" or propJustif == "" or propObj == "" then
return nil
end
local q = {
"([[" .. propJustif .. "::" .. title .. "]] OR [[" .. propObj .. "::" .. title .. "]] OR [[" .. propPro .. "::" .. title .. "]] OR [[" .. propCon .. "::" .. title .. "]] )",
"?" .. propBreadcrumb .. "=chain",
(propDebateName ~= "" and ("?" .. propDebateName .. "=debateName") or nil),
"limit=200",
"link=none"
}
local qq = {}
for _, x in ipairs(q) do
if x then
table_insert(qq, x)
end
end
local res = smwAskSafe(qq)
if type(res) ~= "table" or #res == 0 then
return nil
end
local bestChain, bestLen = nil, nil
for _, row in ipairs(res) do
local function consider(ch)
if type(ch) ~= "string" or ch == "" then
return
end
local n = breadcrumbLenByteSafe(ch)
if n > 0 and (not bestLen or n < bestLen) then
bestLen = n
bestChain = ch
end
end
local chain = row.chain
if type(chain) == "string" then
consider(chain)
elseif type(chain) == "table" then
for _, ch in ipairs(chain) do
consider(ch)
if bestLen == 1 then break end
end
end
if bestLen == 1 then
break
end
end
if not bestChain or not bestLen then
return nil
end
if bestLen >= 6 then
table_insert(cats, "[[Category:" .. L(lang, "categories", "breadcrumb_too_long") .. "]]")
return nil
end
local newChain = bestChain .. "⟭" .. title
if #newChain > 2000 then
local parts = splitByLiteral(newChain, "⟭")
local acc = ""
for i = #parts, 1, -1 do
local seg = t_trim(parts[i] or "")
if seg ~= "" then
local candidate = seg
if acc ~= "" then
candidate = seg .. "⟭" .. acc
end
if #candidate > 2000 then
acc = seg:sub(1, 2000)
break
end
acc = candidate
end
end
newChain = acc
end
return newChain
end
local function wkBuildArgumentData(items, PF, kind)
items = items or {}
------------------------------------------------------------------
-- Liste texte (affichage)
------------------------------------------------------------------
local listLines = {}
for _, it in ipairs(items) do
table_insert(listLines, "* " .. tostring(it.title or ""))
end
local listText = table_concat(listLines, "\n")
local argsVarParts = {}
for _, it in ipairs(items) do
local page = t_trim(tostring(it.page or ""))
if page ~= "" then
local title = t_trim(tostring(it.title or ""))
if title == "" or title == page then
table_insert(argsVarParts, page)
else
table_insert(
argsVarParts,
page
.. FIELD_SEP
.. title
)
end
end
end
local argsVar = table_concat(argsVarParts, ITEM_SEP)
------------------------------------------------------------------
-- Carte hover (HTML)
------------------------------------------------------------------
local hoverLines = {}
for _, it in ipairs(items) do
table_insert(hoverLines,
'<div class="argument-title--map wk-icon argument-icon">' .. '[[' .. tostring(it.page or "") .. '|' .. tostring(it.title or "") .. ']]' .. '</div>'
)
end
local mapHoverHtml = table_concat(hoverLines, "\n")
return {
items = items,
list = listText,
argsVar = argsVar,
mapHover = mapHoverHtml
}
end
computeArgumentMapsFromData = function(args, PF)
local rawJ = args["justifications"] or ""
local rawO = args["objections"] or ""
local justifItems = wkExtractItemsFromSerializedData( rawJ )
local objItems = wkExtractItemsFromSerializedData( rawO )
return {
justif = wkBuildArgumentData( justifItems, PF, "justif" ),
obj = wkBuildArgumentData( objItems, PF, "obj" )
}
end
local function wkRenderArgumentLi(pv, PF, it, kind, idx)
local isJustif = (kind == "justif")
local page = tostring(it.page or "")
local titleShown = tostring(it.title or "")
if titleShown == "" then titleShown = page end
local aId = anchorId(titleShown)
local warnings = tostring(it.warnings or "")
return
'<li class="argument">'
.. '<div id="'
.. escapeAttr(aId)
.. '" class="argument-title wk-icon argument-icon wk-carret"'
.. (warnings ~= "" and (' data-warnings="' .. escapeAttr(warnings) .. '"') or "") .. '>'
.. '[[' .. page .. '|' .. titleShown .. ']]'
.. '</div>'
.. '</li>'
end
----------------------------------------------------------------------
-- Mots-clés : lien direct RunQuery (GET)
----------------------------------------------------------------------
local function renderKeywords(args, pv, cats, lang, PF)
local out = {}
table_insert(out, '<div style="font-size: 95%; margin-top: 1em;">' .. PF.keywords_label)
local raw = args["keywords"]
if raw and raw ~= "" then
local extra = nil
if PF.search_type_field ~= "" and PF.search_type_value ~= "" then
extra = { [PF.search_type_field] = PF.search_type_value }
end
local rq_title = wkRunQueryPath(PF.form_search_by_keywords)
local rq_qkey = PF.form_search_by_keywords .. "[" .. PF.keywords_field .. "]"
local rendered = {}
for _, k in ipairs(splitCSV(raw, ",")) do
k = t_trim(tostring(k or ""))
if k ~= "" then
local chip = runQueryLink{
form = PF.form_search_by_keywords,
field = PF.keywords_field,
value = k,
label = k,
tooltip = PF.search_by_keyword_tt,
extra = extra,
_title = rq_title,
_qkey = rq_qkey
}
if chip and chip ~= "" then
table_insert(rendered, chip)
else
table_insert(rendered, k)
end
end
end
if #rendered > 0 then
table_insert(out, table_concat(rendered, ", "))
else
table_insert(out, PF.none)
table_insert(cats, "[[Category:" .. PF.cat_no_keywords .. "]]")
end
else
table_insert(out, PF.none)
table_insert(cats, "[[Category:" .. PF.cat_no_keywords .. "]]")
end
table_insert(out,
'<span class="modifier-rubrique navigation-not-searchable noprint">'
.. addDataLink(
PF.form_edit_keywords,
pv.rawTitle,
" ",
PF.edit_keywords_tooltip
)
.. '</span><span style="display: none;">.</span></div>'
)
return table_concat(out)
end
local function renderH2( iconFile, alt, title, editLink, id )
local idAttr = id and ( ' id="' .. id .. '"' ) or ''
return
'<h2 class="section-modifiable"' .. idAttr .. '>'
.. '<span style="margin-right: 0.5em;">[[File: ' .. iconFile .. ' | 17px | link= | alt=' .. alt .. ']]</span>'
.. title
.. '<span class="modifier-section navigation-not-searchable noprint">'
.. ( editLink or '' )
.. '</span></h2>'
end
local function renderSummary(args, pv, cats, lang, PF)
local edit = addDataLink(
PF.form_edit_summary,
pv.rawTitle,
" ",
PF.edit_summary_tooltip
)
local out = {}
table_insert(out, renderH2(PF.file_summary, PF.summary, PF.summary, edit, 'Summary'))
do
local raw = args[ "summary-warnings" ]
raw = ( type( raw ) == "string" ) and t_trim( raw ) or ""
if raw ~= "" then
local lookup = buildSummaryLabelLookup( lang, SUMMARY_WARNING_BANNER_KEYS )
local aliases = SUMMARY_WARNING_ALIASES
if not aliases or aliases._lang ~= lang then
aliases = buildSummaryAliases(lang)
aliases._lang = lang
SUMMARY_WARNING_ALIASES = aliases
end
for _, x in ipairs( splitCSV( raw, "," ) ) do
local key = resolveKeyFromLookup(
x,
lookup,
SUMMARY_WARNING_BANNER_KEYS,
aliases
)
if key ~= "" then
table_insert( out, renderSubsectionBanner( pv, lang, key, nil, cats ) )
end
end
end
end
if args["summary"] and args["summary"] ~= "" then
table_insert(out, "<div>\n" .. args["summary"] .. "</div>")
else
table_insert(out, '<div class="aucun-contenu navigation-not-searchable">' .. PF.no_summary .. '</div>')
table_insert(cats, "[[Category:" .. PF.cat_no_summary .. "]]")
end
return table_concat(out)
end
local function renderQuotes(args, pv, cats, lang, PF)
local edit = addDataLink(
PF.form_edit_quotes,
pv.rawTitle,
" ",
PF.edit_quotes_tooltip
)
local out = {}
table_insert(out, renderH2(PF.file_quote, PF.quotes, PF.quotes, edit))
local q = args["quotes"]
if q and q ~= "" then
table_insert(out, q)
else
table_insert(out, '<div class="aucun-contenu navigation-not-searchable">' .. PF.no_quotes .. '</div>')
table_insert(cats, "[[Category:" .. PF.cat_no_quotes .. "]]")
end
return table_concat(out)
end
local function renderDebateDetailed(args, pv, cats, lang, PF)
if not args["detailed-debate"] or args["detailed-debate"] == "" then
return ""
end
table_insert(cats, "[[Category:" .. PF.cat_has_detailed_debate .. "]]")
local edit = addDataLink(
PF.form_edit_detailed_debate,
pv.rawTitle,
" ",
string.format(PF.edit_detailed_debate_tooltip, pv.rawTitle)
)
local h2 =
'<h2 class="section-modifiable">'
.. '<span style="margin-right: 0.5em;">[[File: ' .. PF.file_browse .. ' | 17px | middle | link= | class=ajustement | alt=' .. PF.detailed_debate .. ']]</span>'
.. PF.detailed_debate
.. '<span class="modifier-section navigation-not-searchable noprint">' .. edit .. '</span></h2>'
local debat = canonicalTitle(args["detailed-debate"])
local mapHtml = ""
if hasSMW() and type(mw.smw.ask) == "function" and PF.prop_argument_map and PF.prop_argument_map ~= "" then
local debatSafe = smwSafeTitle(debat)
if debatSafe ~= "" then
local res = smwAskSafe({
"[[" .. debatSafe .. "]]",
"?" .. PF.prop_argument_map .. "=map",
"limit=1",
"link=none"
})
if type(res) == "table" and res[1] and res[1].map then
local v = res[1].map
if type(v) == "string" then
mapHtml = v
elseif type(v) == "table" then
for _, x in ipairs(v) do
if type(x) == "string" and t_trim(x) ~= "" then
mapHtml = x
break
elseif type(x) == "table" then
local ft = x.fulltext or x.page or x[1]
if type(ft) == "string" and t_trim(ft) ~= "" then
mapHtml = ft
break
end
end
end
end
end
end
end
local box =
'<div style="border-style: solid; border-width: 0 0 0 6px; margin: 1em 0; padding: 2px 10px; border-color: #02a68f;">'
.. '<table style="background-color:transparent"><tr><td style="font-weight: bold;">'
.. string.format(PF.arg_is_debate, t_nowiki(pv.rawTitle))
.. '</td></tr>'
.. '<tr><td><div class="bandeau-section navigation-not-searchable onglet-externe" style="margin: 0.25em 0 0.25em 0; padding-right: 0.25em; position: inherit;">'
.. '<span style="margin: 0 0.5em 0 0.25em">[[File: ' .. PF.file_search .. ' | 13px | link= | class=mw-no-invert]]</span>'
.. PF.detailed_debate_label .. '<span class="onglet-externe">[[ ' .. debat .. ' ]]</span>'
.. '</div></td></tr></table></div>'
local mapBox = ""
if mapHtml ~= "" then
mapBox =
'<div style="color: var(--color-base,#202122); background-color: var(--background-color-neutral-subtle, #f8f9fa); font-size: 95%; width: 100%; padding: 5px;" class="onglet-externe">'
.. '<div style="font-weight: bold; font-size: 140%; line-height: 1.25; margin-bottom: 0.25em;">'
.. debat
.. '</div>'
.. mapHtml
.. '</div>'
end
return h2 .. box .. mapBox
end
----------------------------------------------------------------------
-- Sections
----------------------------------------------------------------------
local function safeRubriqueValue(s)
s = t_trim(tostring(s or ""))
if s == "" then
return ""
end
if s:find("[%[%]{}|#<>]") then
return ""
end
s = s:gsub("%s+", " ")
s = t_trim(s)
return s
end
local function renderRubriques(args, cats, lang, PF)
if args["sections"] and args["sections"] ~= "" then
local out = {}
local nOk = 0
for _, r in ipairs(splitCSV(args["sections"], ",")) do
r = safeRubriqueValue(r)
if r ~= "" then
nOk = nOk + 1
table_insert(out, "[[Category:" .. r .. "]]")
table_insert(out, "[[Rubrique::" .. r .. "| ]]")
end
end
if nOk > 0 then
return table_concat(out)
end
return "[[Category:" .. PF.cat_no_sections .. "]]"
else
return "[[Category:" .. PF.cat_no_sections .. "]]"
end
end
----------------------------------------------------------------------
-- Quotes : extraction optimisée — Patch #4
----------------------------------------------------------------------
local function readAttrDataWkCite( openTag )
if not openTag or openTag == "" then
return nil
end
local p = openTag:find( "data%-wk%-cite", 1 )
if not p then
return nil
end
-- Avance jusqu’à '='
local eq = openTag:find( "=", p, true )
if not eq then
return nil
end
-- Saute espaces après '='
local i = eq + 1
while true do
local c = openTag:sub( i, i )
if c == " " or c == "\t" or c == "\n" or c == "\r" then
i = i + 1
else
break
end
end
local q = openTag:sub( i, i )
if q ~= '"' and q ~= "'" then
return nil
end
local j = openTag:find( q, i + 1, true )
if not j then
return nil
end
local v = openTag:sub( i + 1, j - 1 )
if v == "" then
return nil
end
local num = tonumber( v )
return num
end
local function extractSimpleDiv(html, startPos)
local openEnd = html:find(">", startPos, true)
if not openEnd then return "" end
local closeEnd = html:find("</div>", openEnd + 1, true)
if not closeEnd then return "" end
return html:sub(startPos, closeEnd + 5)
end
local function extractTagSegmentFast( html, startPos, tagName )
local openEnd = html:find( ">", startPos, true )
if not openEnd then
return ""
end
local closeTag = "</" .. tagName .. ">"
local closePos = html:find( closeTag, openEnd + 1, true )
if not closePos then
return ""
end
return html:sub( startPos, closePos + #closeTag - 1 )
end
local function extractBlockquoteFast( html, startPos )
return extractTagSegmentFast( html, startPos, "blockquote" )
end
local function extractQuotesFast( html, wantMax )
wantMax = wantMax or 4
html = tostring( html or "" )
if html == "" then
return {}, 0
end
local bqByN = {}
local rfByN = {}
local maxN = 0
local foundBq = 0
local foundRf = 0
do
local i = 1
while true do
local s = html:find( "<blockquote", i, true )
if not s then
break
end
local openEnd = html:find( ">", s, true )
if not openEnd then
break
end
local openTag = html:sub( s, openEnd )
local n = readAttrDataWkCite( openTag )
if n and n > maxN then
maxN = n
end
if n and n >= 1 and n <= wantMax and bqByN[ n ] == nil then
local seg = extractBlockquoteFast( html, s )
if seg ~= "" then
bqByN[ n ] = seg
foundBq = foundBq + 1
i = s + #seg
else
i = openEnd + 1
end
else
-- Pas utile (n>wantMax) : avance juste après le tag ouvrant
i = openEnd + 1
end
if foundBq >= wantMax and foundRf >= wantMax then
break
end
end
end
do
local i = 1
while true do
local s = html:find( "<div", i, true )
if not s then
break
end
local openEnd = html:find( ">", s, true )
if not openEnd then
break
end
local openTag = html:sub( s, openEnd )
if openTag:find( "reference-citation", 1, true ) then
local n = readAttrDataWkCite( openTag )
if n and n > maxN then
maxN = n
end
if n and n >= 1 and n <= wantMax and rfByN[ n ] == nil then
local seg = extractSimpleDiv( html, s )
if seg ~= "" then
rfByN[ n ] = seg
foundRf = foundRf + 1
i = s + #seg
else
i = openEnd + 1
end
else
-- Pas utile : avance juste après le tag ouvrant
i = openEnd + 1
end
else
i = openEnd + 1
end
if foundBq >= wantMax and foundRf >= wantMax then
break
end
end
end
local pairsOut = {}
local upto = maxN
if upto > wantMax then
upto = wantMax
end
for n = 1, upto do
local bq = bqByN[ n ] or ""
local rf = rfByN[ n ] or ""
if bq ~= "" or rf ~= "" then
pairsOut[ n ] = bq .. rf
end
end
return pairsOut, maxN
end
----------------------------------------------------------------------
-- CITATIONS BIS
----------------------------------------------------------------------
local function wkParseQuoteMarkers(raw)
raw = tostring(raw or "")
raw = t_trim(raw)
if raw == "" then
return {}
end
-- Heuristique: si aucun séparateur de champ, pas notre format
if not raw:find(QUOTE_FIELD_SEP, 1, true) then
return {}
end
local items = {}
local blocks
-- Si séparateur d’items présent : split
if raw:find(QUOTE_ITEM_SEP, 1, true) then
blocks = splitByLiteral(raw, QUOTE_ITEM_SEP)
else
blocks = { raw }
end
for _, block in ipairs(blocks) do
block = t_trim(block)
if block ~= "" then
local f = {}
local parts = splitByLiteral(block, QUOTE_FIELD_SEP)
for _, part in ipairs(parts) do
f[#f + 1] = t_trim(tostring(part or ""))
end
for i = #f + 1, 13 do
f[i] = ""
end
items[#items + 1] = {
quote = f[1],
authors = f[2],
article = f[3],
work = f[4],
volume = f[5],
number = f[6],
page = f[7],
location = f[8],
publisher = f[9],
place = f[10],
date = f[11],
link = f[12],
warnings = f[13]
}
end
end
return items
end
local function wkRenderOneQuoteHtml(item, idx, pv, lang, cats, PF)
local quote = tostring(item.quote or "")
local authors = t_trim(item.authors or "")
local article = t_trim(item.article or "")
local work = t_trim(item.work or "")
local volume = t_trim(item.volume or "")
local number = t_trim(item.number or "")
local page = t_trim(item.page or "")
local location = t_trim(item.location or "")
local publisher = t_trim(item.publisher or "")
local place = t_trim(item.place or "")
local date = t_trim(item.date or "")
local link = t_trim(item.link or "")
local warnings = t_trim(item.warnings or "")
-- Accumulateurs VariablesLua (comme Module:Quote)
vset("WD_QUOTES_MAX", tostring(idx))
if authors ~= "" then
local cur = t_trim(tostring(vget("WD_QUOTES_AUTHORS") or ""))
vset("WD_QUOTES_AUTHORS", (cur == "" and authors) or (cur .. "," .. authors))
end
if article ~= "" then
local cur = t_trim(tostring(vget("WD_QUOTES_ARTICLES") or ""))
vset("WD_QUOTES_ARTICLES", (cur == "" and article) or (cur .. "," .. article))
end
if work ~= "" then
local cur = t_trim(tostring(vget("WD_QUOTES_WORKS") or ""))
vset("WD_QUOTES_WORKS", (cur == "" and work) or (cur .. "," .. work))
end
if publisher ~= "" then
local cur = t_trim(tostring(vget("WD_QUOTES_PUBLISHERS") or ""))
vset("WD_QUOTES_PUBLISHERS", (cur == "" and publisher) or (cur .. "," .. publisher))
end
if place ~= "" then
local cur = t_trim(tostring(vget("WD_QUOTES_PLACES") or ""))
vset("WD_QUOTES_PLACES", (cur == "" and place) or (cur .. "," .. place))
end
-- Blockquote
local warnHtml = ""
if warnings ~= "" then
local lookup = buildSummaryLabelLookup( lang, QUOTE_WARNING_BANNER_KEYS )
local b = {}
for x in t_gsplit( warnings, ",", true ) do
x = t_trim( x )
if x ~= "" then
local key = resolveKeyFromLookup( x, lookup, QUOTE_WARNING_BANNER_KEYS )
if key ~= "" then
local h = renderSubsectionBanner(
pv,
lang,
key,
nil,
cats
)
if h ~= "" then
b[ #b + 1 ] = h
end
end
end
end
if #b > 0 then
warnHtml = "<div>" .. table_concat( b, "" ) .. "</div>"
end
end
local bq =
'<blockquote data-wk-cite="' .. tostring(idx) .. '">'
.. warnHtml
.. '« ' .. quote .. ' »'
.. '</blockquote>'
-- Référence (sans fallback demandé => on n’invente pas d’auteur)
local ref = {}
table_insert(ref, '<div class="reference-citation" data-wk-cite="' .. tostring(idx) .. '">')
if authors ~= "" then
table_insert( ref, authors )
else
table_insert( ref, PF.unknown_author )
table_insert( cats, "[[Category:" .. PF.cat_no_authors .. "]]" )
end
if article ~= "" then
if link ~= "" then
table_insert(ref, ', « [' .. link .. ' ' .. article .. '] »')
else
table_insert(ref, ', « ' .. article .. ' »')
end
end
if work ~= "" then
if article ~= "" then
table_insert(ref, ", ''" .. work .. "''")
else
if link ~= "" then
table_insert(ref, ", ''[" .. link .. " " .. work .. "]''")
else
table_insert(ref, ", ''" .. work .. "''")
end
end
end
if work ~= "" and number ~= "" then
if volume ~= "" then
table_insert(ref, ", vol. " .. volume)
end
table_insert(ref, ", n° " .. number)
end
if page ~= "" then
table_insert(ref, ", p." .. page)
end
if location ~= "" then
table_insert(ref, ", " .. location)
end
if publisher ~= "" then
table_insert(ref, ", " .. publisher)
end
if place ~= "" then
table_insert(ref, ", " .. place)
end
if date ~= "" then
table_insert(ref, ", " .. date)
end
table_insert(ref, ".</div>")
local html = bq .. "\n" .. table_concat(ref) .. "\n"
if idx >= 1 and idx <= 4 then
vset("WD_QUOTE_" .. tostring(idx), html)
end
return html
end
local function wkRenderQuotesFromSerialized(raw, pv, lang, cats, PF)
local items = wkParseQuoteMarkers(raw)
if #items == 0 then
return ""
end
local out = {}
for i, it in ipairs( items ) do
out[ #out + 1 ] = wkRenderOneQuoteHtml( it, i, pv, lang, cats, PF )
end
return table_concat(out, "")
end
----------------------------------------------------------------------
-- SMW : setSemanticData + argument_map/justif_list/obj_list
----------------------------------------------------------------------
local function setSemanticData(args, pv, lang, cache, PF, breadcrumbChain, maps, rawB, rawW, rawV)
local props = {}
props[PF.prop_arg_name] = pv.rawTitle
props[PF.prop_arg_number] = pv.pageId
if hasSMW() then
ensureDebateParentsFinal(pv, cache, PF)
ensureArgumentParents(pv, cache, PF)
if breadcrumbChain and breadcrumbChain ~= "" and PF.prop_breadcrumb and PF.prop_breadcrumb ~= "" then
props[PF.prop_breadcrumb] = breadcrumbChain
end
if PF.prop_parent_debate and PF.prop_parent_debate ~= "" then
local dp = cache.debatsTousFinal or {}
if #dp > 0 then
props[PF.prop_parent_debate] = dp
end
end
do
local parents = {}
for _, x in ipairs(cache.justifs or {}) do
table_insert(parents, canonicalTitle(x))
end
for _, x in ipairs(cache.objs or {}) do
table_insert(parents, canonicalTitle(x))
end
parents = uniqList(parents)
if #parents > 0 and PF.prop_parent_argument and PF.prop_parent_argument ~= "" then
props[PF.prop_parent_argument] = parents
end
end
end
maps = maps or {}
local justif = maps.justif or {}
local obj = maps.obj or {}
do
local lst = {}
for _, it in ipairs((justif.items or {})) do
local t = canonicalTitle(it.page or "")
if t ~= "" then
table_insert(lst, t)
end
end
lst = uniqList(lst)
if #lst > 0 and PF.prop_justif and PF.prop_justif ~= "" then
props[PF.prop_justif] = lst
end
end
do
local lst = {}
for _, it in ipairs((obj.items or {})) do
local t = canonicalTitle(it.page or "")
if t ~= "" then
table_insert(lst, t)
end
end
lst = uniqList(lst)
if #lst > 0 and PF.prop_obj and PF.prop_obj ~= "" then
props[PF.prop_obj] = lst
end
end
local justifSurvol = t_trim(justif.mapHover or "")
local objSurvol = t_trim(obj.mapHover or "")
if justifSurvol == "" then justifSurvol = '<div class="argument-title--map carte-vide">' .. PF.no_justifications_short .. '</div>' end
if objSurvol == "" then objSurvol = '<div class="argument-title--map carte-vide">' .. PF.no_objections_short .. '</div>' end
if PF.prop_argument_map and PF.prop_argument_map ~= "" then
props[PF.prop_argument_map] =
'<table style="background-color:transparent; width: 100%; margin: 0em 0.5em 0.15em 0;" class="navigation-not-searchable">'
.. '<tr><th style="text-align:left;">' .. PF.justifications .. '</th>'
.. '<th style="text-align:left; padding-left: 1em;">' .. PF.objections .. '</th></tr>'
.. '<tr class="is-pro" style="vertical-align:top;"><td>' .. justifSurvol .. '</td>'
.. '<td class="is-con" style="padding-left: 1em;">' .. objSurvol .. '</td></tr></table>'
end
do
local lst = t_trim(justif.argsVar or "")
if lst ~= "" and PF.prop_justif_list and PF.prop_justif_list ~= "" then
props[PF.prop_justif_list] = lst
end
end
do
local lst = t_trim(obj.argsVar or "")
if lst ~= "" and PF.prop_obj_list and PF.prop_obj_list ~= "" then
props[PF.prop_obj_list] = lst
end
end
------------------------------------------------------------------
-- Contenu d'argument (legacy) — basé sur args (inchangé)
------------------------------------------------------------------
local contenuArgumentParts = {}
local pairsFast, nbQuotes = extractQuotesFast(args["quotes"] or "", 4)
local q1 = tostring(pairsFast[1] or "")
local q2 = tostring(pairsFast[2] or "")
local q3 = tostring(pairsFast[3] or "")
local q4 = tostring(pairsFast[4] or "")
local quote1 = q1
local summary = tostring(args["summary"] or "")
summary = t_trim(summary)
if summary ~= "" or quote1 ~= "" then
table_insert(contenuArgumentParts, ( summary ~= "" and ( "<div>\n" .. summary .. "</div>" ) or "" ) .. quote1)
else
local hasRefsLocal =
(args["bibliography"] and args["bibliography"] ~= "")
or (args["webliography"] and args["webliography"] ~= "")
or (args["videography"] and args["videography"] ~= "")
if not hasRefsLocal then
table_insert(
contenuArgumentParts,
'<div class="argument-vide">\'\'Aucun contenu n\'a été entré.\'\'</div>'
)
end
end
do
if nbQuotes > 1 then
local titrePage = pv.rawTitle
local titrePageEncode = pv.encoded
local extraParts2 = {}
if q2 ~= "" then table_insert(extraParts2, q2) end
if q3 ~= "" then table_insert(extraParts2, q3) end
if q4 ~= "" then table_insert(extraParts2, q4) end
local extra2 = table_concat(extraParts2, "")
if nbQuotes > 4 then
extra2 = extra2
.. '<div style="font-style: italic; margin-top: 1em;" class="onglet-externe">'
.. string.format(PF.more_quotes_note, t_nowiki(titrePage))
.. '</div>'
end
if extra2 ~= "" and PF.prop_additional_content and PF.prop_additional_content ~= "" then
props[PF.prop_additional_content] = extra2
end
table_insert(
contenuArgumentParts,
'<div class="more-content">'
.. '<div class="more-content-button" data-page="' .. escapeAttr(titrePageEncode) .. '">'
.. PF.more_quotes_button
.. '</div>'
.. '<div class="more-content-wrapper">'
.. '<div class="more-content-drop hide"></div>'
.. '</div>'
.. '</div>'
)
end
end
do
local refsHtml = wkRenderReferencesFromRaw( rawB, rawW, rawV, pv, lang, nil, PF, props, args )
if refsHtml ~= "" then
table_insert(
contenuArgumentParts,
'<div style="margin-top: 1em;">\'\'\'' .. PF.references .. '\'\'\'</div>'
.. '<ul class="references-argument">' .. refsHtml .. '</ul>'
)
end
end
do
local debat = args["detailed-debate"]
if debat and debat ~= "" then
local debatSafe = canonicalTitle(debat)
table_insert(contenuArgumentParts,
'<div style="border-style: solid; border-width: 0 0 0 5px; margin: 1em 0; padding: 0 1em; border-color: #02a68f;">'
.. '<table style="background-color:transparent">'
.. '<tr><td style="font-weight: bold; font-size: 95%;">'
.. string.format(PF.arg_is_debate, t_nowiki(pv.rawTitle))
.. '</td></tr>'
.. '<tr><td>'
.. '<div class="bandeau-section navigation-not-searchable" style="margin: 0.25em 0 0.25em 0; padding-right: 0.25em; position: inherit;">'
.. '<span style="margin: 0 0.5em 0 0.25em">[[File: ' .. PF.file_search .. ' | 13px | link= | class=mw-no-invert]]</span>'
.. PF.detailed_debate_label .. '<span class="onglet-externe">[[ ' .. debatSafe .. ' ]]</span>'
.. '</div>'
.. '</td></tr></table></div>'
)
end
end
local contenuArgument = table_concat(contenuArgumentParts, "")
if contenuArgument ~= "" and PF.prop_arg_content and PF.prop_arg_content ~= "" then
props[PF.prop_arg_content] = contenuArgument
end
if args["justification-warnings"] and args["justification-warnings"] ~= "" then
props[PF.prop_warn_justif] = args["justification-warnings"]
end
if args["objection-warnings"] and args["objection-warnings"] ~= "" then
props[PF.prop_warn_obj] = args["objection-warnings"]
end
local mc = args["keywords"]
if mc and mc ~= "" then
local list = splitCSV(mc, ",")
if #list > 0 then
props[PF.prop_keyword] = list
end
end
if args["detailed-debate"] and args["detailed-debate"] ~= "" then
props[PF.prop_detailed_debate] = canonicalTitle(args["detailed-debate"])
end
do
local function uniqCSVVar( varName )
local raw = tostring( vget( varName ) or "" )
raw = t_trim( raw )
if raw == "" then
return nil
end
local lst = splitCSV( raw, "," )
lst = uniqList( lst )
return ( #lst > 0 ) and lst or nil
end
local authors = uniqCSVVar( "WD_QUOTES_AUTHORS" )
if authors then
local k = smwPropRef( PF, "authors" )
if k ~= "" then
smwQueueAdd( props, k, authors, true )
end
end
local articles = uniqCSVVar( "WD_QUOTES_ARTICLES" )
if articles then
local k = smwPropRef( PF, "article_name" )
if k ~= "" then
smwQueueAdd( props, k, articles, true )
end
end
local works = uniqCSVVar( "WD_QUOTES_WORKS" )
if works then
local k = smwPropRef( PF, "work_name" )
if k ~= "" then
smwQueueAdd( props, k, works, true )
end
end
local pubs = uniqCSVVar( "WD_QUOTES_PUBLISHERS" )
if pubs then
local k = smwPropRef( PF, "publishing_house" )
if k ~= "" then
smwQueueAdd( props, k, pubs, true )
end
end
local places = uniqCSVVar( "WD_QUOTES_PLACES" )
if places then
local k = smwPropRef( PF, "place_of_publication" )
if k ~= "" then
smwQueueAdd( props, k, places, true )
end
end
end
smwSetSafe(props)
end
----------------------------------------------------------------------
-- SEO via Wikiseo (mw.ext.seo.set)
----------------------------------------------------------------------
local SEO_CACHE = cacheMake(60)
local function wkSeoHas()
return mw.ext
and mw.ext.seo
and type( mw.ext.seo.set ) == "function"
end
local function wkSeoSet( data )
if not wkSeoHas() then
return
end
pcall( mw.ext.seo.set, data )
end
local function seoL( lang, key, ... )
return WD_I18N.msg( "Argument.seo", lang, key, ... ) or ""
end
local function seoJoinList( items )
local out = {}
local seen = {}
for _, v in ipairs( items or {} ) do
v = t_trim( tostring( v or "" ) )
if v ~= "" and not seen[ v ] then
seen[ v ] = true
out[ #out + 1 ] = v
end
end
return table_concat( out, ", " )
end
local function buildSeoKeywords( args, lang )
local items = {}
for _, k in ipairs( splitCSV( args[ "keywords" ], "," ) ) do
items[ #items + 1 ] = k
end
for _, s in ipairs( splitCSV( args[ "sections" ], "," ) ) do
items[ #items + 1 ] = s
end
return seoJoinList( items )
end
local function seoKeyPart(s)
s = tostring(s or "")
if mw_hash then
local ok, h = pcall(mw_hash.hashValue, "md5", s)
if ok and type(h) == "string" and h ~= "" then
return h
end
end
return s
end
local function renderFullSEO( args, pv, lang )
local titleText = pv.rawTitle or ""
local ck =
"SEO\n" .. tostring( lang or "" ) .. "\n" .. tostring( pv.pageId or "" ) .. "\n"
.. titleText .. "\n"
.. seoKeyPart( args[ "keywords" ] or "" ) .. "\n"
.. seoKeyPart( args[ "sections" ] or "" )
local hit = cacheGet( SEO_CACHE, ck )
if hit ~= nil then
return ""
end
cachePut( SEO_CACHE, ck, true )
local topic = t_trim( tostring( args[ "keywords" ] or "" ) )
if topic == "" then
topic = titleText
end
local kw = buildSeoKeywords( args, lang )
local published = getPageCreation(pv, args)
wkSeoSet{
title = string_format( seoL( lang, "title" ), titleText ),
title_mode = string_format( seoL( lang, "title_mode" ), titleText ),
title_separator = string_format( seoL( lang, "title_separator" ), titleText ),
description = string_format( seoL( lang, "description" ), topic ),
keywords = kw,
author = seoL( lang, "author" ),
image = seoL( lang, "image" ),
image_alt = seoL( lang, "image_alt" ),
type = "article",
section = seoL( lang, "section" ),
site_name = seoL( lang, "site_name" ),
twitter_site = seoL( lang, "twitter_site" ),
locale = seoL( lang, "locale" ),
published_time = published,
robots = "index,follow",
googlebot = "index,follow",
}
return ""
end
----------------------------------------------------------------------
-- Bandeaux “maison” (title-warnings / argument-warnings)
----------------------------------------------------------------------
local function wkWarnTrim(s)
s = t_trim(tostring(s or ""))
if s == "" then
return ""
end
return s
end
-- Parse un token “warning” simple.
-- Format toléré (tu pourras l’aligner sur Debate) :
-- "code"
-- "code:texte"
-- "type:code:texte"
-- Où type ∈ warning|info|success|error (fallback: warning)
local function wkParseWarningToken(tok)
tok = wkWarnTrim(tok)
if tok == "" then
return nil
end
local parts = splitByLiteral(tok, ":", 3)
local a = wkWarnTrim(parts[1])
local b = wkWarnTrim(parts[2])
local c = wkWarnTrim(parts[3])
local wtype = "warning"
local code = ""
local text = ""
if c ~= "" then
-- type:code:texte
wtype = (a ~= "" and a) or "warning"
code = (b ~= "" and b) or ""
text = c
elseif b ~= "" then
-- code:texte
code = a
text = b
else
-- code
code = a
end
if wtype ~= "warning" and wtype ~= "info" and wtype ~= "success" and wtype ~= "error" then
-- Si le 1er segment n’est pas un type connu, on le traite comme “code”
if code == "" then
code = wtype
else
code = wtype .. ":" .. code
end
wtype = "warning"
end
return {
type = wtype,
code = code,
text = text
}
end
-- ⚠️ À aligner sur Debate : classes exactes, structure exacte, icônes exactes.
-- Ici : fallback propre “CDX-like” + tes classes navigation-not-searchable.
local function wkRenderWarningBox(PF, warn, isMini)
if not warn then
return ""
end
local wtype = warn.type or "warning"
local code = wkWarnTrim(warn.code)
local text = wkWarnTrim(warn.text)
-- Si aucun texte explicite, on tente une résolution i18n par code
-- (à adapter si Debate utilise une table de mapping spécifique)
if text == "" and code ~= "" then
-- Ex: Argument.warnings.<code> dans tes messages i18n
-- Si tu veux un autre domaine, on changera.
text = WD_I18N.msg("Argument.warnings", PF._lang or "fr", code)
text = wkWarnTrim(text)
end
if text == "" then
-- ultime fallback : afficher le code brut
text = code
end
local baseClass = "cdx-message cdx-message--block navigation-not-searchable"
local typeClass = "cdx-message--warning"
if wtype == "info" then typeClass = "cdx-message--notice" end
if wtype == "success" then typeClass = "cdx-message--success" end
if wtype == "error" then typeClass = "cdx-message--error" end
local extra = isMini and " wk-warning--mini" or " wk-warning--full"
return
'<div class="' .. baseClass .. ' ' .. typeClass .. extra .. '">'
.. '<div style="font-weight: bold;">'
.. escapeAttr(text)
.. '</div>'
.. '</div>'
end
local function wkRenderWarningsFromArg(PF, raw, isMini)
raw = wkWarnTrim(raw)
if raw == "" then
return ""
end
local boxes = {}
for _, tok in ipairs(splitCSV(raw, ",")) do
local w = wkParseWarningToken(tok)
local html = wkRenderWarningBox(PF, w, isMini)
if html ~= "" then
boxes[#boxes + 1] = html
end
end
if #boxes == 0 then
return ""
end
return '<div>' .. table_concat(boxes, "") .. '</div>'
end
----------------------------------------------------------------------
-- Rendu principal (i18n)
----------------------------------------------------------------------
function p.render(frame)
vset( "WD_QUOTES_AUTHORS", "" )
vset( "WD_QUOTES_ARTICLES", "" )
vset( "WD_QUOTES_WORKS", "" )
vset( "WD_QUOTES_PUBLISHERS", "" )
vset( "WD_QUOTES_PLACES", "" )
vset( "WD_QUOTES_MAX", "" )
vset( "WD_QUOTE_1", "" )
vset( "WD_QUOTE_2", "" )
vset( "WD_QUOTE_3", "" )
vset( "WD_QUOTE_4", "" )
EXPAND_CACHE = cacheMake(250)
CARD_CACHE = cacheMake(600)
SMW_ASK_CACHE = cacheMake(250)
SMW_TITLES_CACHE = cacheMake(250)
I18N_DOMAIN = {}
I18N_MSG = {}
TITLE_CACHE = cacheMake(600)
SEO_CACHE = cacheMake(60)
local args = getArgs(frame)
local function norm(k)
local v = args[k]
if type(v) ~= "string" then
return
end
v = t_trim(v)
if v == "" then
args[k] = nil
else
args[k] = v
end
end
norm("summary")
norm("quotes")
norm("bibliography")
norm("webliography")
norm("videography")
norm("keywords")
norm("sections")
norm("interlanguage")
norm("detailed-debate")
norm("title-warnings")
norm("argument-warnings")
norm("summary-warnings")
norm("justification-warnings")
norm("objection-warnings")
local rawB = args["bibliography"] or ""
local rawW = args["webliography"] or ""
local rawV = args["videography"] or ""
if type(args["justifications"]) ~= "string" then args["justifications"] = nil end
if type(args["objections"]) ~= "string" then args["objections"] = nil end
local lang = detectLang(args)
local pv = computePageVars()
pv.lang = lang
local cache = makeSMWCache()
local pieces, cats = {}, {}
local PF = {}
PF.file_parent_debate = L(lang, "files", "parent_debate")
PF.file_arg_pro = L(lang, "files", "arg_pro")
PF.file_arg_con = L(lang, "files", "arg_con")
PF.file_summary = L(lang, "files", "summary")
PF.file_quote = L(lang, "files", "quote")
PF.file_browse = L(lang, "files", "browse")
PF.file_search = L(lang, "files", "search")
PF.file_biblio = L(lang, "files", "biblio")
PF.parent_banner_debate_alt = L(lang, "text", "parent_banner_debate_alt")
PF.parent_banner_pro_alt = L(lang, "text", "parent_banner_pro_alt")
PF.parent_banner_con_alt = L(lang, "text", "parent_banner_con_alt")
PF.parent_is_justification_of = L(lang, "text", "parent_is_justification_of")
PF.parent_is_objection_to = L(lang, "text", "parent_is_objection_to")
PF.keywords_label = L(lang, "text", "keywords_label_text")
PF.none = L(lang, "text", "none_label")
PF.keywords_field = L(lang, "params", "keywords_field")
PF.form_search_by_keywords = L(lang, "forms", "search_by_keywords_title")
PF.search_by_keyword_tt = L(lang, "text", "search_by_keyword_tt")
PF.search_type_field = tostring(L(lang, "params", "search_type_field") or "")
PF.search_type_value = tostring(L(lang, "params", "search_type_value") or "")
PF.form_edit_keywords = L(lang, "forms", "keywords_edit_title")
PF.edit_keywords_tooltip = L(lang, "text", "keywords_edit_tt")
PF.form_edit_summary = L(lang, "forms", "summary_edit_title")
PF.edit_summary_tooltip = L(lang, "text", "summary_edit_tt")
PF.form_edit_quotes = L(lang, "forms", "quotes_edit_title")
PF.edit_quotes_tooltip = L(lang, "text", "quotes_edit_tt")
PF.form_edit_references = L(lang, "forms", "references_edit_title")
PF.edit_references_tooltip = L(lang, "text", "references_edit_tt")
PF.form_edit_justifications = L(lang, "forms", "justifications_form")
PF.edit_justifications_tooltip = L(lang, "text", "justifications_edit_tt")
PF.form_edit_objections = L(lang, "forms", "objections_form")
PF.edit_objections_tooltip = L(lang, "text", "objections_edit_tt")
PF.form_new_arg_title = L(lang, "forms", "new_arg_title")
PF.arg_new_title_field_base = L(lang, "text", "arg_new_title_field_base")
PF.justification_add_label = L(lang, "text", "justification_add_label")
PF.justification_add_tt = L(lang, "text", "justification_add_tt")
PF.objection_add_label = L(lang, "text", "objection_add_label")
PF.objection_add_tt = L(lang, "text", "objection_add_tt")
PF.summary = L(lang, "text", "summary_title")
PF.no_summary = L(lang, "text", "none_summary_msg")
PF.quotes = L(lang, "text", "quotes_title")
PF.no_quotes = L(lang, "text", "none_quotes_msg")
PF.references = L(lang, "text", "references_title")
PF.no_references = L(lang, "text", "none_references_msg")
PF.justifications = L(lang, "text", "justifications_title")
PF.no_justifications = L(lang, "text", "none_justifications_msg")
PF.no_justifications_short = L(lang, "text", "none_justifications_short_msg")
PF.objections = L(lang, "text", "objections_title")
PF.no_objections = L(lang, "text", "none_objections_msg")
PF.no_objections_short = L(lang, "text", "none_objections_short_msg")
PF.form_edit_detailed_debate = L(lang, "forms", "detailed_debate_edit_title")
PF.edit_detailed_debate_tooltip = L(lang, "text", "detailed_debate_edit_tt")
PF.detailed_debate = L(lang, "text", "detailed_debate_title")
PF.detailed_debate_label = L(lang, "text", "detailed_debate_label_text")
PF.arg_is_debate = L(lang, "text", "arg_is_debate_msg")
PF.form_edit_sections = L(lang, "forms", "sections_edit_title")
PF.edit_sections_tooltip = L(lang, "text", "sections_edit_tt")
PF.form_edit_interlanguage = L(lang, "forms", "interlanguage_edit_title")
PF.edit_interlanguage_tooltip = L(lang, "text", "interlanguage_edit_tt")
PF.edit = L(lang, "text", "edit_label")
PF.rename_link_text = L(lang, "text", "rename_link_label")
PF.type_justification = L(lang, "params", "justification_type_label")
PF.type_objection = L(lang, "params", "objection_type_label")
PF.unknown_author = L( lang, "text", "unknown_author" )
PF.more_quotes_button = L(lang, "text", "more_quotes_button")
PF.more_quotes_note = L(lang, "text", "more_quotes_note")
PF.parent_debates_title_1 = L(lang, "text", "parent_debates_title_1")
PF.parent_debates_title_n = L(lang, "text", "parent_debates_title_n")
PF.cat_arguments = L(lang, "categories", "arguments")
PF.cat_no_keywords = L(lang, "categories", "keywords_missing")
PF.cat_no_summary = L(lang, "categories", "summary_missing")
PF.cat_no_quotes = L(lang, "categories", "quotes_missing")
PF.cat_no_references = L(lang, "categories", "references_missing")
PF.cat_no_justifications = L(lang, "categories", "justifications_missing")
PF.cat_no_objections = L(lang, "categories", "objections_missing")
PF.cat_no_sections = L(lang, "categories", "sections_missing")
PF.cat_no_content = L(lang, "categories", "content_missing")
PF.cat_has_detailed_debate = L(lang, "categories", "has_detailed_debate")
PF.cat_no_authors = L(lang, "categories", "authors_missing")
-- props “contenu”
PF.prop_arg_name = L(lang, "props", "arg_page_name")
PF.prop_arg_number = L(lang, "props", "arg_number")
PF.prop_arg_content = L(lang, "props", "arg_content")
PF.prop_argument_map = L(lang, "props", "argument_map")
PF.prop_justif_list = L(lang, "props", "justif_list")
PF.prop_obj_list = L(lang, "props", "obj_list")
PF.prop_warn_justif = L(lang, "props", "warn_justif")
PF.prop_warn_obj = L(lang, "props", "warn_obj")
PF.prop_keyword = L(lang, "props", "keyword")
PF.prop_detailed_debate = L(lang, "props", "detailed_debate")
PF.prop_additional_content = L(lang, "props", "additional_content")
-- props refs (depuis Références, pas depuis citations)
PF.prop_author = L( lang, "props", "author" )
PF.prop_article_name = L( lang, "props", "article_name" )
PF.prop_work_name = L( lang, "props", "work_name" )
PF.prop_publishing_house = L( lang, "props", "publishing_house" )
PF.prop_place_of_publication = L( lang, "props", "place_of_publication" )
-- props “ask/parents/breadcrumb”
PF.prop_arg_pro = L(lang, "props", "arg_pro")
PF.prop_arg_con = L(lang, "props", "arg_con")
PF.prop_justif = L(lang, "props", "justif")
PF.prop_obj = L(lang, "props", "obj")
PF.prop_breadcrumb = L(lang, "props", "breadcrumb")
PF.prop_debate_name = L(lang, "props", "debate_name")
PF.prop_parent_debate = L(lang, "props", "parent_debate")
PF.prop_parent_argument = L(lang, "props", "parent_argument")
-- SEO
PF.seo_description = L(lang, "text", "seo_description")
PF.seo_title_prefix = L(lang, "seo", "title_prefix")
PF.seo_title_mode = L(lang, "seo", "title_mode")
PF.seo_image_url = L(lang, "seo", "image_url")
PF.seo_image_alt = L(lang, "seo", "image_alt")
PF.seo_section = L(lang, "seo", "section")
PF.seo_site_name = L(lang, "seo", "site_name")
PF.seo_twitter_site = L(lang, "seo", "twitter_site")
PF.seo_locale = L(lang, "seo", "locale")
PF.seo_author = L(lang, "seo", "author")
-- INITIALIZATION
PF.tpl_orphan_banner = L(lang, "templates", "orphan_banner")
PF.param_page = L(lang, "params", "page_field")
PF.param_displayed_title = L(lang, "params", "displayed_title_field")
PF.cat_initialized = L(lang, "categories", "initialized")
PF.cat_uninitialized = L(lang, "categories", "uninitialized")
PF.init_warning_title = L(lang, "text", "init_warning_title")
PF.init_warning_intro = L(lang, "text", "init_warning_intro")
PF.init_yes_finish_label = L(lang, "text", "init_yes_finish_label")
PF.init_yes_tt = L(lang, "text", "init_yes_tt")
PF.init_ok_text = L(lang, "text", "init_ok_text")
PF.init_no_rename_label = L(lang, "text", "init_no_rename_label")
PF.init_summary_pro = L(lang, "text", "init_summary_pro")
PF.init_summary_con = L(lang, "text", "init_summary_con")
PF.init_summary_justif = L(lang, "text", "init_summary_justif")
PF.init_summary_obj = L(lang, "text", "init_summary_obj")
do
local q = args["quotes"]
if type(q) == "string" and q ~= "" then
args["_quotes_wikitext"] = q
local ok, r = pcall(F.preprocess, F, q)
if ok and type(r) == "string" then
args["quotes"] = wkRenderQuotesFromSerialized( r, pv, lang, cats, PF )
else
args["quotes"] = ""
end
end
end
local hasResume = args["summary"] ~= nil
local hasQuotes = args["quotes"] ~= nil
local hasRefs = (args["bibliography"] ~= nil) or (args["webliography"] ~= nil) or (args["videography"] ~= nil)
table_insert(pieces, F:preprocess("{{SHORTDESC: " .. L(lang, "text", "shortdesc_plain") .. "}}"))
table_insert(pieces, "[[" .. PF.prop_arg_number .. ":: " .. pv.pageId .. "| ]]")
table_insert(cats, "[[Category:" .. PF.cat_arguments .. "]]")
table_insert(pieces, renderBreadcrumbJSONLD(pv, lang))
table_insert(pieces, renderTitleWarnings(pv, cats, lang))
do
local titleLookup = buildBannerLabelLookup( lang, TITLE_WARNING_BANNER_KEYS )
local argLookup = buildBannerLabelLookup( lang, ARGUMENT_WARNING_BANNER_KEYS )
local out = {}
if args[ "title-warnings" ] then
table_insert( out, renderBannersFromArgList( args[ "title-warnings" ], titleLookup, pv, lang, cats ) )
end
if args[ "argument-warnings" ] then
table_insert( out, renderBannersFromArgList( args[ "argument-warnings" ], argLookup, pv, lang, cats ) )
end
if #out > 0 then
table_insert( pieces, table_concat( out ) )
end
end
table_insert(pieces, renderDebateParentsBoxes(pv, cats, lang, cache, PF))
table_insert(pieces, renderArgumentParentsBoxes(pv, cats, lang, cache, PF))
table_insert(pieces, renderInitializationBlock(args, pv, cats, lang, cache, PF))
local breadcrumbChain = renderAndSetBreadcrumb(pv, cats, lang, PF)
table_insert(pieces, renderKeywords(args, pv, cats, lang, PF))
table_insert(pieces, renderSummary(args, pv, cats, lang, PF))
table_insert(pieces, renderQuotes(args, pv, cats, lang, PF))
local detailed = renderDebateDetailed(args, pv, cats, lang, PF)
if detailed ~= "" then
table_insert(pieces, detailed)
else
local out = {}
local editRefs = addDataLink(
PF.form_edit_references,
pv.rawTitle,
" ",
PF.edit_references_tooltip
)
table_insert(out,
'<h2 class="section-modifiable">'
.. '<span style="margin-right: 0.5em;">[[File: ' .. PF.file_biblio .. ' | 15px | link= | alt=' .. PF.references .. ' | class=sub]]</span>'
.. PF.references
.. '<span class="modifier-section navigation-not-searchable noprint">' .. editRefs .. '</span></h2>'
)
do
local refsHtml = wkRenderReferencesFromRaw( rawB, rawW, rawV, pv, lang, cats, PF, nil, args )
if refsHtml ~= "" then
table_insert( out, '<ul class="references-argument">' .. refsHtml .. '</ul>' )
else
table_insert( out, '<div class="aucun-contenu navigation-not-searchable">' .. PF.no_references .. '</div>' )
table_insert( cats, "[[Category:" .. PF.cat_no_references .. "]]" )
end
end
local maps = computeArgumentMapsFromData(args, PF)
local justifItems = (maps.justif and maps.justif.items) or {}
local objItems = (maps.obj and maps.obj.items) or {}
-- Justifications
local editJust = addDataLink(
PF.form_edit_justifications,
pv.rawTitle,
" ",
PF.edit_justifications_tooltip
)
table_insert(out,
'<h2 class="section-modifiable">'
.. '<span style="margin-right: 0.5em;">[[File: ' .. PF.file_arg_pro .. ' | 22px | link= | alt=' .. PF.justifications .. ']]</span>'
.. PF.justifications
.. '<span class="modifier-section navigation-not-searchable noprint">' .. editJust .. '</span></h2>'
)
do
local raw = args[ "justification-warnings" ]
raw = ( type( raw ) == "string" ) and t_trim( raw ) or ""
if raw ~= "" then
local lookup = buildSummaryLabelLookup( lang, JUSTIFICATION_WARNING_BANNER_KEYS )
for _, x in ipairs( splitCSV( raw, "," ) ) do
local key = resolveKeyFromLookup( x, lookup, JUSTIFICATION_WARNING_BANNER_KEYS )
if key ~= "" then
table_insert(
out,
renderSubsectionBanner(
pv,
lang,
key,
{
reorg = true,
form = PF.form_edit_justifications,
tooltip = PF.edit_justifications_tooltip
},
cats
)
)
end
end
end
end
if #justifItems > 0 then
local ulJ = html_create("ul"):addClass("argument-list is-pro")
local lis = {}
for i, it in ipairs(justifItems) do
table_insert(lis, wkRenderArgumentLi(pv, PF, it, "justif", i))
end
ulJ:wikitext(table_concat(lis, "\n"))
table_insert(out, tostring(ulJ))
else
table_insert(out, '<div class="aucun-argument navigation-not-searchable">' .. PF.no_justifications .. '</div>')
table_insert(cats, "[[Category:" .. PF.cat_no_justifications .. "]]")
end
do
local baseField = tostring( PF.arg_new_title_field_base or "" )
local sideLabel = tostring( PF.side_pro_word or "pour" )
local label = tostring( PF.justification_add_label or "" )
local tooltip = tostring( PF.justification_add_tt or "" )
local rqForm = tostring( PF.form_new_arg_title or "" )
local rqQuery = {}
rqQuery[ baseField .. "[type]" ] = tostring( PF.type_justification or "" )
rqQuery[ baseField .. "[ID]" ] = tostring( pv.pageId or "" )
rqQuery[ "_run" ] = "1"
table_insert( out,
'<div class="bouton-ajouter wk-btn mw-ui-button navigation-not-searchable noprint">'
.. wkRunQueryHtmlTagButton( rqForm, label, tooltip, rqQuery )
.. '</div>'
)
end
-- Objections
local editObj = addDataLink(
PF.form_edit_objections,
pv.rawTitle,
" ",
PF.edit_objections_tooltip
)
table_insert(out,
listBreaker()
.. '<h2 class="section-modifiable">'
.. '<span style="margin-right: 0.5em;">[[File: ' .. PF.file_arg_con .. ' | 22px | link= | alt=' .. PF.objections .. ' | class=mw-no-invert]]</span>'
.. PF.objections
.. '<span class="modifier-section navigation-not-searchable noprint">' .. editObj .. '</span></h2>'
)
do
local raw = args[ "objection-warnings" ]
raw = ( type( raw ) == "string" ) and t_trim( raw ) or ""
if raw ~= "" then
local lookup = buildSummaryLabelLookup( lang, OBJECTION_WARNING_BANNER_KEYS )
for _, x in ipairs( splitCSV( raw, "," ) ) do
local key = resolveKeyFromLookup( x, lookup, OBJECTION_WARNING_BANNER_KEYS )
if key ~= "" then
table_insert(
out,
renderSubsectionBanner(
pv,
lang,
key,
{
reorg = true,
form = PF.form_edit_objections,
tooltip = PF.edit_objections_tooltip
},
cats
)
)
end
end
end
end
if #objItems > 0 then
local ulO = html_create("ul"):addClass("argument-list is-con")
local lis = {}
for i, it in ipairs(objItems) do
table_insert(lis, wkRenderArgumentLi(pv, PF, it, "obj", i))
end
ulO:wikitext(table_concat(lis, "\n"))
table_insert(out, tostring(ulO))
else
table_insert(out, '<div class="aucun-argument navigation-not-searchable">' .. PF.no_objections .. '</div>')
table_insert(cats, "[[Category:" .. PF.cat_no_objections .. "]]")
end
do
local baseField = tostring( PF.arg_new_title_field_base or "" )
local label = tostring( PF.objection_add_label or "" )
local tooltip = tostring( PF.objection_add_tt or "" )
local rqForm = tostring( PF.form_new_arg_title or "" )
local rqQuery = {}
rqQuery[ baseField .. "[type]" ] = tostring( PF.type_objection or "" )
rqQuery[ baseField .. "[ID]" ] = tostring( pv.pageId or "" )
rqQuery[ "_run" ] = "1"
table_insert( out,
'<div class="bouton-ajouter wk-btn mw-ui-button navigation-not-searchable noprint">'
.. wkRunQueryHtmlTagButton( rqForm, label, tooltip, rqQuery )
.. '</div>'
)
end
-- Inject rendu
table_insert(pieces, table_concat(out))
end
table_insert(pieces, renderDebatesParentsBottom(pv, lang, cache, PF))
if args["interlanguage"] then
table_insert(pieces, args["interlanguage"])
end
table_insert(pieces, renderRubriques(args, cats, lang, PF))
if (not hasResume) and (not hasQuotes) and (not hasRefs) then
table_insert(cats, "[[Category:" .. PF.cat_no_content .. "]]")
end
local maps = computeArgumentMapsFromData(args, PF)
setSemanticData(args, pv, lang, cache, PF, breadcrumbChain, maps, rawB, rawW, rawV)
renderFullSEO( args, pv, lang )
table_insert(pieces, table_concat(cats, ""))
table_insert(pieces,
'<span id="bouton-renommer" class="modifier-rubrique navigation-not-searchable noprint" style="display: none;">[[Special:MovePage/'
.. pv.encoded .. '| ' .. PF.rename_link_text .. ']]</span>'
)
table_insert(pieces, '<span id="bouton-modifier-categories" class="modifier-rubrique navigation-not-searchable noprint" style="display: none;">'
.. addDataLink(PF.form_edit_sections, pv.rawTitle, PF.edit, PF.edit_sections_tooltip)
.. '</span>'
)
if args["interlanguage"] and t_trim( tostring( args["interlanguage"] or "" ) ) ~= "" then
table_insert( pieces,
'<span id="bouton-modifier-interlangue" class="modifier-rubrique navigation-not-searchable noprint" style="display: none;"'
.. ' data-wk-tooltip="' .. escapeAttr( PF.edit_interlanguage_tooltip ) .. '">'
.. addDataLink( PF.form_edit_interlanguage, pv.rawTitle, PF.edit, PF.edit_interlanguage_tooltip )
.. '</span>'
)
end
table_insert( pieces, F:preprocess( "__NOCACHE__" ) )
return table_concat(pieces, "")
end
return p