Modul:Vorlage:Anker
Vorlagenprogrammierung | Diskussionen | Lua | Unterseiten | |||
Modul | Deutsch | English
|
Modul: | Dokumentation |
Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus
local Anchors = { suite = "Anchors", serial = "2020-04-10", item = 79414611, globals = { Multilingual = 47541920, TemplUtl = 52364930 } } --[=[ Support Template:Anchor {{Anker}} ]=] local Failsafe = Anchors local GlobalMod = Anchors local Config = { badPattern = "[#'\"%[%]<>|]", globalPage = { "antispam-container", "bodyContent", "catlinks", "centralNotice", "content", "contentSub", "contentSub2", "editform", "editpage-copywarn", "firstHeading", "footer", "fundraising", "jump-to-nav", "language-settings-dialog", "languagesettings-panels", "page-actions", "siteNotice", "siteSub", "toc", "toctitle", "top", "wikiPreview" }, globalPatterns = { "^mw%-[%l%-]+$", "^ca%-[%l%-]+$", "^n%-[%l%-]+$", "^p%-%a%a[%a%-]+$", "^pt%-[%l%-]+$", "^wp%u" }, errCat = false, errClass = "error_fragment", errHide = true, errNS = false, errInvalid = { en = "Invalid:", de = "Ungültig:" }, errModule = { en = "Library module missing:", de = "Bibliotheksmodul fehlt:" }, errNoFragments = { en = "No fragments given", de = "Keine Bezeichner angegeben" }, errUnkown = { en = "Unkown parameter:", de = "Parameter unbekannt:" } } local foreignModule = function ( access, advanced, append, alt, alert ) -- Fetch global module -- Precondition: -- access -- string, with name of base module -- advanced -- true, for require(); else mw.loadData() -- append -- string, with subpage part, if any; or false -- alt -- number, of wikidata item of root; or false -- alert -- true, for throwing error on data problem -- Postcondition: -- Returns whatever, probably table -- 2020-01-01 local storage = access local finer = function () if append then storage = string.format( "%s/%s", storage, append ) end end local fun, lucky, r, suited if advanced then fun = require else fun = mw.loadData end GlobalMod.globalModules = GlobalMod.globalModules or { } suited = GlobalMod.globalModules[ access ] if not suited then finer() lucky, r = pcall( fun, "Module:" .. storage ) end if not lucky then if not suited and type( alt ) == "number" and alt > 0 then suited = string.format( "Q%d", alt ) suited = mw.wikibase.getSitelink( suited ) GlobalMod.globalModules[ access ] = suited or true end if type( suited ) == "string" then storage = suited finer() lucky, r = pcall( fun, storage ) end if not lucky and alert then error( "Missing or invalid page: " .. storage ) end end return r end -- foreignModule() local function factory( apply ) -- Localization of messages -- apply -- string, with message key -- Returns message text; at least English -- TODO: Might be extended by tabData and Multilingual local entry = Config[ apply ] local r if entry then -- TODO: page language r = entry[ mw.language.getContentLanguage():getCode() ] if not r then r = entry.en end else r = tostring( mw.html.create( "span" ) :addClass( "error" ) :wikitext( string.format( "????.%s.????", apply ) ) ) end return r end -- factory() local function faculty( adjust ) -- Test template arg for boolean -- adjust -- string or nil -- Returns boolean local r if type( adjust ) == "string" then r = mw.text.trim( adjust ) if r ~= "" and r ~= "0" then r = true end else r = adjust or false end return r end -- faculty() local function fair( assembly ) -- Create HTML code for anchors -- assembly -- sequence table, with identifiers -- Returns HTML string local collection = mw.html.create( "" ) local f = function ( a ) collection:node( mw.html.create( "span" ) :attr( "id", a ) ) end local s, seen, shift table.sort( assembly ) for i = 1, #assembly do s = assembly[ i ] if s ~= seen then if not s:match( "^[%w_]+$" ) then shift = mw.uri.encode( s, "WIKI" ) if shift ~= s then f( shift:gsub( "%%", "." ) ) end end f( s ) seen = s end end -- i = 1, #assembly return tostring( collection ) end -- fair() local function fault( alert, about ) -- Format message with class="error" or similar -- alert -- string, with message key -- about -- string, with explanation -- Returns message with markup local scope = Config.errClass local story = factory( alert ) local TemplUtl = foreignModule( "TemplUtl", true, false, Anchors.globals.TemplUtl ) local r, scope, style if type( TemplUtl ) == "table" and type( TemplUtl.TemplUtl ) == "function" then TemplUtl = TemplUtl.TemplUtl() else TemplUtl = false end if Config.self then story = string.format( "%s * %s", Config.self, story ) end if Config.errClasses then if scope then scope = string.format( "%s %s", scope, Config.errClasses ) else scope = Config.errClasses end end if about then story = string.format( "%s %s", story, about ) end if TemplUtl then r = TemplUtl.failure( story, not Config.errHide, scope, Config.frame ) else r = tostring( mw.html.create( "span" ) :addClass( scope ) :addClass( "error" ) :wikitext( story ) ) end if Config.errCat then if Config.errNS then local st = type( Config.errNS ) if st == "number" then st = { } table.insert( st, Config.errNS ) Config.errNS = st elseif st == "string" then Config.errNS = mw.text.split( Config.errNS, "%s+" ) for i = 1, #Config.errNS do Config.errNS[ i ] = tonumber( Config.errNS[ i ] ) end -- for i end if type( Config.errNS ) == "table" then local ns = mw.title.getCurrentTitle().namespace for i = 1, #Config.errNS do if Config.errNS[ i ] == ns then Config.errNS = false break -- for i end end -- for i end end if not Config.errNS then r = string.format( "%s[[Category:%s]]", r, Config.errCat ) end end return r end -- fault() local function features( adapt ) -- Set parameters on compliance if type( adapt ) == "table" then Config.limitMarkup = faculty( adapt.rejectMarkup ) Config.limitMW = faculty( adapt.rejectMW ) Config.limitXML = faculty( adapt.onlyXML ) Config.lightXML = faculty( adapt.softXML ) end end -- features() local function main( argsF, argsT ) -- Invocation -- argsF -- table, with #invoke parameters, or false -- argsT -- table, with template parameters -- Returns appropriate string local r if type( argsF ) == "table" then Config.errCat = argsF.errCat Config.errClasses = argsF.errClasses Config.errHide = faculty( argsF.errHide ) Config.errNS = argsF.errNS features( argsF ) if mw.site.server:find( "simpsonspedia.", 1, true ) then Config.errClasses = "Linkwartung" Config.errHide = true Config.errNS = 0 Config.limitMarkup = true Config.limitMW = false Config.limitXML = true Config.lightXML = true end end if type( argsT ) == "table" then local e, got, less, s, unknown for k, v in pairs( argsT ) do if type( k ) == "number" then v = mw.text.trim( v ) if v == "" then v = false end less = ( k < 0 ) k = false else less = k:match( "^[x%-][1-9]%d*" ) if less then k = false end end if k then unknown = unknown or { } table.insert( unknown, k ) elseif v then got = got or { } v = v:gsub( " +", "_" ) :gsub( "_+", "_" ) got[ v ] = less end end -- for k, v if unknown then e = mw.html.create( "code" ) :css( "white-space", "nowrap" ) :wikitext( table.concat( unknown, " " ) ) s = string.format( " in [[%s]]", Config.frame:getTitle() ) e:tag( "span" ) :wikitext( s ) r = fault( "errUnkown", tostring( e ) ) elseif got then local profile = { limitMarkup = false, limitMW = false, limitXML = false } local bad, checked, legal, o, s for k, v in pairs( got ) do if v then o = profile else o = false end s, legal = Anchors.feasible( k, o ) if s then checked = checked or { } table.insert( checked, s ) end if not legal then bad = bad or { } e = mw.html.create( "code" ) :css( "white-space", "nowrap" ) :wikitext( mw.text.encode( k ) ) table.insert( bad, tostring( e ) ) end end -- for k, v if checked then r = fair( checked ) end if bad then s = string.format( "%s in [[%s]]", table.concat( bad, ", " ), Config.frame:getParent():getTitle() ) r = string.format( "%s%s", r or "", fault( "errInvalid", s ) ) end else r = fault( "errNoFragments" ) end end return r or "" end -- main() Anchors.feasible = function ( analyse, allow ) -- Check ID for compliance -- Precondition: -- analyse -- string, with presumptive anchor -- allow -- optional table, with compliance criteria -- -- limitMarkup -- -- limitMW -- -- limitXML -- -- lightXML -- Postcondition: -- Returns -- 1. -- string -- with normalised anchor -- -- false -- if not appropriate -- 2. -- true, if fully compliant local r1 = false local r2 = false if type( analyse ) == "string" then local s = mw.text.trim( analyse ) if s ~= "" then local deny = { limitMarkup = Config.limitMarkup, limitMW = Config.limitMW, limitXML = Config.limitXML, lightXML = Config.lightXML } if type( allow ) == "table" then for k, v in pairs( deny ) do if type( allow[ k ] ) == "boolean" then deny[ k ] = allow[ k ] end end -- for k, v end if deny.limitMW then r1 = true else s = mw.text.unstripNoWiki( s ) s = mw.text.trim( s ) r1 = ( s ~= "" ) end if r1 and deny.limitMarkup and ( s:match( Config.badPattern ) or s:match( "&#%w+;" ) ) then r1 = false end if r1 then for k = 1, #Config.globalPage do if s == Config.globalPage[ k ] then r1 = false break -- k = 1, #Config.globalPage end end -- k = 1, #Config.globalPage if r1 then Config.badCtrl = Config.badCtrl or string.format( "[%c-%c]", 1, 31 ) r1 = not s:find( Config.badCtrl ) if r1 and s:match( "%%%x%x" ) then r1 = false end if r1 then for k = 1, #Config.globalPatterns do if s:match( Config.globalPatterns[ k ] ) then r1 = false break -- k = 1, #Config.globalPatterns end end -- k = 1, #Config.globalPatterns end end end if r1 then r2 = true if deny.limitXML and ( s:match( "^%d" ) or s:sub( 1, 1 ) == "-" ) then r2 = false if not deny.lightXML then r1 = false end end end if r1 then r1 = s:gsub( " +", "_" ) :gsub( "_+", "_" ) end end end return r1, r2 end -- Anchors.feasible() Failsafe.failsafe = function ( atleast ) -- Retrieve versioning and check for compliance -- Precondition: -- atleast -- string, with required version or "wikidata" or "~" -- or false -- Postcondition: -- Returns string -- with queried version, also if problem -- false -- if appropriate -- 2019-10-15 local last = ( atleast == "~" ) local since = atleast local r if last or since == "wikidata" then local item = Failsafe.item since = false if type( item ) == "number" and item > 0 then local entity = mw.wikibase.getEntity( string.format( "Q%d", item ) ) if type( entity ) == "table" then local seek = Failsafe.serialProperty or "P348" local vsn = entity:formatPropertyValues( seek ) if type( vsn ) == "table" and type( vsn.value ) == "string" and vsn.value ~= "" then if last and vsn.value == Failsafe.serial then r = false else r = vsn.value end end end end end if type( r ) == "nil" then if not since or since <= Failsafe.serial then r = Failsafe.serial else r = false end end return r end -- Failsafe.failsafe() -- Export local p = { } p.f = function ( frame ) local lucky, r Config.frame = frame lucky, r = pcall( main, frame.args, frame:getParent().args ) if not lucky then r = tostring( mw.html.create( "span" ) :addClass( "error" ) :wikitext( string.format( "%s * %s", frame:getTitle(), r ) ) ) end return r end -- p.f() p.feasible = function ( frame ) local r1, r2 Config.frame = frame features( frame.args ) r1, r2 = Anchors.feasible( frame.args[ 1 ] ) return r1 or "" end -- p.feasible() p.forbidden = function () local r = "" local sep = "" local e for k = 1, #Config.globalPage do e = mw.html.create( "code" ) :css( "white-space", "nowrap" ) :wikitext( Config.globalPage[ k ] ) r = string.format( "%s%s%s", r, sep, tostring( e ) ) sep = ", " end -- k = 1, #Config.globalPage return r end -- p.forbidden() p.failsafe = function ( frame ) -- Versioning interface local s = type( frame ) local since if s == "table" then since = frame.args[ 1 ] elseif s == "string" then since = frame end if since then since = mw.text.trim( since ) if since == "" then since = false end end return Failsafe.failsafe( since ) or "" end -- p.failsafe() p.Anchors = function () return Anchors end -- p.Anchors return p