Modul:Vorlage:Phab
Modul:Vorlage:LuaModuleDoc:142: attempt to index field 'wikibase' (a nil value)
--[=[ 2015-03-23
{{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
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 phabAssigned( frame )
-- Retrieve table with callsign assignments
-- Precondition:
-- frame -- object or nil
-- Postcondition:
-- Return table or nil
-- Throws error on failure
-- Uses:
-- mw.getCurrentFrame()
-- mw.loadData()
local got, lucky, r, s
if not frame then
frame = mw.getCurrentFrame()
end
s = frame:getTitle() .. "/callsigns"
lucky, got = pcall( mw.loadData, s )
if type( got ) == "table" then
r = got
else
error( "Invalid: " .. s, 0 )
end
return r
end -- phabAssigned()
local function phabCallsigns( args, frame )
-- Create wikitable of all callsigns
-- Precondition:
-- args -- table; options
-- frame -- object or nil
-- Uses:
-- phabAssigned()
local connect = phabAssigned( frame )
local r
if connect then
local limit = ( args.callsigns == "0" )
local callsigns = { }
local order = { }
local store = "https://phabricator.wikimedia.org/"
local sub = "diffusion/"
local details, pages, s, sign, support
r = "{| class='wikitable sortable'\n" ..
"|- class='hintergrundfarbe6'\n" ..
"! Callsign !! Titel !!\n"
for k, v in pairs( connect ) do
if k:find( "/", 3, true ) then
-- GIT legacy
v = false
elseif limit and type( v ) == "string" then
details = connect[ v ]
if type( details ) == "table" and
type( details.list ) == "boolean" and
not details.list then
v = false
end
end
if type( v ) == "string" then
s = callsigns[ v ]
if s then
if #s < #k then
callsigns[ v ] = k
end
else
callsigns[ v ] = k
table.insert( order, v )
end
end
end -- for k, v
table.sort( order )
for i = 1, #order do
sign = order[ i ]
show = callsigns[ sign ]
r = string.format( "%s|-\n|[%s%s%s %s]||%s\n",
r, store, sub, sign, sign, show )
pages = { }
details = connect[ sign ]
if type( details ) ~= "table" then
details = { }
end
if details.support then
table.insert( pages,
string.format( "[[%s]]",
details.support ) )
end
if type( details.bound ) == "table" then
for k, v in pairs( details.bound ) do
table.insert( pages,
string.format( "[%s%s %s]",
store, v, v ) )
end -- for k, v
end
support = details.mwPage
if not support then
s = show:match( "^extension%-([%w_]+)$" )
if s then
support = "Extension:" .. s
end
end
if support then
table.insert( pages,
string.format( "[[mw:%s]]", support ) )
end
if #pages > 0 then
r = string.format( "%s|%s\n",
r, table.concat( pages, "<br>" ) )
end
end -- for i
r = r .. "|}\n"
end
return r
end -- phabCallsigns()
local function phabDiffusion( action, assembly, adopt, attach,
anchor, against, frame )
-- Create URL for Phabricator Diffusion since 2015-03
-- 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)
-- frame -- object or nil
-- Postcondition:
-- Return URL, if identified, or nil
-- Uses:
-- phabAssigned()
local translate = phabAssigned( frame )
local r
if translate then
local shortcut
if assembly:match( "^%u%u%u?%u?$" ) then
shortcut = assembly
else
shortcut = translate[ assembly ]
if not shortcut and assembly:find( "/", 3, true ) then
-- GIT legacy
local s = assembly:gsub( "mediawiki/extensions/",
"extension-" )
shortcut = translate[ s ]
end
end
if shortcut then
local store = "https://phabricator.wikimedia.org/"
local state, sub, swift
if adopt ~= "master" and adopt ~= "HEAD" then
state = adopt
end
if attach and not state then
state = "master"
end
if state then
if action then
if action == "commit" or
action == "commitdiff" then
swift = action
sub = string.format( "r%s%s",
shortcut, state )
elseif action == "history" then
swift = action
else
swift = "browse"
end
else
swift = "browse"
end
end
if not sub then
sub = "diffusion/" .. shortcut
if swift then
sub = string.format( "%s/%s", sub, swift )
end
if state then
sub = string.format( "%s/%s", sub, state )
end
if attach then
sub = string.format( "%s/%s", sub, attach )
if anchor then
sub = string.format( "%s#L%s", sub, anchor )
end
end
end
r = store .. sub
end
end
return r
end -- phabDiffusion()
local function sourcing( args, frame )
-- Link to a code source, directory, branch or related
-- Precondition:
-- args -- table; assignments
-- frame -- object or nil
-- Postcondition:
-- Returns string, if fine
-- Throws error on failure
-- Uses:
-- lonely()
-- id40()
-- mw.text.trim()
-- phabDiffusion()
-- git()
local jump = false
local last = false
local scope = "mediawiki/core"
local show
local since = false
local slot = "HEAD"
local source = false
local swift = "dir"
local load, 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
r = false
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"
else
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 = phabDiffusion( swift, scope, slot, source, jump, since, frame )
if not r then
r = git( swift, scope, slot, source, jump, since )
.. "[[Category:Wikipedia:Technik/Phabricator/Diffusion]]"
end
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 #c%d", show, item )
end
r = string.format( "[%s %s]", r, show )
r = string.format( "%s <small style=\"font-weight:normal\">(%s)</small>",
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, frame )
-- Do the job
-- Precondition:
-- args -- table; assignments
-- frame -- object or nil
-- 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,
callsigns = 3 }
local gr
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, phabCallsigns }
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, frame )
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, frame )
if not lucky then
r = string.format( "<span class=\"error\">%s</span>", r )
end
return r
end
return p