Herzlich Willkommen zum Mitglieder-Wiki von Wikimedia Österreich.
Für ein Benutzerkonto schreibe bitte an verein [at] wikimedia.at
Modul:Vorlage:Phab
Zur Navigation springen
Zur Suche springen
Die Dokumentation für dieses Modul kann unter Modul:Vorlage:Phab/Doku erstellt werden
local Serial = "2018-07-15" --[=[ {{Template:Phab}} ]=] -- local globals local Sick = "Category:Wikipedia:Vorlagenfehler/Vorlage:Phab" local Site = "https://phabricator.wikimedia.org/" 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 -- .File -- .Show -- .style -- Postcondition: -- Returns string with bracketed external link, if fine -- Throws error on failure local r = args.File 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 if args.style then if args.Show then show = args.Show else show = string.format( "F%d", n ) end show = string.format( "<span style='%s'>%s</span>", args.style, show ) end if show then r = string.format( "[[phab:F%d|%s]]", n, show ) else r = string.format( "[[phab:F%d]]", n ) end 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, "PATH" ) 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 -- .Mock -- .Show -- .style -- Postcondition: -- Returns string with bracketed external link, if fine -- Throws error on failure local r = args.Mock 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 if args.style then if args.Show then show = args.Show else show = string.format( "M%d", n ) end show = string.format( "<span style='%s'>%s</span>", args.style, show ) end if show then r = string.format( "[[phab:M%d|%s]]", n, show ) else r = string.format( "[[phab:M%d]]", n ) end 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 phabBoards( args ) -- Link to a board, project, team: tag -- Precondition: -- args -- table; assignments -- .Engage -- .Show -- .style -- string; single ID -- Postcondition: -- Returns string URL, if fine local s = type( args ) local n, r, show, tags if s == "table" then if type( args.Engage ) == "string" then tags = args.Engage end if args.Show and ( args.Show ~= "" ) then show = args.Show end elseif s == "string" then tags = args end if not tags or tags == "" then tags = "MediaWiki-General-or-Unknown" end tags = mw.text.split( tags, "%s+" ) n = #tags if show and n > 1 then show = false end for i = 1, n do s = tags[ i ] if r then r = r .. " | " else r = "" end if not show then show = "#" .. s end if args.style then show = string.format( "<span style='%s'>%s</span>", args.style, show ) end r = string.format( "%s[[phab:tag/%s|%s]]", r, s, show ) end -- for i return r end -- phabBoards() 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 sub = "phab:diffusion/" local details, pages, s, sign, support r = "{| class='wikitable sortable'\n" .. "|- class='hintergrundfarbe6'\n" .. "! Callsign !! ''repository'' !!class='unsortable'| Infos\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|- id='%s'\n|[[%s%s|%s]]||%s\n", r, sign, sub, sign, sign, show ) pages = { } details = connect[ sign ] if type( details ) ~= "table" then details = { } end if details.say then table.insert( pages, details.say ) end if details.support then table.insert( pages, string.format( "[[%s]]", details.support ) ) end if type( details.tags ) == "table" then for k, v in pairs( details.tags ) do table.insert( pages, phabBoards( 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 and 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 = string.format( "%s|}\n%d ", r, #order ) end return r end -- phabCallsigns() local function phabDiffusion( action, assembly, adopt, attach, anchor, against, as, frame ) -- Create path 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) -- as -- string; s = query -- frame -- object or nil -- Postcondition: -- Return 1 -- URL, if identified, or nil -- 2 -- callsign, or nil -- Uses: -- phabAssigned() local translate = phabAssigned( frame ) local r, shortcut if translate then 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 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 if attach then if action == "commit" then swift = "browse" else swift = "change" end state = "master" else sub = string.format( "r%s%s", shortcut, state ) end 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 local sep if state == "master" then sep = "/" else sep = "" end sub = string.format( "%s%s%s", sub, sep, state ) end if attach then sub = string.format( "%s/%s", sub, attach ) if adopt and adopt ~= "HEAD" and state ~= adopt then sub = string.format( "%s;%s", sub, adopt ) end if anchor then sub = string.format( "%s$%s", sub, anchor ) end end if as then sub = string.format( "%s?%s", sub, as ) end end r = Site .. sub:gsub( " ", "%%20" ) end end return r, shortcut end -- phabDiffusion() local function phabUser( args, frame ) -- Link to a user -- Precondition: -- args -- table; assignments -- .User -- .Show -- .style -- frame -- object or nil -- Postcondition: -- Returns string URL, if fine -- Uses: -- mw.text.trim() local r local single = args.User if type( single ) == "string" then single = mw.text.trim( single ) end if type( single ) == "string" and single ~= "" then local show = args.Show if not show or show == "" then show = "@" .. single end if not frame then frame = mw.getCurrentFrame() end if args.style then show = string.format( "<span style='%s'>%s</span>", args.style, show ) end r = string.format( "[[phab:p/%s|%s]]", frame:callParserFunction( "urlencode", single ), show ) else r = string.format( "Missing User :-(" ) error( r, 0 ) end return r end -- phabUser() 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() -- phabDiffusion() -- git() local jump = false local last = false local query = false local scope = "mediawiki/core" local show local since = false local slot = "HEAD" local source = false local swift = "dir" local lapsus, load, r r = lonely( args, { "branch", "commit", "commitdiff", "diff" } ) if r then slot = args[ r ] end r = lonely( args, { "dir", "file", "plain" } ) if r then swift = r source = args[ swift ] if args.file then local suffix = args.file:match( "%.(%l+)$" ) if suffix then if suffix == "svg" then query = "as=source&highlight=xml&" elseif suffix == "json" or suffix == "txt" or suffix == "xml" then query = "?" end if query then query = query .. "blame=off" end if suffix == "md" then query = "as=remarkup" end end end end r = lonely( args, { "commit", "commitdiff" } ) if r then swift = r load = true 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 ) elseif args.line then if args.diff then r = "No ''line'' on ''diff'' page available" error( r, 0 ) end jump = args.line end end if load then if scope == "SVN" then r = "Invalid SVN identifier: " if slot:match( "^[0-9]+$" ) then slot = tonumber( slot ) if slot >= 1 and slot <= 115794 then r = false end slot = string.format( "%d", slot ) end if r then local e = r .. slot error( e, 0 ) end else id40( slot ) end elseif args.diff then since = args.diff id40( since ) elseif last then swift = "history" end r, show = phabDiffusion( swift, scope, slot, source, jump, since, query, frame ) if args.title and ( args.title ~= "" ) then show = args.title elseif last and source then show = "history: " .. source elseif source then show = source if args.diff then show = string.format( "%s ./.%s", show, since:sub( 1, 7 ) ) elseif load then show = string.format( "%s;%s", show, slot:sub( 1, 7 ) ) end elseif load then if show then show = "r" .. show else show = "GIT:" end show = show .. slot:sub( 1, 7 ) else show = scope .. "/*" end if r then r = string.format( "[%s %s]", r, show ) else if not scope:find( "/", 3, true ) then error( "Unknown project: " .. scope, 0 ) end lonely( args, { "commit", "commitdiff", "dir", "file", "plain" } ) r = git( swift, scope, slot, source, jump, since ) r = string.format( "[%s %s]", r, show ) lapsus = true end if lapsus then r = string.format( "%s[[%s/Diffusion]]", r, Sick ) end return r end -- sourcing() local function task( args ) -- Link to a Task -- Precondition: -- args -- table; assignments -- .Task -- .Anchor -- .Show -- .style -- Postcondition: -- Returns string with bracketed external link, if fine -- Throws error on failure local r = args.Task local j = r:find( "#", 1, true ) local sub = args.Anchor 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 sub then sub = 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 if sub then sub = string.format( "#anchor-%s", sub ) else sub = "" 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 if args.style then show = string.format( "<span style='%s'>%s</span>", args.style, show ) end r = string.format( "[[phab:T%d%s|%s]]", n, sub, 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 <= 73681 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, frame ) -- Link to a management issue -- Precondition: -- args -- table; assignments -- Postcondition: -- Returns string, if fine -- Throws error on failure -- Uses: -- lonely() -- taskBugzilla() -- task() -- phabBoards() -- phabUser() local r lonely( args, { "Bugzilla", "Countdown", "Differential", "Engage", "File", "Gerrit", "Join", "Mock", "Paste", "Review", "Task", "User" } ) if args.Bugzilla then r = taskBugzilla( args ) elseif args.Engage then r = phabBoards( args ) elseif args.File then r = file( args ) elseif args.Mock then r = mock( args ) elseif args.Task then r = task( args ) elseif args.User then r = phabUser( args, frame ) else r = "NOT YET READY" error( r, 0 ) end -- Countdown -- Differential -- Gerrit -- Join -- Paste -- Review -- Leerzeichen-getrennte Liste 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 defaults = { Bugzilla = "", Engage = true, User = true } local got = { } local mode = 0 local params = { style = 0, Anchor = 1, Anchor = 1, Bugzilla = 1, Countdown = 1, Differential = 1, Engage = 1, File = 1, -- Gerrit = 1, Join = 1, Mock = 1, Paste = 1, Review = 1, Show = 1, Task = 1, User = 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, s 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 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 if got[ 0 ] then for k, v in pairs( got[ 0 ] ) do got[ mode ][ k ] = v end -- for k, v end got = got[ mode ] for k, v in pairs( got ) do s = type( v ) if s == "number" then v = tostring( v ) elseif s == "string" then if #v == 0 then v = defaults[ k ] end elseif v == true then v = defaults[ k ] else v = nil end if v then got[ k ] = v else 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>[[%s]]", r, Sick ) end return r end function p.failsafe() return Serial end return p