Modul:GlobalSharing

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen
Vorlagenprogrammierung Diskussionen Lua Unterseiten
Modul Deutsch English

Modul: Dokumentation

Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus

Dies ist die (produktive) Mutterversion eines global benutzten Lua-Moduls.
Wenn die serial-Information nicht übereinstimmt, müsste eine Kopie hiervon in das lokale Wiki geschrieben werden.
Versionsbezeichnung auf WikiData: 2019-11-09

local GlobalSharing = { suite   = "GlobalSharing",
                        serial  = "2019-11-09",
                        item    = 76292536,
                        globals = { Multilingual   = 47541920,
                                    WikidataScheme = 74420405 } }
--[=[
Support for sharing things in the WMF world
]=]
local Failsafe  = GlobalSharing
local GlobalMod = GlobalSharing
local Config    = { ltr = true }
local Data      = { }
local Present   = { }
local Text



Config.spaces = { L = "Lexeme:",
                  P = "Property:",
                  Q = false }
Config.pages  = { User      = 2300,
                  Template  = 10,
                  Mediawiki = 2300,
                  Module    = 828,
                  Gadgets   = 2300 }
Config.sites  = { mediawiki   = true,
                  wikibooks   = true,
                  wikidata    = true,
                  wikimedia   = true,
                  wikinews    = true,
                  wikipedia   = true,
                  wikiquote   = true,
                  wikisource  = true,
                  wikiversity = true,
                  wikivoyage  = true,
                  wiktionary  = true }
Config.icons  =
    { npx      = 28,
      download = { source  = "OOjs UI icon download-ltr-progressive.svg",
                   support = "Update" },
      home     = { source  = "Noun 1580514 3333ff.svg",
                   support = "Upstream" },
      wikidata = { source  = "Wikidata-logo.svg",
                   support = "Wikidata Logo" }
    }
Config.css    = { ["="] = { ["background-color"] = "#00FF00",
                            color                = "#000000" },
                  [">"] = { ["background-color"] = "#FF0000",
                            color                = "#FFFF00" },
                  ["?"] = { ["background-color"] = "#FFFF00",
                            color                = "#000000" },
                  ["#"] = { display           = "inline-block",
                            ["font-size"]     = "120%",
                            ["font-weight"]   = "900",
                            ["margin-left"]   = "1em",
                            ["margin-right"]  = "1em",
                            ["padding-left"]  = "1em",
                            ["padding-right"] = "1em"
                          }
               }
Config.errors = { bag = false }
Data.resolve  = { source   = { P = 1324 },
                  host     = { P = 137 },
                  version  = { P = 348 },
                  licence  = { P = 275 },
                  usedby   = { P = 1535 },
                  uses     = { P = 2283 },
                  doc      = { P = 973 },
                  doclang  = { P = 407 },
                  iso6391  = { P = 218 },
                  iso6392  = { P = 219 },
                  tags     = { P = 366 },
                  linksite = { Q = 677652 },
                  upgrade  = { Q = 920419 },
                  upstream = { Q = 352213 },
                  wikidata = { Q = 2013 }
                }



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
    -- 2019-10-29
    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, 0 )
        end
    end
    return r
end -- foreignModule()



local failures = function ()
    -- Summarize all faults
    -- Postcondition:
    --     Returns  string, hopefully empty
    local r
    if Config.errors.bag then
        local max    = 1000000000
        local id     = math.floor( os.clock() * max )
        local show   = string.format( "%s * Errors",
                                      WikidataScheme.suite )
        local sign   = string.format( "error_%d", id )
        local errors = mw.html.create( "ul" )
                         :addClass( "error" )
        local e      = mw.html.create( "h2" )
                         :addClass( "error" )
                         :attr( "id", sign )
                         :wikitext( show )
        for i = 1, #Config.errors.bag do
            errors:newline()
                  :node( mw.html.create( "li" )
                           :addClass( "error" )
                           :wikitext( Config.errors.bag[ i ] ) )
        end -- for i
        r = string.format( "%s\n%s\n",
                           tostring( e ), tostring( errors ) )
        show = string.format( "[[#%s|%s]]", sign, WikidataScheme.suite )
        mw.addWarning( show )
    end
    Config.errors.bag = false
    return r or ""
end -- failures()



