Modul:Vorlage:Phab

Version vom 8. Dezember 2014, 13:36 Uhr von wp>Mabschaaf (Schützte „Modul:Vorlage:Phab“: Häufig eingebundenes Modul ([Bearbeiten=Nur angemeldete, nicht neue Benutzer] (unbeschränkt) [Verschieben=Nur Administratoren] (unbeschränkt)))

Modul:Vorlage:LuaModuleDoc:142: attempt to index field 'wikibase' (a nil value)


--[=[ 2013-12-01
{{Template:Phab}}
]=]



local function concatKeys( args )
    -- Concatenate keys
    -- Precondition:
    --     args   -- table; assignments
    local r
    for k in pairs( args ) do
        if r then
            r = r .. " "
        else
            r = ""
        end
        r = r .. tostring( k )
    end -- for k
    return r
end -- concatKeys()



local function file( args )
    -- Link to a File
    -- Precondition:
    --     args   -- table; assignments
    -- Postcondition:
    --     Returns string with bracketed external link, if fine
    --     Throws error on failure
    local r = args.Task
    local j = r:find( ",", 1, true )
    local n, params, show
    if j then
        params = r:sub( j + 1 )
        r      = r:sub( 1,  j - 1 )
        -- params = mw.text.split( split, "%s*,%s*" )
        -- , width=, height=, size=
        -- , layout, float, alt
    end
    n = r:match( "^[Ff]?([0-9]+)%s*$" )
    if n then
        n = tonumber( n )   -- discard leading zeros
    else
        r = string.format( "Invalid format: 'File=%s'", r )
        error( r, 0 )
    end
    r = string.format( "https://phabricator.wikimedia.org/F%d", n )
    if args.Show then
        show = args.Show
    else
        show = string.format( "phab:F%d", n )
    end
    r = string.format( "[%s %s]", r, show )
    return r
end -- file()



local function git( action, assembly, adopt, attach, anchor, against )
    -- Create URL for mediawiki GIT since 2013-06-06
    -- Precondition:
    --     action    -- string; kind of request
    --     assembly  -- string; project
    --     adopt     -- string; branch
    --     attach    -- string; file or directory (or empty)
    --     anchor    -- string; line number (or empty)
    --     against   -- string; diff ./. previous ID (or empty)
    -- Postcondition:
    --     Return URL
    -- Uses:
    --     mw.uri.encode()
    local r = "https://git.wikimedia.org/"
    if action == "file" then
        r = r .. "blob"
        if against then
            r = r .. "diff"
        end
    elseif action == "dir" then
        r = r .. "tree"
    elseif action == "plain" then
        r = r .. "raw"
    elseif action == "commit" or
           action == "commitdiff" or
           action == "history" then
        r = r .. action
    end
    r = r .. "/" .. mw.uri.encode( assembly ) .. "/" .. adopt
    if attach then
        r = r .. "/" .. mw.uri.encode( attach )
    end
    if anchor then
        r = r .. "#L" .. anchor
    end
    return r
end -- git()



local function id40( ask )
    -- Check for 40 hex lowercase id
    -- Precondition:
    --     ask  -- string; project
    -- Postcondition:
    --     Throws error, if failed
    if #ask ~= 40 or ask:match( "^[0-9a-f]+$" ) ~= ask then
        local e = "Invalid identifier: " .. ask
        error( e,  0 )
    end
end -- id40()



local function intersect( args, alone )
    -- Find elements occurring in both args and alone
    -- Precondition:
    --     args   -- table; assignments
    --     alone  -- table (sequence)
    -- Postcondition:
    --     Return table with sequence of matching elements, or false
    local r = { }
    local i, k, v
    for k, v in pairs( args ) do
        for i = 1, #alone do
            if k == alone[ i ] then
                table.insert( r, k )
            end
        end -- for i
    end -- for k, v
    if #r == 0 then
        r = false
    end
    return r
end -- intersect()



local function lonely( args, alone )
    -- Find elements occurring in both args and alone
    -- Precondition:
    --     args   -- table; assignments
    --     alone  -- table (sequence)
    -- Postcondition:
    --     Throws error, if occurring in both
    --     Return single matching element, or false
    -- Uses:
    --     intersect()
    --     mw.text.listToText()
    local r = intersect( args, alone )
    if r then
        if #r > 1 then
            r = "Must not be used together: ''"
                .. mw.text.listToText( r,  ", ",  " " ) .. "''"
            error( r, 0 )
        end
        r = r[ 1 ]
    end
    return r
