„Modul:Schaufenster“ – Versionsunterschied
Erscheinungsbild
KKeine Bearbeitungszusammenfassung |
KKeine Bearbeitungszusammenfassung Markierung: Zurückgesetzt |
||
| Zeile 239: | Zeile 239: | ||
local ohneVorlagen= tostring(args.ohneVorlagen or "1") ~= "0" | local ohneVorlagen= tostring(args.ohneVorlagen or "1") ~= "0" | ||
local linkParam = args.link | local linkParam = args.link | ||
local link = not ( | local link = not (linkParam == "0" or linkParam == 0 or linkParam == false) | ||
local refs = tostring(args.refs or args.reflist or "0") ~= "0" | local refs = tostring(args.refs or args.reflist or "0") ~= "0" | ||
local inline = tostring(args.inline or args.zeilig or "0") ~= "0" | local inline = tostring(args.inline or args.zeilig or "0") ~= "0" | ||
Version vom 8. Dezember 2025, 12:34 Uhr
Die Dokumentation für dieses Modul kann unter Modul:Schaufenster/Doku erstellt werden
-- Modul:Schaufenster (VE‑Marker & Suchsteuerung)
-- Zweck:
-- * Transklusion von Quellseiten-Text (komplett, Einleitung, Abschnitt, markierte Spanne)
-- * Optionales Strippen von Kategorien und bestimmten Vorlagen
-- * Steuerung des Suchverhaltens für Zielseiten (CirrusSearch):
-- - "navigation-not-searchable" → Transklusionsinhalt wird nicht indiziert
-- - "searchaux" → Transklusionsinhalt wird nur schwach gewichtet
-- * VE‑taugliche Marker: {{SchaufensterBegin|Label}} … {{SchaufensterEnde|Label}}
-- (kompatibel: {{SchaufensterBeginn|…}}, {{SchaufensterBegin(n)|…}}, {{SchaufensterEnde|…}})
-- * Zusätzlich Erkennung von LST‑Markern: <section begin="Label"/> … <section end="Label"/>
--
-- Aufruf (empfohlen über eine Vorlage, z. B. {{ZitatTransklusion}}):
-- {{#invoke:Schaufenster|zeige
-- | Seite = Werk:Beispiel
-- | Abschnitt = Ein Abschnittstitel (optional)
-- | Marke = Zitat-001 (optional; dominiert gegenüber Abschnitt/Lead)
-- | suche = ignorieren | abwerten | (leer) (optional; Default: ignorieren)
-- | ohneKat = 1/0 (Default 1)
-- | ohneVorlagen = 1/0 (Default 1)
-- | link = 1 / eigener Linktext (optional: Quelle-Link unterhalb)
-- | refs = 1 (optional: <references/> am Ende)
-- }}
local p = {}
local debugModus = false
-- Utils ----------------------------------------------------------------------
local u = {}
function u.trim(s)
if not s then return "" end
return (mw.text.trim(tostring(s)))
end
function u.esc(s)
-- Escape für Lua-Pattern
return (mw.ustring.gsub(s, "([%(%)%.%%%+%-%*%?%[%^%$])", "%%%1"))
end
-- Einfache Listenlese-Funktion: jede nicht-leere Zeile = Eintrag
local function ladeListeVonSeite(titel)
local t = mw.title.new(titel)
if not t or not t.exists then return {} end
local text = t:getContent() or ""
-- Zeilenenden vereinheitlichen: \r\n und \r -> \n
text = mw.ustring.gsub(text, "\r\n?", "\n")
-- (optional) UTF-8-BOM entfernen
text = mw.ustring.gsub(text, "^\239\187\191", "")
local out = {}
for line in mw.text.gsplit(text, "\n", true) do
line = mw.text.trim(line)
if line ~= "" and not line:match("^%-%-") and not line:match("^//") and not line:match("^#") then
table.insert(out, line)
end
end
return out
end
-- Kategorien entfernen ([[Kategorie:…]])
local function stripKategorien(text)
-- grob, aber effektiv; mehrfach ausführen ist ok
text = mw.ustring.gsub(text, "%[%[%s*[Kk]ategorie:%s*[^%]]+%]%]%s*", "")
return text
end
-- Ausgewählte Vorlagen entfernen (Namen werden aus einer Liste geladen)
local function stripVorlagen(text, namen)
for _, name in ipairs(namen or {}) do
local patt = u.esc(name)
-- Ohne Namensraumpräfix
text = mw.ustring.gsub(text, "{{%s*" .. patt .. "%s*}}", "")
text = mw.ustring.gsub(text, "{{%s*" .. patt .. "[^}]*}}", "")
-- Mit Namensraumpräfix (Vorlage:/Template:)
text = mw.ustring.gsub(text, "{{%s*[Vv]orlage:%s*" .. patt .. "%s*}}", "")
text = mw.ustring.gsub(text, "{{%s*[Vv]orlage:%s*" .. patt .. "[^}]*}}", "")
text = mw.ustring.gsub(text, "{{%s*[Tt]emplate:%s*" .. patt .. "%s*}}", "")
text = mw.ustring.gsub(text, "{{%s*[Tt]emplate:%s*" .. patt .. "[^}]*}}", "")
end
return text
end
-- Abschnitt per Überschrift extrahieren (einfacher Parser)
local function extrahiereBereich(text, heading)
if not heading or u.trim(heading) == "" then return nil end
local H = u.esc(heading)
-- Finde erste Überschrift mit diesem Titel
local startPos = mw.ustring.find(text, "==+%s*" .. H .. "%s*==+")
if not startPos then return nil end
-- Finde nächste Überschrift nach startPos
local nextPos = mw.ustring.find(text, "\n==+%s*[^=\n].-==+", startPos + 1)
local seg
if nextPos then
seg = mw.ustring.sub(text, startPos, nextPos - 1)
else
seg = mw.ustring.sub(text, startPos)
end
-- schneide die Überschriftenzeile selbst ab
local body = seg:gsub("^.-\n", "")
return body
end
-- Einleitung (vor erster Überschrift)
local function extrahiereLead(text)
local firstH = mw.ustring.find(text, "==+")
if firstH then
return mw.ustring.sub(text, 1, firstH - 1)
end
return text
end
-- Markenspannen extrahieren ---------------------------------------------------
local function extrahiereMarke(text, label)
label = u.trim(label)
if label == "" then return nil end
local L = u.esc(label)
-- (a) VE‑Marker via Vorlagen: {{SchaufensterBegin|Label}} ... {{SchaufensterEnde|Label}}
local function find_any(txt, patterns, init)
for _, patt in ipairs(patterns) do
local s, e = mw.ustring.find(txt, patt, init)
if e then return s, e end
end
return nil, nil
end
local beginNames = { "SchauBegin", "SchaufensterBegin", "SchaufensterBeginn", "SF%-Begin" }
local endNames = { "SchauEnde", "SchaufensterEnde", "SF%-Ende" }
local beginPats = {}
for _, name in ipairs(beginNames) do
table.insert(beginPats, "{{%s*" .. name .. "%s*|%s*" .. L .. "%s*}}")
end
local b1s, b1e = find_any(text, beginPats)
if b1e then
local endPats = {}
for _, name in ipairs(endNames) do
table.insert(endPats, "{{%s*" .. name .. "%s*|%s*" .. L .. "%s*}}")
end
local e2s = select(1, find_any(text, endPats, b1e + 1))
if e2s then
return mw.ustring.sub(text, b1e + 1, e2s - 1)
end
end
-- (b) LST‑Marker: <section begin="Label"/> ... <section end="Label"/>
do
local lb1s, lb1e = mw.ustring.find(text, "<%s*[Ss]ection%s+[Bb]egin%s*=%s*[\"']" .. L .. "[\"']%s*/%s*>")
if lb1e then
local le2s = mw.ustring.find(text, "<%s*[Ss]ection%s+[Ee]nd%s*=%s*[\"']" .. L .. "[\"']%s*/%s*>", lb1e + 1)
if le2s then
return mw.ustring.sub(text, lb1e + 1, le2s - 1)
end
end
end
-- (c) Kommentar‑Marker (optional): <!-- BF:BEGIN Label --> ... <!-- BF:END Label -->
do
local cb1s, cb1e = mw.ustring.find(text, "<!%-%-%s*[BbSs][Ff]%s*:%s*[Bb][Ee][Gg][Ii][Nn]%s+" .. L .. "%s*%-%->")
if cb1e then
local ce2s = mw.ustring.find(text, "<!%-%-%s*[BbSs][Ff]%s*:%s*[Ee][Nn][Dd]%s+" .. L .. "%s*%-%->", cb1e + 1)
if ce2s then
return mw.ustring.sub(text, cb1e + 1, ce2s - 1)
end
end
end
return nil
end
-- SMW-Property-Links in normale Links umwandeln (immer anwenden)
local function stripSMWPropertyLinks(text)
if not text or text == "" then return text end
-- [[Property::Wert|Label]] -> [[Wert|Label]]
text = mw.ustring.gsub(text, "%[%[%s*[%w_]+::([^|%]]+)%|([^%]]+)%]%]", "[[%1|%2]]")
-- [[Property::Wert]] -> [[Wert]]
text = mw.ustring.gsub(text, "%[%[%s*[%w_]+::([^%]]+)%]%]", "[[%1]]")
return text
end
-- --- Rekursiver Expander für innere {{Schaufenster …}} ----------------------
-- Parsen einer einfachen |key=value|-Liste (dein üblicher Vorlagenstil)
local function parseTemplateArgs(paramstr)
local a, i = {}, 1
for part in mw.text.gsplit(paramstr or "", "|", false) do
part = mw.text.trim(part)
if part ~= "" then
local k, v = part:match("^([^=]+)=(.*)$")
if k then
a[mw.text.trim(k)] = mw.text.trim(v)
else
a[i] = part; i = i + 1
end
end
end
return a
end
-- Expandiert innere {{Schaufenster …}}-Aufrufe, ohne die Vorlage erneut zu transkludieren.
-- depth: wie viele Schaufenster-Ebenen dürfen noch expandiert werden (>=0).
local function expandInnerSchaufenster(text, depth, outerFrame)
if not text or text == "" or (depth or 0) <= 0 then
return text
end
-- Achtung: einfacher Matcher – erwartet "normale" Benutzung (keine verschachtelten "{{" in Parametern)
local patt = "{{%s*[Ss]chaufenster%s*(.-)}}"
return (mw.ustring.gsub(text, patt, function(paramstr)
-- Wenn doch verschachtelte "{{" im Param, gib unverändert zurück (Parser macht das dann)
if paramstr:find("{{", 1, true) then
return "{{Schaufenster" .. paramstr .. "}}"
end
local a = parseTemplateArgs(paramstr)
-- künstlicher Frame für p.zeige (direkter Funktionsaufruf, KEIN Template-Call)
local fake = {
args = a,
-- p.zeige nutzt frame:preprocess → leite auf den äußeren Frame durch
preprocess = function(_, w) return outerFrame:preprocess(w) end,
getParent = function() return nil end
}
-- expandiere eine Ebene tiefer
return p.zeige(fake, (depth or 1) - 1)
end))
end
-- Hauptfunktion --------------------------------------------------------------
function p.zeige(frame, depth)
depth = tonumber(depth) or 2
local args = frame:getParent() and frame:getParent().args or frame.args
local depthArg = tonumber(args.tiefe or args.depth)
if depthArg then depth = depthArg end
local seitentitel = u.trim(args.Seite or args.Seitenname or args[1] or "")
local bereich = u.trim(args.Abschnitt or args.Bereich or args[2] or "")
local marke = u.trim(args.Marke or args.label or "")
local suchmodus = (args.suche or args.modus or ""):lower() -- "ignorieren" | "abwerten" | ""
local ohneKat = tostring(args.ohneKat or "1") ~= "0"
local ohneVorlagen= tostring(args.ohneVorlagen or "1") ~= "0"
local linkParam = args.link
local link = not (linkParam == "0" or linkParam == 0 or linkParam == false)
local refs = tostring(args.refs or args.reflist or "0") ~= "0"
local inline = tostring(args.inline or args.zeilig or "0") ~= "0"
local linkpos = (args.linkpos or args.linkposition or "prefix"):lower()
if seitentitel == "" then
return "<strong class=\"error\">Schaufenster: Kein Seitentitel angegeben.</strong>"
end
local t = mw.title.new(seitentitel)
if not t or not t.exists then
-- Fehlende Seite: gib den roten Link zur Anlage/Prüfung des Titels aus (MediaWiki-Standardverhalten)
return string.format("[[%s]]", seitentitel)
end
local content = t:getContent() or ""
local debugText = ""
if debugModus then
debugText = debugText .. string.format("[Debug] Quelle geladen: %s (%d Zeichen)\n", t.prefixedText, mw.ustring.len(content))
end
-- 1) Vorrang: markierte Spanne
local out
if marke ~= "" then
out = extrahiereMarke(content, marke) or ""
if debugModus then debugText = debugText .. string.format("[Debug] Marke '%s' extrahiert.\n", marke) end
end
-- 2) Abschnitt
if not out or out == "" then
if bereich ~= "" then
out = extrahiereBereich(content, bereich) or ""
if debugModus then debugText = debugText .. string.format("[Debug] Abschnitt '%s' extrahiert.\n", bereich) end
else
-- 3) Fallback: ganze Seite (kein Marker/Abschnitt)
out = content
if debugModus then debugText = debugText .. "[Debug] Fallback: ganze Seite.\n" end
end
end
-- 4) Filter anwenden
if ohneKat then out = stripKategorien(out) end
if ohneVorlagen then
local namen = ladeListeVonSeite("Vorlage:Schaufenster/Vorlagenfilter")
if #namen == 0 then namen = ladeListeVonSeite("Template:Schaufenster/Vorlagenfilter") end
if #namen == 0 then namen = ladeListeVonSeite("MediaWiki:Vorlagenfilter") end
out = stripVorlagen(out, namen)
end
-- SMW-Property-Links immer entschärfen
out = stripSMWPropertyLinks(out)
-- Innere {{Schaufenster …}} im Ergebnis vorab expandieren (verhindert Template-Selbstrekursion)
out = expandInnerSchaufenster(out, depth, frame)
-- 5) Ausgabe verpacken (Suchsteuerung)
local cls
if suchmodus == "abwerten" or suchmodus == "aux" then
cls = "searchaux"
elseif suchmodus == "ignorieren" or suchmodus == "exclude" or suchmodus == "" then
cls = "navigation-not-searchable"
else
cls = nil
end
-- Prefix-Link (später im Wrapper ausgeben)
local html = {}
local prefixHTML = ""
if link and linkpos == 'prefix' then
local linkLabel
if type(linkParam) == 'string' and linkParam ~= '' and linkParam ~= '1' then
linkLabel = linkParam
else
linkLabel = '▷'
end
prefixHTML = string.format('[[%s|%s]] ', t.prefixedText, linkLabel)
end
local inlineMode = inline or (link and linkpos == 'prefix')
if cls then
-- Immer Block-Wrapper für klare VE-Grenzen
table.insert(html, '<div class="schaufenster-content ' .. cls .. '">')
table.insert(html, prefixHTML)
table.insert(html, out)
table.insert(html, '</div>\n')
else
if inlineMode then
-- Auch ohne Klasse einen Block-Wrapper setzen
table.insert(html, '<div class="schaufenster-content">')
table.insert(html, prefixHTML)
table.insert(html, out)
table.insert(html, '</div>\n')
else
-- Klassischer Blockmodus mit Absatz-Puffern
table.insert(html, '\n' .. out .. '\n')
end
end
-- Suffix-Link (unterhalb; außerhalb des Blocks)
if link and linkpos ~= 'prefix' then
local linkLabel
if type(linkParam) == 'string' and linkParam ~= '' and linkParam ~= '1' then
linkLabel = linkParam
else
linkLabel = '▷ Quelle'
end
table.insert(html, string.format('\n<div class="schaufenster-quelle">[[%s|%s]]</div>', t.prefixedText, linkLabel))
end
if refs then
-- refs typischerweise unterhalb; Blockgrenze ist bereits gesetzt
table.insert(html, '\n<references responsive="1" />')
end
local wikitext = table.concat(html)
local rendered = frame:preprocess(wikitext)
if debugModus then
return string.format("<pre>%s</pre>%s", debugText, rendered)
end
return rendered
end
return p