local fault = function ( alert )
    -- Format one error message in HTML
    -- Precondition:
    --     alert  -- string, plain error message
    -- Postcondition:
    --     Returns  string, HTML error message
    local e = mw.html.create( "span" )
                     :addClass( "error" )
                     :wikitext( alert )
    Config.errors.bag = Config.errors.bag  or  { }
    table.insert( Config.errors.bag,  alert or "??fault??" )
    return tostring( e )
end -- fault()



local first = function ( atleast, actual )
    -- Check whether local version is matched
    -- Precondition:
    --     atleast  -- string, with global version, or not
    --     actual   -- string, with local version, or not
    -- Postcondition:
    --     Returns
    --         "=", if equal
    --         ">", if update recommended
    --         "<", if local newer than global (may be upstream)
    --         "?", if strange data
    --         else unknown
    local r
    if type( atleast ) == "string"  and
       type( actual ) == "string" then
        local sg = mw.text.trim( atleast )
        local sl = mw.text.trim( actual )
        if sg == sl then
            r = "="
        elseif sg == ""  or  sl == "" then
            r = "?"
        elseif sg:match( "^[0-9.]+$" )  and
               sl:match( "^[0-9.]+$" ) then
            local f = function ( a )
                          if not a or a == "" then
                              return -1
                          end
                          return tonumber( a )
                      end
            local vg = mw.text.split( sg, "%.+" )
            local vl = mw.text.split( sl, "%.+" )
            local kg, kl
            r = "?"
            for i = 1, #vg do
                kg = f( vg[ i ] )
                kl = f( vl[ i ] )
                if kg < 0  or kl < 0 then
                    break -- for i
                elseif kg > kl then
                    r = ">"
                    break -- for i
                else
                    r = "="
                end
            end -- for i
            if r == "="  and  #vl > #vg then
                r = "?"
            end
        elseif sg > sl then
            r = ">"
        else
            r = "<"
        end
    end
    return r
end -- first()



local found = function ( at, area )
    -- Check whether local source code (copy) is present
    -- Precondition:
    --     at    -- string, with local page name
    --     area  -- number, of required namespace
    -- Postcondition:
    --     Returns
    --         title object, if appropriate
    --         string, if bad page name
    --         nil, if not found
    local t = mw.title.new( at )
    local r
    if t and t.exists then
        if t.namespace == area   or
           ( area == 10  and  t.namespace == 4 ) then
            r = t
        else
            r = at
        end
    end
    return r
end -- found()



Config.fetch = function ( access, at )
    -- Retrieve external module
    -- Precondition:
    --     access  -- string, with requested name
    --     at      -- table, for hosting
    if not at[ access ]  and  at[ access ] ~= false then
        local bib = foreignModule( access,
                                   true,
                                   false,
                                   GlobalSharing.globals[ access ],
                                   true )
        if type( bib ) == "table"  and
           type( bib[ access ] ) == "function" then
            at[ access ] = bib[ access ]()
        else
            at[ access ] = false
        end
    end
end -- Config.fetch()



Config.first = function ()
    -- Initialize generally
    Config.fetch( "Multilingual", Present )
    Config.fetch( "WikidataScheme", GlobalSharing )
    if GlobalSharing.WikidataScheme then
        Text = GlobalSharing.WikidataScheme.Text
        Text.flipper( GlobalSharing.suite )
    end
end -- Config.first()