end -- lonely()



local function mock( args )
    -- Link to a Mock
    -- Precondition:
    --     args   -- table; assignments
    -- Postcondition:
    --     Returns string with bracketed external link, if fine
    --     Throws error on failure
    local r = args.Task
    local n = r:match( "^[Mm]?([0-9]+)$" )
    local show
    if n then
        n = tonumber( n )   -- discard leading zeros
    else
        r = string.format( "Invalid format: 'Mock=%s'", r )
        error( r, 0 )
    end
    r = string.format( "https://phabricator.wikimedia.org/M%d", n )
    if args.Show then
        show = args.Show
    else
        show = string.format( "phab:M%d", n )
    end
    r = string.format( "[%s %s]", r, show )
    return r
end -- mock()



local function sourcing( args )
    -- Link to a code source, directory, branch or related
    -- Precondition:
    --     args   -- table; assignments
    -- Postcondition:
    --     Returns string, if fine
    --     Throws error on failure
    -- Uses:
    --     lonely()
    --     id40()
    --     mw.text.trim()
    --     git()
    local jump   = false
    local last   = false
    local load
    local scope  = "mediawiki/core"
    local show
    local since  = false
    local slot   = "HEAD"
    local source = false
    local swift  = "dir"
    local k, v, r
    r = lonely( args,
                { "branch", "commit", "commitdiff", "diff" } )
    if r then
        slot = args[ r ]
    end
    r = lonely( args,
                { "commit", "commitdiff", "dir", "file", "plain" } )
    if r then
        swift = r
    end
    if args.history then
        last = true
    end
    if args.project then
        scope = args.project
    end
    if args.line or args.diff then
        if not args.file then
            r = " valid for ''file'' only."
            if args.line then
                r = "Number ''line''" .. r
                if args.diff then
                    r = " / " .. r
                end
            end
            if args.diff then
                r = "Version ''diff''" .. r
            end
            error( r, 0 )
        end
        if args.line then
            if args.diff then
                r = "No ''line'' on ''diff'' page available"
                error( r, 0 )
            end
            jump = args.line
        end
    end
    load = ( args.commit or args.commitdiff )
    if load then
        id40( slot )
    elseif args.diff then
        since = args.diff
        id40( since )
    else
        source = args[ swift ]
        if last then
            swift = "history"
        end
    end
    if args.title then
        show = args.title
    elseif last then
        show = "history: " .. source
    elseif source then
        show = source
        if args.diff then
            show = show .. " ./." .. since:sub( 1, 7 )
        end
    elseif load then
        show = "GIT:" .. slot:sub( 1, 7 )
    else
        show = scope .. "/*"
    end
    r = git( swift, scope, slot, source, jump, since )
    return string.format( "[%s %s]", r, show )
end -- sourcing()



local function task( args )
    -- Link to a Task
    -- Precondition:
    --     args   -- table; assignments
    -- Postcondition:
    --     Returns string with bracketed external link, if fine
    --     Throws error on failure
    local r = args.Task
    local j = r:find( "#", 1, true )
    local n, show
    if j then
        local scroll = r:sub( j + 1 )
        r      = r:sub( 1,  j - 1 )
        scroll = scroll:match( "^([0-9]+)$" )
        if scroll  and  not args.Anchor then
            args.Anchor = scroll
        end
    end
    n = r:match( "^[Tt]?([0-9]+)%s*$" )
    if n then
        n = tonumber( n )   -- discard leading zeros
    else
        r = string.format( "Invalid format: 'Task=%s'", r )
        error( r, 0 )
    end
    r = string.format( "https://phabricator.wikimedia.org/T%d", n )
    if args.Anchor then
        r = string.format( "%s#anchor-%s", r, args.Anchor )
    end
    if args.Show then
        show = args.Show
    else
        show = string.format( "phab:T%d", n )
        if args.Anchor then
            show = string.format( "%s #%s", show, args.Anchor )
        end
    end
    r = string.format( "[%s %s]", r, show )
    return r
end -- task()



