Modul:Vorlage:Personendaten
Zur Navigation springen
Zur Suche springen
Vorlagenprogrammierung | Diskussionen | Lua | Test | Unterseiten | |||
---|---|---|---|---|---|---|---|
Modul | Deutsch | English
|
Modul: | Dokumentation |
local Personendaten = { serial = "2017-01-20" }
--[=[
{{Personendaten}}
]=]
local Config = {
errCatTop = "Wikipedia:Vorlagenfehler/",
errMsg = { bidi = "[[Bidirektionales Steuerzeichen]]",
casing = "Groß-/Kleinschreibung",
empty = "Pflichtparameter leer",
required = "Pflichtparameter fehlt",
unknown = "Unbekannt",
FATAL = "Interner Fehler" },
errTypesArgs = { "unknown", "casing", "empty", "required", "bidi" },
maxAge = 140,
order = { "NAME",
"ALTERNATIVNAMEN",
"KURZBESCHREIBUNG",
"GEBURTSDATUM",
"GEBURTSORT",
"STERBEDATUM",
"STERBEORT",
"SORTIERUNG" },
params = { NAME = { lazy=false, least=true },
ALTERNATIVNAMEN = { lazy=true, least=true },
ALTERNATIVNAME = { },
KURZBESCHREIBUNG = { lazy=false, least=true },
GEBURTSDATUM = { lazy=false, least=true },
GEBURTSORT = { lazy=true, least=true },
STERBEDATUM = { lazy=true, least=true },
STERBEORT = { lazy=true, least=true },
SORTIERUNG = { lazy=true, least=false } },
section = "Vorlage_Personendaten",
self = "Vorlage:Personendaten",
title = mw.title.getCurrentTitle(),
warnings = { NAME =
{ find = { ["("] = "enthält '('",
["#"] = "enthält '#'" }
},
ALTERNATIVNAMEN =
{ find = { ["Pseud."] = "Pseudonym",
["bürgerlicher Name"]
= "wirklicher Name",
["eigentlicher Name"]
= "wirklicher Name",
["richtiger Name"]
= "wirklicher Name",
["bürgerlich "]
= "wirklicher Name",
["eigentlich "]
= "wirklicher Name",
["voller Name"]
= "vollständiger Name" }
},
--KURZBESCHREIBUNG = { },
GEBURTSDATUM =
{ find = { Taufdatum = "getauft",
Taufe = "getauft",
bezeugt = "vor …",
dokumentiert = "vor …",
["erwähnt"] = "vor …",
nachweisbar = "vor …" }
},
GEBURTSORT = { link = true },
STERBEDATUM =
{ find = { beerdigt = "begraben",
Beerdigung = "begraben",
["Begräbnis"] = "begraben",
["frühestens "] = "nach …",
["nicht vor"] = "nach …",
vermisst = "nach …",
["vermißt"] = "nach …",
verschollen = "nach …" }
},
STERBEORT = { link = true },
Datum =
{ find = { Jh = "Jahrhundert",
["ungefähr "] = "um …",
["gegen "] = "um …",
["etwa "] = "um …",
["circa "] = "um …",
["ca."] = "um …",
["~"] = "um …",
["≈"] = "um …",
["später als"] = "nach …",
["frühestens"] = "nach …",
["nicht vor"] = "nach …",
["oder später"] = "nach …" },
match = { ["%.Jahrhundert"]
= "(Punkt Leerzeichen)",
["v%.%s*[du]%.%s*Z%."] = "v. Chr.",
["n%.%s*[du]%.%s*Z."] = "(ohne)",
["u%.%s*Z."] = "(ohne)",
["n%.%s*Chr%."] = "(ohne)",
["Ende des"]
= "um (mittlere Jahrezahl)" }
}
}
}
local Errors, Lookup
if mw.site.server:find( "wmflabs", 1, true ) then
local lucky, ignore = pcall( require, "No Globals" )
Lookup = true
end
Personendaten.fetch = function ( assigned, acquire )
-- Binde Standardbibliotheks-Modul ein
-- Parameter:
-- assigned -- string mit Name
-- "DateTime"
-- "FormatNum"
-- acquire -- string mit abweichendem Modulnamen, oder false
-- Rückgabewert: table des Moduls
-- error: Modul nicht gefunden
local r
if Personendaten.extern then
r = Personendaten.extern[ assigned ]
else
Personendaten.extern = { }
end
if not r then
local s = assigned
local lucky, g
if acquire then
s = acquire
end
lucky, g = pcall( require, "Module:" .. s )
if type( g ) == "table" then
r = g[ assigned ]()
Personendaten.extern[ assigned ] = r
else
fehler( "Modul", g )
error( string.format( "Personendaten.fetch(%s) %s", s, g ) )
end
end
return r
end -- Personendaten.fetch()
local function failed( album, add )
-- Collect single errors
-- album -- string, with collection name
-- add -- string, with arg name
Errors = Errors or { }
Errors[ album ] = Errors[ album ] or { }
table.insert( Errors[ album ], add .. "=" )
end -- failed()
local function failure( alert )
-- Perform error handling
-- alert -- string, with message
-- Returns appropriate string
local err, r
mw.addWarning( string.format( "<br>[[#%s|PERSONENDATEN]] – %s",
Config.section, alert ) )
err = mw.html.create( "span" )
:addClass( "error" )
:addClass( "metadata" ) -- Bis Altbestand sauber
:css( "display", "none" ) -- Bis Altbestand sauber
:wikitext( string.format( "[[%s]] – %s",
Config.self, alert ) )
r = tostring( err )
if Config.title.namespace == 0 then
local s = string.format( "%s/%s",
Config.errCatTop, Config.self )
r = string.format( "%s[[Category:%s]]", r, s )
end
return r
end -- failure()
local function fair( assert, assigned )
-- Content validation
-- assert -- table, with rules for this parameter
-- assigned -- string, with parameter value
-- Returns string with error message, or not
local r, rules, s
local fairy = function ( a )
if r then
r = string.format( "%s, %s", r, a )
else
r = a
end
end -- fair.fairy()
if assigned:match( "&#?%w+;" ) then
fairy( "HTML-Entities unzulässig" )
end
if assigned:find( "{{", 1, true ) then
fairy( "Vorlagenbenutzung unzulässig" )
end
if not assert.link and assigned:find( "[[", 1, true ) then
fairy( "Wikilink unzulässig" )
end
if assert.find then
for k, v in pairs( assert.find ) do
if mw.ustring.find( assigned, k, 1, true ) then
s = string.format( "'%s' statt '%s' verwenden",
v, mw.text.trim( k ) )
fairy( s )
end
end -- for k, v
end
if assert.match then
for k, v in pairs( assert.match ) do
s = mw.ustring.match( assigned, k )
if s then
s = string.format( "'%s' statt '%s' verwenden", v, s )
fairy( s )
end
end -- for k, v
end
return r
end -- fair()
local function features( args )
-- Parameter validation
-- args -- table, with template parameters
-- Returns string with error message, or not
local r, s
for k, v in pairs( args ) do
s = k:upper()
if Config.params[ s ] then
Config.params[ s ].s = v
if s ~= k then
failed( "casing", k )
end
else
failed( "unknown", k )
end
end -- for k, v
if Config.params.SORTIERUNG.s == "-" then
Config.params.SORTIERUNG.s = ""
end
s = Config.params.ALTERNATIVNAME.s
if s then
failed( "unknown", "ALTERNATIVNAME" )
if not Config.params.ALTERNATIVNAMEN.s then
Config.params.ALTERNATIVNAMEN.s = s
end
end
for k, v in pairs( Config.params ) do
if v.least then
if v.s then
if v.s == "" then
v.s = false
if not v.lazy then
failed( "empty", k )
end
end
else
failed( "required", k )
end
end
end -- for k, v
if Errors then
local e
for k, v in pairs( Config.errTypesArgs ) do
e = Errors[ v ]
if e then
r = string.format( "%s: %s",
Config.errMsg[ v ],
table.concat( e, ", " ) )
break -- for k, v
end
end -- for k, v
end
return r
end -- features()
local function fence( arg, at )
-- Day validation
-- arg -- string, with parameter name
-- at -- string, with single point of time
-- Returns string with error message, or not
local DateTime = Personendaten.fetch( "DateTime" )
local s = at
local d, r
if s:match( "^%l" ) then
s = s:gsub( "^begraben ", "" )
:gsub( "^getauft ", "" )
:gsub( "^nach ", "" )
:gsub( "^um ", "" )
:gsub( "^vor ", "" )
end
d = DateTime( s )
if not d then
r = string.format( "Datum nicht erkannt: '%s'", s )
elseif d.year then
if d.month then
local suggest, suitable
if d.dom then
suitable = "j. F Y"
else
suitable = "F Y"
end
suggest = d:format( suitable )
if s == suggest then
if d > DateTime() then
r = string.format( "Datum in der Zukunft: '%s'", s )
else
Config.params[ arg ].datetime = d
end
else
r = string.format( "'%s' soll sein '%s'", s, suggest )
end
end
else
r = string.format( "Jahr fehlt '%s'", s )
end
return r
end -- fence()
local function fenced( arg, assigned )
-- Date expression validation
-- arg -- string, with parameter name
-- assigned -- string, with parameter value
-- Returns string with error message, or not
local r = fair( Config.warnings.Datum, assigned )
if not r and not assigned:find( ". Jahr", 2, true ) then
local days
if assigned:find( "zwischen", 1, true ) then
days = { false, false }
days[ 1 ], days[ 2 ] =
assigned:match( "^zwischen (%s+) und (%s+)$" )
if not days[ 1 ] then
r = "Zeitspanne unverständlich"
end
else
days = mw.text.split( assigned, " oder " )
end
if not r and not assigned:find( "/", 2, true ) then
for k, v in pairs( days ) do
r = fence( arg, v )
if r then
break
end
end -- for k, v
end
end
return r
end -- fenced()
local function fences()
-- Date sequence validation
local r, r2
d0 = Config.params.GEBURTSDATUM.datetime
d = Config.params.STERBEDATUM.datetime
if d0 and d then
if d < d0 then
r = "Sterbedatum vor Geburtsdatum"
else
s = string.format( "%d years", Config.maxAge )
if d > d0:future( s ) then
r = string.format( "Lebensspanne über %d Jahre",
Config.maxAge )
end
end
end
end -- fences()
local function fill( arg )
-- Create table row, and check values
-- arg -- string, with parameter name
-- Returns
-- 1. nil, or string with table row, starting with \n
-- 2. nil, or string with error message
-- Returns string with table row, starting with \n
local s = Config.params[ arg ].s
local r, r2
if s and Config.params[ arg ].least then
local warn = Config.warnings[ arg ]
r = string.format( "\n|-\n|style='color:#AAAAAA'|%s\n|%s",
arg, s )
if warn then
r2 = fair( warn, s )
if not r2 and arg:find( "DATUM", 5, true ) then
r2 = fenced( arg, s )
end
if r2 then
r2 = string.format( "Wert von %s: %s", arg, r2 )
end
end
end
return r, r2
end -- fill()
local function flair( already )
-- Refine sort string for princes but not artists (no comma in NAME=)
-- Returns string with sort key
local r = already
if r:match( " [IVXL]" ) and
( r:match( " [IVXL]+[ .,]" ) or r:match( " [IVXL]+$" ) ) then
local start, s, space, suffix
start, s, suffix = r:match( "^(.+ )([IVXL]+)%.(.*)$" )
if not s then
start, s, suffix = r:match( "^(.+ )([IVXL]+)([ ,].*)$" )
end
if s then
local FormatNum = Personendaten.fetch( "FormatNum" )
if type( FormatNum.roman2number ) == "function" then
local k = FormatNum.roman2number( s )
if k then
r = string.format( "%s%d%s", start, k, suffix )
else
failed( "FATAL", "FormatNum.roman2number.type" )
end
else
failed( "FATAL", "FormatNum.roman2number" )
end
s = Config.title.text:match( " %((.+)%)$" )
if s then
r = string.format( "%s #%s", r, s )
end
end
end
return r
end -- flair()
local function flat()
-- Build sort string from NAME=
-- Returns string with sort key
local r = Config.params.NAME.s
local c, k, lrmetc, r2, s
for i = mw.ustring.len( r ), 1, -1 do
c = mw.ustring.codepoint( r, i, i )
if c < 128 then
if c == 34 or -- "
c == 39 or -- '
c == 45 or -- -
c == 96 then -- grave
-- . (?)
s = ""
elseif c == 95 then -- _
s = " "
c = 32
end
elseif c >= 160 and c <= 255 then
if c == 160 then -- nbsp
s = " "
c = 32
elseif c == 173 or c == 180 then -- shy, acute
s = ""
elseif c == 222 then -- THORN
s = "Th"
elseif c == 254 then -- thorn
s = "th"
end
elseif c <= 0x024F then -- Latin (591)
elseif c <= 0x036F then
if c >= 697 and c <= 735 then -- MODIFIER
s = ""
end
elseif c <= 0x1CF9 then -- Non-Latin (7417)
if c == 0x061C or -- ARABIC LETTER MARK
c == 0x0639 then -- ARABIC LETTER AIN
s = ""
end
elseif c <= 0x1EFF then -- Latin (7935)
elseif c <= 0x2069 then
if c >= 8192 and c <= 8202 then
s = " "
c = 32
elseif c >= 8203 and c <= 8223 then
s = ""
lrmetc = ( lrmetc or c == 8206 or c == 8207 )
elseif c >= 8234 and c <= 8238 then
s = ""
lrmetc = true
elseif c == 8239 then
s = " "
c = 32
elseif c >= 8294 and c <= 8297 then
s = ""
lrmetc = true
end
end
if c == 32 and k == 32 then
s = ""
end
if s then
local start
if i == 1 then
start = ""
else
start = mw.ustring.sub( r, 1, i - 1 )
end
r = string.format( "%s%s%s",
start,
s,
mw.ustring.sub( r, i + 1 ) )
s = false
end
k = c
end -- for i
if r:find( ",", 1, true ) then
if Config.params.NAME.s:find( ",[^ ]" ) then
r = r:gsub( ",([^ ])", ", $1" )
r2 = "Leerzeichen nach Komma erforderlich"
end
else
r = flair( r )
end
if lrmetc then
failed( "bidi", "NAME" )
end
return r, r2
end -- flat()
local function furnish()
-- Create table and defaultsort
-- Returns
-- 1. appropriate string
-- 2. nil, or string with error message
local r = string.format( "{| id='%s' class='%s' style='%s'",
Config.section,
"metadata rahmenfarbe1",
"border-style: solid; margin-top: 20px;" )
local r2, s, s2
for i = 1, 8 do
s, s2 = fill( Config.order[ i ] )
if s then
r = r .. s
if s2 then
if r2 then
r2 = string.format( "%s; %s", r2, s2 )
else
r2 = s2
end
end
end
end -- for i
fences()
r = r .. "\n|}"
s = Config.params.SORTIERUNG.s
if s ~= "" then
if not s and Config.params.NAME.s then
s, s2 = flat()
if s2 then
if r2 then
r2 = string.format( "%s; %s", r2, s2 )
else
r2 = s2
end
end
end
if s then
s = string.format( "{{SORTIERUNG:%s}}", s )
if Config.frame then
r = r .. Config.frame:preprocess( s )
if Lookup then
r = string.format( "%s\n<code>%s</code>", r, s )
end
else
r = string.format( "%s\n%s", r, s )
end
end
end
return r, r2
end -- furnish()
-- Export
local p = { }
p.main = function ( args )
-- Invocation
-- args -- table, with template parameters
-- Returns appropriate string
local screamArgs = features( args )
local r, screamVals = furnish()
if screamArgs or screamVals then
local s
if screamArgs and screamVals then
s = string.format( "%s; %s", screamArgs, screamVals )
elseif screamArgs then
s = screamArgs
else
s = screamVals
end
r = string.format( "%s\n%s", r, failure( s ) )
end
return r
end -- p.main()
p.f = function ( frame )
local lucky, r
Config.frame = frame
lucky, r = pcall( p.main, frame:getParent().args )
if not lucky then
r = failure( r )
end
return r
end -- p.f()
function p.failsafe()
return Personendaten.serial
end
p.Personendaten = function ()
return Personendaten
end -- p.Personendaten
return p