Config.from = function ()
    -- Initialize current site
    -- Postcondition:
    --     Returns  string, with normalized site
    if not Config.server then
        local i
        Config.server = mw.site.server
        if Config.server:sub( 1, 2 ) == "//" then
            Config.server = "https:" .. Config.server
        end
        i = Config.server:find( "[/.]m%." )
        if i  and  i < 20 then
            Config.server = Config.server:sub( 1,  i + 1 )   ..
                          Config.server:sub( i + 4 )
        end
        if Config.server:sub( -1, 1 ) == "/" then
            Config.server = Config.server:sub( 1,  #Config.server - 1 )
        end
    end
    return Config.server
end -- Config.from()



Data.father = function ( all )
    -- Check whether source matches upstream condition
    -- Precondition:
    --     all  -- item object
    -- Postcondition:
    --     Returns  mw.html object, or not
    local source = Data.find( all, "source", 1 )
    local r
    if source then
        local start = Config.from() .. "/wiki/"
        if source:find( start ) == 1 then
            local i
            start = source:sub( #start + 1 )
            i = start:find( ":" )
            if i > 2 then
                local space = start:sub( 1,  i - 1 )
                if ( Config.pages[ space ] ) then
                    local t = mw.title.makeTitle( space,
                                                  start:sub( i + 1 ) )
                    if not Config.title then
                        Config.title = mw.title.getCurrentTitle()
                    end
                    r  = mw.title.equals( Config.title, t )
                end
            end
        end
    end
    return r
end -- Data.father()



Data.fetch = function ( access )
    -- Resolve entity name
    -- Precondition:
    --     access  -- string, with entity name or ID
    -- Postcondition:
    --     Returns  string or not, with entity ID
    local r
    if access:find( "^[PQL]%d+$" ) then
        r = access
    elseif type( Data.resolve[ access ] ) == "table" then
        local entry = Data.resolve[ access ]
        if type( entry ) == "table" then
            for k, v in pairs( Config.spaces ) do
                if type( entry[ k ] ) == "number" then
                    r = string.format( "%s%d", k, entry[ k ] )
                    break -- for k, v
                end
            end -- for k, v
        end
    end
    return r
end -- Data.fetch()



Data.final = function ( all )
    -- Retrieve upstream source code URL
    -- Precondition:
    --     all  -- item object
    -- Postcondition:
    --     Returns string, or not
    local t = Data.full( 0 )
    local r
    if t then
        r = all.id:match( "^Q(%d+)$" )
        r = t[ tonumber( r ) ]
        if type( r ) ~= "string"  or
           r:sub( 1, 8 ) ~= "https://" then
            r = false
        else
            local seek = "^https://[a-z_.]+%.(%l+)%.org/wiki/(%u%l+):"
            local site, space = r:match( seek )
            if Config.sites[ site ] then
                space = Config.pages[ space ]
                if space ~= 10  and  space ~= 828 then
                    r = false
                end
            else
                r = false
            end
        end
    end
    return r
end -- Data.final()



Data.find = function ( all, assign, at )
    -- Retrieve entity value
    -- Precondition:
    --     all     -- item object
    --     assign  -- string, with  entity name
    --     at      -- number, with value counter, or not
    -- Postcondition:
    --     Returns
    --         1.  value, or not
    --         2.  qualifiers, if any
    local r1, r2
    if type( assign ) == "string" then
        local k = at or 1
        local s = Data.fetch( assign )
        local d = all.claims
        if s  and
           type( d ) == "table"  and
           type( d[ s ] ) == "table"  and
           type( d[ s ][ k ] ) == "table" then
            d = d[ s ][ k ]
            if type( d.mainsnak ) == "table"  and
               type( d.mainsnak.datavalue ) == "table" then
                r1 = d.mainsnak.datavalue.value
                if type( d.qualifiers ) == "table" then
                    r2 = d.qualifiers
                end
            end
        end
    end
    return r1, r2
end -- Data.find()



Data.full = function ( area )
    -- Retrieve one Data.tab element
    -- Precondition:
    --     area    -- number, of collection; 0 for URL, 10, 828, 2300
    -- Returns: table, or not
    --     0    -- map, item -> URL
    --     else -- sequence of Q
    local r
    Data.collections = Data.collections  or  { }
    r = Data.collections[ area ]
    if type( r ) == "nil"  and  type( area ) == "number" then
        local storage = string.format( "%s/%d.tab",
                                       GlobalSharing.suite, area )
        local lucky, data = pcall( mw.ext.data.get, storage )
        r = false
        if type( data ) == "table"  and
           type( data.data ) == "table" then
            data = data.data
            if #data > 0 then
                local low = ( area ~= 0 )
                local dup = { }
                local entry, k
                r = { }
                for i = 1, #data do
                    entry = data[ i ]
                    if type( entry ) == "table" then
                        k = entry[ 1 ]
                        if low then
                            if not dup[ k ] then
                                table.insert( r, k )
                                dup[ k ] = true
                            end
                        else
                            r[ k ] = entry[ 2 ]
                        end
                    end
                end -- for i
                if type( k ) ~= "number"   or
                   ( low  and  type( entry[ 2 ] ) ~= "string" ) then
                    r = false
                end
            end
        end
        Data.collections[ area ] = r
    end
    return r
end -- Data.full()



Present.facet = function ( access )
    -- Create decorated Wikidata link
    -- Precondition:
    --     access  -- string, with item ID
    -- Postcondition:
    --     Returns  mw.html object
    local r = mw.html.create( "span" )
    local s = string.format( "[[d:%s|%s]]", access, access )
    r:node( Present.flag( "wikidata" ) )
     :newline()
     :node( mw.html.create( "code" )
                   :attr( "dir", "ltr" )
                   :attr( "lang", "zxx" )
                   :wikitext( s ) )
    return r
end -- Present.facet()



Present.failsafe = function ( about, atleast )
    -- Check local version
    -- Precondition:
    --     about    -- mw.title object
    --     atleast  -- string, with global version
    -- Postcondition:
    --     Returns  mw.html object, or not
    local r
    if about.namespace == 828  and  type( atleast ) == "string" then
        local lucky, x = pcall( require, about.prefixedText  )
        if type( x ) == "table"  and  x.failsafe then
            local s = type( x.failsafe )
            if s == "function" then
                s = x.failsafe()
            elseif s == "string" then
                s = x.failsafe
            else
                s = false
            end
            if s then
                r = first( atleast, s )
                if r then
                    local e, css
                    if r == ">" or r == "<" then
                        s = mw.text.nowiki( tostring( s ) )
                        e = mw.html.create( "code" )
                                   :wikitext( s )
                        if r == "<" then
                            r = "?"
                        end
                    end
                    css = Config.css[ r ]
                    r = mw.html.create( "span" )
                               :css( Config.css[ "#" ] )
                               :wikitext( mw.text.nowiki( r ) )
                    if css then
                        r:css( css )
                    end
                    if e then
                        r = mw.html.create( "span" )
                                   :node( r )
                                   :node( e )
                    end
                end
            end
        end
    end
    return r
end -- Present.failsafe()



Present.father = function ()
    -- Create decorated upstream indicator
    -- Postcondition:
    --     Returns  mw.html object
    if not Present.upstream then
        local e = mw.html.create( "span" )
                         :attr( { dir   = "ltr",
                                  lang  = "en",
                                  title = "Upstream" } )
                         :css( { ["background-color"] = "#B0FFB0" } )
        local s = "Upstream"
        if Data.resolve.upstream   and
           Data.resolve.upstream.Q then
           local sq = string.format( "Q%d", Data.resolve.upstream.Q )
           local sl = mw.wikibase.getSitelink( sq )
           if not sl then
               sl = mw.wikibase.getSitelink( sq, "enwiki" )
               if sl then
                   sl = "w:en:" .. sl
               end
           end
           if sl then
               s = string.format( "[[%s|%s]]", sl, s )
           end
        end
        e:wikitext( s )
        Present.upstream = mw.html.create( "span" )
                                  :node( Present.flag( "home" ) )
                                  :newline()
                                  :node( e )
    end
    return Present.upstream
end -- Present.father()



Present.features = function ( access, adjust )
    -- Description term etc. of one item
    -- Precondition:
    --     access  -- number, of item
    --     adjust  -- table, with options
    --                .locally    -- local sources only
    --                .namespace  -- number, of repo resource
    --                .wikidata   -- show link to item
    --                .linksite   -- show link to page
    --                .source     -- show link to local source
    --                .host       -- show link to global source host
    -- Postcondition:
    --     Returns
    --         1.  table, with sequence of mw.html objects DT, DD, DD, DD
    --             or not, if unavailable local source queried
    --         2.  string, with sort key
    local s = string.format( "Q%d", access )
    local e, q, r1, r2, slang, source
    if mw.wikibase.isValidEntityId( s )  and
       mw.wikibase.entityExists( s ) then
        q = mw.wikibase.getEntity( s )
    end
    if q and adjust.locally then
        source = Present.fresh( q, adjust.namespace )
        if not source then
            q = false
        end
    end
    s = type( q )
    if s == "table" then
        s, slang = Present.wikibase( q, false )
        if s then
            r2 = s:lower()
            if adjust.linksite then
                if q.sitelinks then
                    Config.site = Config.site  or
                                  mw.wikibase.getGlobalSiteId()
                    e = q.sitelinks[ Config.site ]
                    if e and e.title then
                        s = string.format( "[[%s|%s]]", e.title, s )
                    end
                end
            else
                s = Present.find( q, s )
            end
            e = Text.html( s, "dt", false, slang )
        else
            e = mw.html.create( "dt" )
                       :node( mw.html.create( "code" )
                                     :attr( "dir", "ltr" )
                                     :attr( "lang", "zxx" )
                                     :wikitext( string.format( "Q%d",
                                                               access )
                                              ) )
        end
    elseif s == "nil" then
        e = mw.html.create( "dt" )
                   :wikitext( fault( Text.flip( "err_ItemUnknown" ) ) )
                   :node( mw.html.create( "code" )
                                 :attr( "dir", "ltr" )
                                 :attr( "lang", "und" )
                                 :css( "margin-left",  "1em" )
                                 :css( "margin-right", "1em" )
                                 :wikitext( string.format( "Q%d",
                                                           access ) ) )
    end
    if e then
        r1 = { }
        table.insert( r1, e )
        if not r2 then
            r2 = string.format( "#%d", access )
        end
    end
    if q then
        local f = function ( add, alien, attr )
                      e = Text.html( tostring( add ),
                                     "dd",
                                     false,
                                     alien )
                      if attr then
                          e:attr( attr )
                      end
                      table.insert( r1, e )
                  end
        s, slang = Present.wikibase( q, true )
        if s then
            f( s, slang )
        end
        if adjust.wikidata then
            f( Present.facet( q.id ) )
        end
        if adjust.source then
            Present.from( q, adjust, f )
        end
        if adjust.version  or  adjust.locally then
            s = Data.find( q, "version", 1 )
            if s then
                e = mw.html.create( "code" )
                           :attr( "dir", "ltr" )
                           :attr( "lang", "zxx" )
                           :wikitext( s )
                if type( source ) == "table" then
                    s = Present.failsafe( source, s )
                    if s then
                        e:newline()
                         :node( s )
                    end
                end
                f( e,  "zxx",  { dir = "ltr" }  )
            end
        end
    end
    return r1, r2
end -- Present.features()



Present.final = function ( all, apply )
    -- Present link to validated upstream source URL
    -- Precondition:
    --     all    -- item object
    --     apply  -- string, with protected source URL, or not
    -- Postcondition:
    --     Returns  mw.html object, or not
    local r
    if type( apply ) == "string" then
        local source, q = Data.find( all, "source", 1 )
        if source == apply then
            local store = string.format( "Q%d", Data.resolve.upgrade.Q )
            local show  = mw.wikibase.getSitelink( store )
            local slang
            if show then
                if not Config.slang then
                    Config.slang = mw.language.getContentLanguage()
                                              :getCode()
                                              :lower()
                end
                slang = Config.slang
            else
                store = Data.fetch( "host" )
                if q[ store ] then
                    q = q[ store ]
                    if q[ 1 ] then
                        store = q[ 1 ].datavalue.value.id
                        show  = Present.wikibase( store )
                    end
                end
            end
            show = show or "Upgrade"
            show = string.format( "[%s %s]", apply, show )
            r    = Text.html( show, "span", false, slang )
            r    = mw.html.create( "span" )
                          :node( Present.flag( "download" ) )
                          :newline()
                          :node( r )
        end
    end
    return r
end -- Present.final()



Present.find = function ( all, appear )
    -- Try to create doc URL link
    -- Precondition:
    --     all     -- item object
    --     appear  -- link title
    -- Postcondition:
    --     Returns  string
    local r   = appear
    local i   = 1
    local url = true
    local collection, q, s, s1, s2, slang, store, t
    while url do
        url, q = Data.find( all, "doc", i )
        if url then
            collection = collection  or  { }
            slang      = false
            if q then
                store = store  or  Data.fetch( "doclang" )
                if q[ store ] then
                    q = q[ store ]
                    for k = 1, #q do
                        t = q[ k ].datavalue.value
                        if t and t.id then
                            s  = t.id
                            s1 = s1  or  Data.fetch( "iso6391" )
                            t  = mw.wikibase.getBestStatements( s, s1 )
                            if t[ 1 ] then
                                slang = t[ 1 ].mainsnak.datavalue.value
                            else
                                s2 = s2  or  Data.fetch( "iso6392" )
                                t  = mw.wikibase.getBestStatements( s,
                                                                    s2 )
                                slang = t[ 1 ].mainsnak.datavalue.value
                            end
                        end
                    end -- k = 1, #q
                end
            end
            slang = slang  or  "und"
            if not collection[ slang ] then
                collection[ slang ] = url
            end
        end
        i = i + 1
    end    -- while q
    if collection then
        s = Text.find( collection )
       if s then
           r = string.format( "[%s %s]", s, r )
       end
    end
    return r
end -- Present.find()



Present.flag = function ( ask )
    -- Create icon
    -- Postcondition:
    --     Returns  mw.html object
    if not Present[ ask ] then
        local c = Config.icons[ ask ]
        local e = mw.html.create( "span" )
                         :attr( { ["aria-hidden"] = "true",
                                  role            = "presentation" } )
        local s = string.format( "[[File:%s|%dpx|%s|%s]]",
                                 c.source,
                                 Config.icons.npx,
                                 "class=noviewer",
                                 c.support )
        e:wikitext( s )
        if Config.ltr then
            s = "right"
        else
            s = "left"
        end
        e:css( "margin-" .. s,  "1em" )
        Present[ ask ] = e
    end
    return Present[ ask ]
end -- Present.flag()



Present.focus = function ( all )
    -- Present one single item object
    -- Precondition:
    --     all  -- item object
    -- Postcondition:
    --     Returns  mw.html object
    local r
    if all  and  all.id then
        local f = function ( add )
                      r:node( mw.html.create( "br" ) )
                       :newline()
                       :node( add )
                  end
        local s, slang
        r = mw.html.create( "div" )
                   :addClass( "plainlinks" )
                   :newline()
                   :node( Present.facet( all.id ) )
        s, slang = Present.wikibase( all, false )
        if s then
            s = Present.find( all, s )
            f( Text.html( s, "span", false, slang ) )
        end
        s, slang = Present.wikibase( all, true )
        if s then
            f( Text.html( s, "span", false, slang ) )
        end
        s = Data.find( all, "version", 1 )
        if s then
            f( mw.html.create( "code" )
                      :attr( "dir", "ltr" )
                      :wikitext( s ) )
        end
        if Data.father( all ) then
            f( Present.father() )
        else
            s = Present.final( all,  Data.final( all ) )
            if s then
                f( s )
            end
        end
        r:newline()
    end
    return r
end -- Present.focus()



Present.fresh = function ( all, area )
    -- Check whether local source code (copy) is present
    -- Precondition:
    --     all   -- item object
    --     area  -- number, of namespace
    -- Postcondition:
    --     Returns
    --         title object, if appropriate
    --         string, if error message
    --         nil, if not found
    local r, s
    if all.sitelinks then
        local wiki
        Config.site = Config.site  or  mw.wikibase.getGlobalSiteId()
        wiki        = all.sitelinks[ Config.site ]
        if wiki and wiki.title then
            r = found( wiki.title, area )
            if type( r ) == "string" then
                s = "err_SourceSitelink"
            end
        end
    end
    if not r then
        r = Data.find( all, "source", 1 )
        if r then
            r = r:match( "^https://[^/]+/wiki/(.+:.+)$" )
            if r then
                r = found( r, area )
                s = type( r )
                if s == "string" then
                    s = "err_SourceWikidataBad"
                elseif s == "table" then
                    s = false
                else
                    s = "err_SourceUnregistered"
                end
            else
                s = "err_SourceWikidataInvalid"
            end
        end
    end
    if s then
        s = fault( Text.flip( s ) )
        if type( r ) == "string" then
            local e = mw.html.create( "em" )
                             :attr( "dir", "ltr" )
                             :attr( "lang", "zxx" )
                             :wikitext( r:gsub( "//", "&#47;&#47;" ) )
            r = string.format( "%s %s",  s,  tostring( e ) )
        else
            r = s
        end
    end
    return r
end -- Present.fresh()



Present.from = function ( all, adjust, apply )
    -- Present link to validated upstream source URL
    -- Precondition:
    --     all     -- item object
    --     adjust  -- table, with options
    --                .host  -- show info about global source host
    --     apply   -- function, to drop result lines
    local source, q = Data.find( all, "source", 1 )
    if type( source ) == "string"   then
        local s = Data.final( all )
        if s == source then
            local seek = "^https://([^/ ]+%.org)/wiki/(%S*)$"
            local e, site
            site, s = source:match( seek )
            if s then
                local f = function ()
                              return mw.html.create( "span" )
                                        :css( { display = "inline-block",
                                                width   = "2em" } )
                                        :wikitext( "&#160;" )
                          end
                s = string.format( "[%s %s]", source, s )
                e = mw.html.create( "span" )
                           :attr( "dir", "ltr" )
                           :attr( "lang", "zxx" )
                           :wikitext( s )
                if source:find( Config.from() .. "/wiki/" ) == 1 then
                    e:newline()
                     :node( f() )
                     :newline()
                     :node( Present.father() )
                    q = false
                end
                apply( e )
                if q and adjust.host then
                    local ex, slang
                    s = string.format( "[https://%s/ %s]", site, site )
                    e = mw.html.create( "em" )
                               :attr( "dir", "ltr" )
                               :attr( "lang", "zxx" )
                               :wikitext( s )
                    s = Data.fetch( "host" )
                    if q[ s ] then
                        q = q[ s ]
                        if q[ 1 ] then
                            ex = q[ 1 ].datavalue.value.id
                            s, slang = Present.wikibase( ex, true )
                            if s then
                                e = mw.html.create( "span" )
                                           :node( e )
                                           :newline()
                                           :node( f() )
                                           :newline()
                                           :node( Text.html( s,
                                                             "span",
                                                             false,
                                                             slang ) )
                            end
                        end
                    end
                    apply( e )
                end
            end
        end
    elseif s then
        apply( fault( Text.flip( "err_SourceUnregistered" ) ) )
    end
end -- Present.from()



Present.full = function ( array, adjust )
    -- Description list of entire area
    -- Precondition:
    --     array   -- table, with non-empty sequence of item numbers
    --     adjust  -- table, with options
    -- Postcondition:
    --     Returns  mw.html object, or not
    local entries = { }
    local order   = { }
    local entry, r, sort
    for i = 1, #array do
        entry, sort = Present.features( array[ i ], adjust )
        if entry then
            entries[ sort ] = entry
            table.insert( order, sort )
        end
    end -- for i
    if #order then
        r = mw.html.create( "dl" )
                   :addClass( "plainlinks" )
        if not adjust.lazy then
            table.sort( order )
        end
        for i = 1, #order do
            entry = entries[ order[ i ] ]
            for k = 1, #entry do
                r:newline()
                 :node( entry[ k ] )
            end -- for k
        end -- for i
        Text.fashion( r, adjust )
        Text.flow( r )
        if type( adjust.id ) == "string" then
            r:attr( "id", adjust.id )
        end
        r:newline()
    end
    return r
end -- Present.full()



Present.wikibase = function ( all, about, attempt )
    -- Translation of wikibase component
    -- Precondition:
    --     all      -- string or table, object ID or entity
    --     about    -- boolean, true "descriptions" or false "labels"
    --     attempt  -- string or not, code of preferred language
    -- Postcondition:
    --     Returns
    --         1. string, with selected message, or not
    --         2. string, with language code, or not
    local r1, r2
    if Present.Multilingual then
        r1, r2 = Present.Multilingual.wikibase( all,
                                                about,
                                                attempt,
                                                Config.frame )
    else
        local s = type( all )
        local object
        if s == "table" then
            object = all
        elseif s == "string" then
            if mw.wikibase.isValidEntityId( all )  and
               mw.wikibase.entityExists( all ) then
                object = mw.wikibase.getEntity( all )
            end
            r1 = all
        end
        if type( object ) == "table" then
            if about then
                s = "descriptions"
            else
                s = "labels"
            end
            object = object[ s ]
            if type( object ) == "table" then
                if object[ attempt ] then
                    r1 = object[ attempt ].value
                    r2 = attempt
                elseif object.en then
                    r1 = object.en.value
                    r2 = "en"
                else
                    local poly
                    for k, v in pairs( object ) do
                        r1 = v.value
                        r2 = k
                        break -- for k, v
                    end -- for k, v
                end
            end
        end
    end
    return r1 or "????????", r2
end -- Present.wikibase()



local Collection = function ( action, area, add )
    -- Present all registered shared things in one area
    -- Precondition:
    --     action  -- false->"full" or true->"fresh"
    --     area    -- number, nonzero, of area
    --     add     -- table, with options, or not
    -- Postcondition:
    --     Returns  string, HTML code
    local r
    Config.first()
    if type( area ) == "number"  and  area ~= 0 then
        local q
        if type( add ) == "table"  and
           type( add.items ) == "string" then
            q = mw.text.split( add.items, "%s+" )
            for i = 1, #q do
                q[ i ] = tonumber( q[ i ] )
            end -- for i
        else
            q = Data.full( area )
        end
        if q then
            local o = { }
            if type( add ) == "table" then
                for k, v in pairs( add ) do
                    if v ~= "0"  and  v ~= "-" then
                        o[ k ] = v
                    end
                end -- for k, v
            end
            if action then
                o.linksite = true
                if area == 10  or  area == 828 then
                    o.locally = true
                end
            elseif area == 4 then
                o.lazy = true
                o.linksite = true
                o.locally = true
            end
            o.namespace = area
            if o.version or o.source then
                o.wikidata = true
            end
            r = Present.full( q, o )
            if r then
                r = tostring( r )
            end
        else
            r = fault( Text.flip( "err_CollectionUnknown" ) )
        end
    else
        r = fault( Text.flip( "err_CollectionInvalid" ) )
    end
    return r or ""
end -- Collection()



GlobalSharing.fetch = function ( access )
    -- Retrieve item object
    -- Precondition:
    --     access  -- string, or number, of item
    -- Postcondition:
    --     Returns  table   -- with item object
    --              string  -- with missing item ID
    --              false   -- if not appropriate
    local s = type( access )
    local r
    if s == "number"  and
       access > 0  and
       math.floor( access ) == access then
        r = string.format( "Q%d", access )
    elseif s == "string"  and
        access:match( "^Q[1-9]%d*$" ) then
        r = access
    end
    if r  and
       mw.wikibase.isValidEntityId( r )  and
       mw.wikibase.entityExists( r ) then
        r = mw.wikibase.getEntity( r )
    end
    return r or false
end -- GlobalSharing.fetch()



GlobalSharing.focus = function ( access )
    -- Present one single item object
    -- Precondition:
    --     access  -- string, or number, of item
    -- Postcondition:
    --     Returns  table  -- with item object
    --              false  -- if not appropriate
    local q = GlobalSharing.fetch( access )
    local r
    if type( q ) == "table" then
        Config.first()
        r = Present.focus( q )
    end
    return r or false
end -- GlobalSharing.focus()



GlobalSharing.fresh = function ( area, add )
    -- Present state of local pages of registered transcludes
    -- Precondition:
    --     area  -- number, nonzero, of area
    --     add   -- table, with options, or not
    -- Postcondition:
    --     Returns  string, HTML code
    return Collection( true, area, add )
end -- GlobalSharing.fresh()



GlobalSharing.full = function ( area, add )
    -- Present all registered shared things in one area
    -- Precondition:
    --     area  -- number, nonzero, of area
    --     add   -- table, with options, or not
    -- Postcondition:
    --     Returns  string, HTML code
    return Collection( false, area, add )
end -- GlobalSharing.full()



local Template = function ( action, frame )
    local params = { }
    local f = function ( a )
                  for k, v in pairs( a.args ) do
                      if type( v ) == "string" then
                          v = mw.text.trim( v )
                          if v ~= ""  and v ~= "-" then
                              if type( k ) == "number" then
                                  k = tostring( k )
                              end
                              params[ k ] = v
                          end
                      end
                  end -- for k, v
              end
    local lucky, plus, r, s
    Config.frame = frame
    f( frame )
    f( frame:getParent() )
    s = params[ "1" ]
    if action == "focus" then
        if s then
            if s:match( "^Q[1-9]%d*$" ) then
            elseif s:match( "^[1-9]%d*$" ) then
                s = "Q" .. s
            else
                s = false
            end
        else
            s = mw.wikibase.getEntityIdForCurrentPage()
        end
        lucky = s
    elseif s  and  s:match( "^[1-9]%d*$" ) then
        s = tonumber( s )
        lucky = s
        if action == "full" then
            local i, sp
            for k, v in pairs( params ) do
                if Data.resolve[ k ] then
                    sp = k
                else
                    i = k:match( "^P?([1-9]%d+)$" )
                    if i then
                        i = tonumber( i )
                        for kk, vv in pairs( Data.resolve ) do
                            if vv.P == i  or  vv.Q == i then
                                sp = kk
                                break -- for kk, vv
                            end
                        end -- for kk, vv
                    end
                end
                if sp then
                    plus = plus or { }
                    if v ~= "0" then
                        plus[ sp ] = true
                    end
                    sp = false
                end
            end -- for k, v
            -- version=
            -- P348=
            -- 348=
        end
    end
    if lucky then
        lucky, r = pcall( GlobalSharing[ action ], s, plus )
        if type( r ) == "table" then
            r = tostring( r )
        end
        if r then
            s = params[ "wrap" ]
            if s and s:find( "$1", 1, true ) then
                r = s:gsub( "$1", r )
            end
        end
    end
    return r or ""
end -- Template()



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.focus = function ( frame )
    return Template( "focus", frame )
end -- p.focus

p.fresh = function ( frame )
    return Template( "fresh", frame )
end -- p.fresh

p.full = function ( frame )
    return Template( "full", frame )
end -- p.full

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.GlobalSharing = function ()
    return GlobalSharing
end -- p.GlobalSharing

return p