local function taskBugzilla( args )
    -- Link to an old Bugzilla ticket
    -- Precondition:
    --     args   -- table; assignments
    -- Postcondition:
    --     Returns string, if fine
    --     Throws error on failure
    -- Uses:
    --     task()
    local r = args.Bugzilla
    local j = r:find( "#", 1, true )
    local item, n, show
    if j then
        local scroll = r:sub( j + 1 )
        r      = r:sub( 1,  j - 1 )
        scroll = scroll:match( "^[cC]?([0-9]+)$" )
        if scroll then
            item = tonumber( scroll )
        end
    end
    n = r:match( "^([0-9]+)%s*$" )
    if n then
        n = tonumber( n )   -- discard leading zeros
        if n < 100000 then
            args.Task = tostring( n + 2000 )
        else
            n = false
        end
    end
    if not n then
        r = string.format( "Invalid number: 'Bugzilla=%s'", r )
        error( r, 0 )
    end
    r = string.format( "https://old-bugzilla.wikimedia.org/%s%d",
                       "show_bug.cgi?id=", n )
    show = string.format( "Bugzilla:%d", n )
    if item then
        r    = string.format( "%s#c%d", r, item )
        show = string.format( "%s&nbsp;#c%d", show, item )
    end
    r = string.format( "[%s %s]", r, show )
    r = string.format( "%s %s", task( args ), r )
    return r
end -- taskBugzilla()



local function unified( args )
    -- Link to a management issue
    -- Precondition:
    --     args   -- table; assignments
    -- Postcondition:
    --     Returns string, if fine
    --     Throws error on failure
    -- Uses:
    --     lonely()
    --     taskBugzilla()
    --     task()
    local r
    lonely( args,
            { "Bugzilla", "Countdown", "Differential", "File", "Gerrit",
              "Join", "Mock", "Paste", "Review", "Task" } )
    if args.Bugzilla then
        r = taskBugzilla( args )
    elseif args.File then
        r = file( args )
    elseif args.Mock then
        r = mock( args )
    elseif args.Task then
        r = task( args )
    else
        r = "NOT YET READY"
        error( r, 0 )
    end
    --    Countdown
    --    Differential
    --    Gerrit
    --    Join
    --    Paste
    --    Review
    return r
end -- unified()



local function main( args )
    -- Do the job
    -- Precondition:
    --     args   -- table; assignments
    -- Postcondition:
    --     Returns string with link, if fine
    --     Throws error on failure
    -- Uses:
    --     unified()
    --     sourcing()
    local got    = { }
    local mode   = 0
    local params = { Anchor       = 1,
                     Bugzilla     = 1,
                     Countdown    = 1,
                     Differential = 1,
                     File         = 1,
          --         Gerrit       = 1,
                     Join         = 1,
                     Mock         = 1,
                     Paste        = 1,
                     Review       = 1,
                     Show         = 1,
                     Task         = 1,
                     branch     = 2,
                     commit     = 2,
                     commitdiff = 2,
                     diff       = 2,
                     dir        = 2,
                     file       = 2,
                     history    = 2,
                     line       = 2,
                     plain      = 2,
                     project    = 2,
                     title      = 2 }
    local k, v, r
    for k, v in pairs( args ) do
        mode = params[ k ]
        if not mode then
            mode = -1
        end
        if not got[ mode ] then
            got[ mode ] = { }
        end
        got[ mode ][ k ] = v
    end -- for k, v
    r = got[ -1 ]
    if r then
        local s
        if #r == 1 then
            s = ""
        else
            s = "s"
        end
        r = string.format( "Unknown parameter%s: '%s'",
                           s,  concatKeys( r ) )
        error( r, 0 )
    elseif got[ 1 ] and got[ 2 ] then
        r = string.format( "'%s' conflicting with '%s'",
                           concatKeys( got[ 1 ] ),
                           concatKeys( got[ 2 ] ) )
        error( r, 0 )
    else
        local procs  = { unified, sourcing }
        if mode == 0 then
            mode = 2
            got[ 2 ] = { }
        end
        got = got[ mode ]
        for k, v in pairs( got ) do
            if v  and  #v == 0 then
                got[ k ] = nil
            end
        end -- for k, v
        r = procs[ mode ]( got )
    end
    return r
end -- main()



-- Export
local p = {}

function p.test( a )
    local lucky, r = pcall( main, a )
    return r
end

function p.f( frame )
    local lucky, r = pcall( main, frame:getParent().args )
    if not lucky then
        r = string.format( "<span class=\"error\">%s</span>", r )
    end
    return r
end

return p