Module:UserLinks: Difference between revisions

From Sarkarverse
Jump to navigation Jump to search
m (1 revision)
 
m (1 revision)
 
(One intermediate revision by the same user not shown)
Line 1: Line 1:
local ToolbarBuilder = require('Module:Toolbar')
--------------------------------------------------------------------------------
local interwikiTable = mw.loadData("Module:InterwikiTable")
--                                UserLinks                                  --
-- This module creates a list of links about a given user. It can be used on  --
-- its own or from a template. See the /doc page for more documentation.      --
--------------------------------------------------------------------------------


local u = {} -- Table for user-data helper strings.
-- Require necessary modules
local trackingCategories = {} -- Table for storing the tracking categories.
local yesno = require('Module:Yesno')
local demo


-- Define a custom error message for this module.
-- Lazily initialise modules that we might or might not need
local function err(msg, section)
local mExtra -- [[Module:UserLinks/extra]]
    local help
local mArguments -- [[Module:Arguments]]
    if section then
local mToolbar -- [[Module:Toolbar]]
        help = ' ([[Template:User-multi#' .. section .. '|help]])'
local mCategoryHandler -- [[Module:Category handler]]
    else
local mTableTools -- [[Module:TableTools]]
        help = ''
local interwikiTable -- [[Module:InterwikiTable]], loaded with mw.loadData
    end
 
    local cat
-- Load shared helper functions
    if demo == 'yes' then
local mShared = require('Module:UserLinks/shared')
        cat = ''
local raiseError = mShared.raiseError
    else
local maybeLoadModule = mShared.maybeLoadModule
        cat = '[[Category:UserLinks transclusions with errors]]'
local makeWikitextError = mShared.makeWikitextError
    end
local makeWikilink = mShared.makeWikilink
    return '<span class="error">[[Template:User-multi|User-multi]] error: ' .. msg
local makeUrlLink = mShared.makeUrlLink
        .. help .. '.</span>' .. cat
local makeFullUrlLink = mShared.makeFullUrlLink
end
local message = mShared.message
 
local p = {}
 
--------------------------------------------------------------------------------
-- Link table
--------------------------------------------------------------------------------
 
function p.getLinks(snippets)
--[=[
-- Get a table of links that can be indexed with link codes. The table
-- returned is blank, but links are added to it on demand when it is
-- indexed. This is made possible by the metatable and by the various link
-- functions, some of which are defined here, and some of which are defined
-- at [[Module:UserLinks/extra]].
--]=]
local links, linkFunctions = {}, {}
 
----------------------------------------------------------------------------
-- Link functions
--
-- The following functions make the links from the link codes and the user
-- data snippets. New link functions should be added below the existing
-- functions.
--
-- For documentation on how to add new link functions, please see
-- [[Module:UserLinks#Adding new links]].
----------------------------------------------------------------------------
 
function linkFunctions.u(snippets)
-- User page
return makeWikilink(
snippets.interwiki,
2,
snippets.username,
snippets.username
)
end
 
function linkFunctions.t(snippets)
-- User talk page
return makeWikilink(
snippets.interwiki,
3,
snippets.username,
message('display-talk')
)
end
 
function linkFunctions.c(snippets)
-- Contributions
return makeWikilink(
snippets.interwiki,
-1,
'Contributions/' .. snippets.username,
message('display-contributions')
)
end
 
function linkFunctions.ct(snippets)
-- Edit count
return makeUrlLink(
{
host = 'tools.wmflabs.org',
path = '/supercount/index.php',
query = {
user = snippets.username,
project = snippets.toolLang .. '.' .. snippets.projectLong
}
},
message('display-count')
)
end
 
function linkFunctions.m(snippets)
-- Page moves
return makeWikilink(
snippets.interwiki,
-1,
'Log/move/' .. snippets.username,
message('display-moves')
)
end
 
function linkFunctions.l(snippets)
-- Logs
return makeWikilink(
snippets.interwiki,
-1,
'Log/' .. snippets.username,
message('display-logs')
)
end
 
function linkFunctions.bl(snippets)
-- Block log
return makeFullUrlLink(
snippets.interwiki,
-1,
'Log/block',
{page = 'User:' .. snippets.username},
message('display-blocklog')
)
end
 
function linkFunctions.bls(snippets)
-- Blocks
return makeWikilink(
snippets.interwiki,
-1,
'Log/block/' .. snippets.username,
message('display-blocks')
)
end
 
function linkFunctions.bu(snippets)
-- Block user
return makeWikilink(
snippets.interwiki,
-1,
'Block/' .. snippets.username,
message('display-blockuser')
)
end
 
function linkFunctions.ca(snippets)
-- Central auth
return makeWikilink(
snippets.interwiki,
-1,
'CentralAuth/' .. snippets.username,
message('display-centralauth')
)
end
 
function linkFunctions.dc(snippets)
-- Deleted contribs
return makeWikilink(
snippets.interwiki,
-1,
'DeletedContributions/' .. snippets.username,
message('display-deletedcontributions')
)
end
 
function linkFunctions.e(snippets)
-- Email
return makeWikilink(
snippets.interwiki,
-1,
'EmailUser/' .. snippets.username,
message('display-email')
)
end
 
function linkFunctions.es(snippets)
-- Edit summaries
return makeUrlLink(
{
host = 'tools.wmflabs.org',
path = '/xtools/editsummary/index.php',
query = {
name = snippets.username,
lang = snippets.toolLang,
wiki = snippets.projectLong
}
},
message('display-editsummaries')
)
end
 
function linkFunctions.del(snippets)
-- Deletions
return makeWikilink(
snippets.interwiki,
-1,
'Log/delete/' .. snippets.username,
message('display-deletions')
)
end
 
function linkFunctions.lu(snippets)
-- List user
return makeFullUrlLink(
snippets.interwiki,
-1,
'ListUsers',
{limit = 1, username = snippets.username},
message('display-listuser')
)
end
 
function linkFunctions.sul(snippets)
-- SUL
return makeWikilink(
nil,
nil,
'sulutil:' .. snippets.username,
message('display-sul')
)
end
 
function linkFunctions.tl(snippets)
-- Target logs
return makeFullUrlLink(
snippets.interwiki,
-1,
'Log',
{page = mw.site.namespaces[2].name .. ':' .. snippets.username},
message('display-targetlogs')
)
end


----------------------------------------------------------------------------------------------
function linkFunctions.efl(snippets)
--      To add more link types, write a function that produces an individual link, and put  --
-- Edit filter log
--      it at the bottom of the list below. Then, add a link code for your function to the  --
return makeFullUrlLink(
--      "linktypes" table. Try and make the code three letters or less. There are a number  --
snippets.interwiki,
--     of helper strings available for writing the functions:                      --
-1,
--                                                                                          --
'AbuseLog',
--      u.username          The plain username. If the username is not present then the    --
{wpSearchUser = snippets.username},
--                          module returns an error.                                        --
message('display-abuselog')
--      u.usernameHtml      The username html-encoded. Spaces are encoded with plus signs.  --
)
--      u.project          The project name. Nil if not specified.                        --
end
--      u.lang              The language code. Nil if not specified.                        --
--      u.interwiki         The interwiki prefix, consisting of the project and language    --
--                          values, separated by colons, e.g. ":wikt:es:". If no project    --
--                          or language values are found, this is the blank string, "".    --
--      u.projectCode      If a valid project is specified, this is the code for that      --
--                          project in [[Module:InterwikiTable]]. Otherwise this is nil.    --
--      u.projectLong      The long project name, e.g. "wikipedia" or "wikibooks". If      --
--                          not specified the default is "wikipedia".                      --
--      u.toolLang          The language code for use with toolserver or labs tools. The    --
--                          default is "en".                                                --
--                                                                                          --
--      If you want more helper strings, you can define them in the generateUserDataStrings --
--      function below.                                                                    --
----------------------------------------------------------------------------------------------


----------------------------------------------------------------------------------------------
function linkFunctions.pr(snippets)
--                                LINK FUNCTIONS START                                      --
-- Protections
----------------------------------------------------------------------------------------------
return makeWikilink(
snippets.interwiki,
-1,
'Log/protect/' .. snippets.username,
message('display-protections')
)
end


local function makeUserLink()
function linkFunctions.rl(snippets)
    return '[[' .. u.interwiki .. 'User:' .. u.username .. '|' .. u.username .. ']]'
-- User rights
end
return makeWikilink(
snippets.interwiki,
-1,
'Log/rights/' .. snippets.username,
message('display-rights')
)
end


local function makeTalkLink()
function linkFunctions.ren(snippets)
    return '[[' .. u.interwiki .. 'User talk:' .. u.username .. '|talk]]'
-- Renames
end
return makeWikilink(
snippets.interwiki,
-1,
'Log/renameuser/' .. snippets.username,
message('display-renames')
)
end


local function makeContribsLink()
function linkFunctions.rfa(snippets)
    return '[[' .. u.interwiki .. 'Special:Contributions/' .. u.username .. '|contribs]]'
-- Requests for adminship
end
return makeWikilink(
nil,
-1,
'PrefixIndex/' .. message('page-rfa') .. '/' .. snippets.username,
message('display-rfa')
)
end


local function makeCountLink()
function linkFunctions.api(snippets)
    return '[//tools.wmflabs.org/xtools/pcount/index.php?name=' .. u.usernameHtml
-- API user data
        .. '&lang=' .. u.toolLang
return makeUrlLink(
        .. '&wiki=' .. u.projectLong
{
        .. ' count]'
host = snippets.fullDomain,
end
path = '/w/api.php',
query = {
action = 'query',
list = 'users',
usprop = 'groups|editcount',
ususers = snippets.username
}
},
message('display-api')
)
end


local function makeMovesLink()
function linkFunctions.up(snippets)
    return '[[' .. u.interwiki .. 'Special:Log/move/' .. u.username .. '|page&nbsp;moves]]'
-- Uploads
end
return makeWikilink(
snippets.interwiki,
-1,
'ListFiles/' .. snippets.username,
message('display-uploads')
)
end
----------------------------------------------------------------------------
-- End of link functions
----------------------------------------------------------------------------


local function makeLogsLink()
-- Define the metatable that memoizes the link functions, and fetches link
    return '[[' .. u.interwiki .. 'Special:Log/' .. u.username .. '|logs]]'
-- functions from [[Module:UserLinks/extra]] if necessary.
end


local function makeBlockLogLink()
-- Lazily initialise the extraLinkFunctions table. We only want to load
    local url = mw.uri.fullUrl(u.interwiki .. 'Special:Log/block', 'page=User:' .. u.usernameHtml)
-- [[Module:UserLinks/extra]] as necessary, so it has a low transclusion
    return '[' .. tostring(url) .. ' block&nbsp;log]'
-- count.
end
local extraLinkFunctions


local function makeBlocksLink()
-- Define functions for shared code in the metatable.
    return '[[' .. u.interwiki .. 'Special:Log/block/' .. u.username .. '|blocks]]'
local function validateCode(code)
end
-- Checks whether code is a valid link code - i.e. checks that it is a
-- string and that it is not the blank string. Returns the code if
-- the check passes, and nil if not.
if type(code) == 'string' and code ~= '' then
return code
else
return nil
end
end


local function makeBlockUserLink()
local function getExtraLinkFunctions()
    return '[[' .. u.interwiki .. 'Special:Block/' .. u.username .. '|block&nbsp;user]]'
-- Loads the table of extra link functions from the /extra module.
end
-- If there is a problem with loading it, return false. We use the
-- distinction between false and nil to record whether we have already
-- tried to load it.
if extraLinkFunctions ~= nil then
return extraLinkFunctions
end
if mExtra == nil then
-- If loading the module fails, maybeLoadModule returns false.
-- Here we use the distinction between false and nil to record
-- whether we have already tried to load the /extra module.
mExtra = maybeLoadModule('Module:UserLinks/extra')
end
if type(mExtra) == 'table'
and type(mExtra.linkFunctions) == 'table'
then
extraLinkFunctions = mExtra.linkFunctions
else
extraLinkFunctions = false
end
return extraLinkFunctions
end


local function makeCentralAuthLink()
local function memoizeExtraLink(code, func)
    return '[[' .. u.interwiki .. 'Special:CentralAuth/' .. u.username .. '|central&nbsp;auth]]'
local success, link = pcall(func, snippets)
end
if success and type(link) == 'string' then
links[code] = link
return link
end
return nil
end


local function makeDeletedContribsLink()
-- Define the metatable.
    return '[[' .. u.interwiki .. 'Special:DeletedContributions/' .. u.username .. '|deleted&nbsp;contribs]]'
setmetatable(links, {
__index = function (t, key)
local code = validateCode(key)
if not code then
raiseError(
message('error-malformedlinkcode'),
message('error-malformedlinkcode-section')
)
end
local linkFunction = linkFunctions[code]
local link
if linkFunction then
link = linkFunction(snippets)
links[code] = link
else
extraLinkFunctions = getExtraLinkFunctions()
if extraLinkFunctions then
local extraLinkFunction = extraLinkFunctions[code]
if type(extraLinkFunction) == 'function' then
link = memoizeExtraLink(code, extraLinkFunction)
end
end
end
if link then
return link
else
raiseError(
message('error-invalidlinkcode', code),
message('error-invalidlinkcode-section')
)
end
end,
__pairs = function ()
extraLinkFunctions = getExtraLinkFunctions()
if extraLinkFunctions then
for code, func in pairs(extraLinkFunctions) do
if validateCode(code) and type(func) == 'function' then
memoizeExtraLink(code, func)
end
end
end
-- Allow built-in functions to overwrite extra functions.
for code, func in pairs(linkFunctions) do
local link = func(snippets)
links[code] = link
end
return function (t, key)
return next(links, key)
end
end
})
return links
end
end


local function makeEmailLink()
--------------------------------------------------------------------------------
    return '[[' .. u.interwiki .. 'Special:Emailuser/' .. u.username .. '|email]]'
-- User data snippets
end
--------------------------------------------------------------------------------


local function makeEditSummariesLink()
function p.getSnippets(args)
    return '[http://tools.wmflabs.org/xtools/editsummary/index.php?name=' .. u.usernameHtml
--[=[
        .. '&lang=' .. u.toolLang
-- This function gets user data snippets from the arguments, and from
        .. '&wiki=' .. u.projectLong
-- [[Module:InterwikiTable]]. The data is loaded as necessary and memoized
        .. ' edit&nbsp;summaries]'
-- in the snippets table for performance.  
end
--
-- Snippets default to the blank string, '', so they can be used in
-- concatenation operations without coders having to worry about raising
-- errors. Because of this, the local functions snippetExists and
-- getSnippet have been written to aid people writing new snippets. These
-- functions treat the blank string as false. It is not necessary to return
-- the blank string from a snippet function, as nil and false values are
-- automatically converted into the blank string by the metatable.
--
-- If you add a new snippet, please document it at
-- [[Module:UserLinks#Adding new links]].
--]=]
local snippets, snippetFunctions = {}, {}
setmetatable(snippets, {
__index = function (t, key)
local snippetFunction = snippetFunctions[key]
if snippetFunction then
snippets[key] = snippetFunction() or ''
return snippets[key]
else
raiseError(
message('error-nosnippet', key),
message('error-nosnippet-section')
)
end
end
})


local function makeDeletionsLink()
-- Define helper functions for writting the snippet functions.
    return '[[' .. u.interwiki .. 'Special:Log/delete/' .. u.username .. '|deletions]]'
local function snippetExists(key)
end
-- We have set the metatable up to make snippets default to '', so we
-- don't have to test for false or nil.
return snippets[key] ~= ''
end


local function makeListUserLink()
local function getSnippet(key)
    local url = mw.uri.fullUrl(u.interwiki .. 'Special:ListUsers', 'limit=1&username=' .. u.usernameHtml)
local ret = snippets[key]
    return '[' .. tostring(url) .. ' list&nbsp;user]'
if ret == '' then
end
return nil
else
return ret
end
end


local function makeSulLink()
-- Start snippet functions.
    return '[[sulutil:' .. u.username .. '|global&nbsp;contribs]]'
end


local function makeTargetLogsLink()
function snippetFunctions.username()
    local url = mw.uri.fullUrl(u.interwiki .. 'Special:Log', 'page=User:' .. u.usernameHtml)
-- The username.
    return '[' .. tostring(url) .. ' target&nbsp;logs]'
local username = args.user or args.User
end
return username or raiseError(
message('error-nousername'),
message('error-nousername-section')
)
end


local function makeEditFilterLogLink()
function snippetFunctions.usernameHtml()
    local url = mw.uri.fullUrl(u.interwiki .. 'Special:AbuseLog', 'wpSearchUser=' .. u.usernameHtml)
-- The username html-encoded. Spaces are encoded as pluses.
    return '[' .. tostring(url) .. ' edit&nbsp;filter&nbsp;log]'
return mw.uri.encode(snippets.username)
end
end


local function makeProtectionsLink()
function snippetFunctions.project()
    return '[[' .. u.interwiki .. 'Special:Log/protect/' .. u.username .. '|protections]]'
-- The project name.
end
-- Also does the work for snippetFunctions.interwikiTableKey, and adds
-- the project value to snippets.lang if it is a valid language code.
local project = args.Project or args.project
if not project then
return nil
end
local projectValidated, interwikiTableKey = p.validateProjectCode(project)
if not projectValidated then
if mw.language.isKnownLanguageTag(project) then
if not snippetExists('lang') then
snippets.lang = project
end
else
raiseError(
message('error-invalidproject', project),
message('error-invalidproject-section')
)
end
end
snippets.interwikiTableKey = interwikiTableKey
return project
end


local function makeRightsLink()
function snippetFunctions.interwikiTableKey()
    return '[[' .. u.interwiki .. 'Special:Log/rights/' .. u.username .. '|rights]]'
-- The key for the project in Module:InterwikiTable.
end
-- Relies on snippetFunctions.project to do the real work.
local temp = snippets.project -- required; puts key in snippets table
return rawget(snippets, 'interwikiTableKey')
end


local function makeRenamesLink()
function snippetFunctions.toolProject()
    return '[[' .. u.interwiki .. 'Special:Log/renameuser/' .. u.username .. '|renames]]'
-- The short project code for use with toolserver or labs. It is always
end
-- present, even if the "project" argument is absent. The default value
-- is the "snippet-project-default" message.
local project = getSnippet('project')
if not project then
return message('snippet-project-default')
else
return project
end
end


local function makeRfaLink()
function snippetFunctions.projectLong()
    if u.project or u.lang then
-- The long form of the project name, e.g. "wikipedia" or "wikibooks".
        table.insert( trackingCategories, '[[Category:UserLinks transclusions with unresolvable interwiki links]]' )
local key = getSnippet('interwikiTableKey')
    end
if not key then
    return '[[Special:PrefixIndex/Wikipedia:Requests for adminship/' .. u.username .. '|RfA]]'
return message('snippet-projectlong-default')
end
end
interwikiTable = interwikiTable or mw.loadData('Module:InterwikiTable')
local prefixes = interwikiTable[key].iw_prefix
-- Using prefixes[2] is a bit of a hack, but should find the long name
-- most of the time.
return prefixes[2] or prefixes[1]  
end


local function makeApiLink()
function snippetFunctions.lang()
    -- Find the full domain, as the API can't be accessed through the interwiki system.
-- The language code.
    local fulldomain
local lang = args.lang or args.Lang
    local lang = u.lang or 'en'
if not lang then
    if u.projectCode then
return nil
        local domain = interwikiTable[u.projectCode].domain
end
        local takesLangPrefix = interwikiTable[u.projectCode].takes_lang_prefix
if mw.language.isKnownLanguageTag(lang) then
        if not takesLangPrefix then
return lang
            fulldomain = domain
else
        else
raiseError(
            fulldomain = lang .. '.' .. domain
message('error-invalidlanguage', lang),
        end
message('error-invalidlanguage-section')
    else
)
        fulldomain = lang .. '.wikipedia.org'
end
    end
end
   
    -- Return the API link
    return '[//' .. fulldomain .. '/w/api.php?action=query&list=users&usprop=editcount&ususers=' .. u.usernameHtml .. ' api]'
end


local function makeUploadsLink()
function snippetFunctions.toolLang()
    return '[[' .. u.interwiki .. 'Special:ListFiles/' .. u.username .. '|uploads]]'
-- The language code for use with toolserver or labs tools. It is always
end
-- present, even if the "lang" argument is absent. The default value is
-- the "snippet-lang-default" message.  
return getSnippet('lang') or message('snippet-lang-default')
end


----------------------------------------------------------------------------------------------
function snippetFunctions.interwiki()
--                                   LINK FUNCTIONS END                                    --
-- The interwiki prefix, consisting of the project and language values,
--     To enable new link functions, add the code to the "linktypes" table directly below. --
-- separated by colons, e.g. ":wikt:es:".
----------------------------------------------------------------------------------------------
local project = getSnippet('project')
local lang = getSnippet('lang')
if not project and not lang then
return nil
end
local ret = {}
ret[#ret + 1] = project
ret[#ret + 1] = lang
return table.concat(ret, ':')
end


local linktypes = {
function snippetFunctions.fullDomain()
    {'t'  , makeTalkLink},
-- The full domain name of the site, e.g. www.mediawiki.org,
    {'c'  , makeContribsLink},
-- en.wikpedia.org, or ja.wikibooks.org.
    {'ct'  , makeCountLink},
local fullDomain
    {'m'   , makeMovesLink},
local lang = getSnippet('toolLang')
    {'l'   , makeLogsLink},
local key = getSnippet('interwikiTableKey')
    {'bl'  , makeBlockLogLink},
if key then
    {'bls' , makeBlocksLink},
interwikiTable = interwikiTable or mw.loadData('Module:InterwikiTable')
    {'bu'  , makeBlockUserLink},
local domain = interwikiTable[key].domain
    {'ca'  , makeCentralAuthLink},
local takesLangPrefix = interwikiTable[key].takes_lang_prefix
    {'dc'  , makeDeletedContribsLink},
if takesLangPrefix then
    {'e'  , makeEmailLink},
fullDomain = lang .. '.' .. domain
    {'es' , makeEditSummariesLink},
else
    {'del' , makeDeletionsLink},
fullDomain = domain
    {'lu'  , makeListUserLink},
end
    {'sul' , makeSulLink},
else
    {'tl'  , makeTargetLogsLink},
fullDomain = lang .. '.wikipedia.org'
    {'efl' , makeEditFilterLogLink},
end
    {'pr' , makeProtectionsLink},
return fullDomain
    {'rl'  , makeRightsLink},
end
    {'ren' , makeRenamesLink},
    {'rfa' , makeRfaLink},
    {'api' , makeApiLink},
    {'up'  , makeUploadsLink}
}


local function getLink(linktype)
-- End snippet functions. If you add a new snippet function, please
    local linkNumber
-- document it at [[Module:UserLinks#Adding new links]].
    for i, value in ipairs(linktypes) do
        if value[1] == linktype then
            linkNumber = i
            break
        end
    end
    if not linkNumber then
        return err('"' .. linktype .. '" is not a valid link code', 'Not a valid link code')
    end
    local result = linktypes[linkNumber][2]()
    if type(result) ~= 'string' then
        return err(
            'the function for code "' .. linktype .. '" did not return a string value',
            'Function did not return a string value'
        )
    end
    return result
end


local function makeToolbar(args)
return snippets
    local targs = {}
end
    local numArgsExist = false
    for k, v in pairs(args) do
        if type(k) == 'number' then
            numArgsExist = true
            targs[k] = getLink(v)
        end
    end
    targs.style = args.small and 'font-size: 90%;'
    targs.separator = args.separator or 'dot'
   
    if numArgsExist == false then
        return nil -- Don't return a toolbar if no numeric arguments exist.
    else
        return ToolbarBuilder.main(targs)
    end
end


-- This function finds whether a string is a valid interwiki project prefix.
function p.validateProjectCode(s)
-- If the string is valid, the function outputs two values: true, and the site code
-- Validates a project code, by seeing whether it is present in
-- used in [[Module:InterwikiTable]]. If the string is valid, the function outputs
-- [[Module:InterwikiTable]]. If it is present, returns the code and the
-- false and nil.
-- InterwikiTable key for the corresponding site. If not present,
local function isKnownProject(prefix)
-- returns nil for both.
     for projectCode, projectVal in pairs(interwikiTable) do
interwikiTable = interwikiTable or mw.loadData('Module:InterwikiTable')
         for _, iwCode in ipairs(projectVal.iw_prefix) do
     for key, t in pairs(interwikiTable) do
             if iwCode == prefix then
         for i, prefix in ipairs(t.iw_prefix) do
                 return true, projectCode
             if s == prefix then
                 return s, key
             end
             end
         end
         end
     end
     end
     return false, nil
     return nil, nil
end
end


local function generateUserDataStrings(args)
--------------------------------------------------------------------------------
    -- If the username is absent or blank, return an error and a tracking category.
-- Main functions
    if args.user == '' or (not args.user and (not args.User or args.User == '')) then
--------------------------------------------------------------------------------
        return err('no username detected', 'No username detected')
    else
        u.username = args.user or args.User
    end
   
    -- Get other basic user data strings.
    u.project = args.Project or args.project
    u.lang = args.lang or args.Lang
    if u.lang then
        if mw.language.isKnownLanguageTag(u.lang) then
            table.insert(trackingCategories, '[[Category:UserLinks transclusions with language parameters]]')
        else
            return err('"' .. u.lang .. '" is not a valid language code', 'Not a valid language code')
        end
    end
   
    -- Process the project value if it is present.
    if u.project then
        table.insert( trackingCategories, '[[Category:UserLinks transclusions with project parameters]]' )
       
        -- If u.project is a known project, we only need to get the project code. If the project
        -- isn't known, first check whether it is a valid language code, and if not then see if it's
        -- an interwiki code separated by colons, e.g. "wikt:es".
        local uprojectIsKnownProject, uprojectProjectCode = isKnownProject(u.project)
        if uprojectIsKnownProject then
            u.projectCode = uprojectProjectCode
        else
            if mw.language.isKnownLanguageTag(u.project) then
                u.lang = u.project
                u.project = nil
            else
                local pref1, pref2 = mw.ustring.match( u.project, '^(%w+):(%w+)$' )
                if pref1 and pref2 then
                    local pref1IsKnownProject, pref1ProjectCode = isKnownProject(pref1)
                    local pref2IsKnownProject, pref2ProjectCode = isKnownProject(pref2)
                    if pref1IsKnownProject
                        and mw.language.isKnownLanguageTag(pref2)
                        and interwikiTable[pref1ProjectCode].takes_lang_prefix then
                        u.project = pref1
                        u.lang = pref2
                        u.projectCode = pref1ProjectCode
                        table.insert(
                            trackingCategories,
                            '[[Category:UserLinks transclusions with project parameters containing language codes]]'
                        )
                    elseif pref2IsKnownProject
                        and mw.language.isKnownLanguageTag(pref1)
                        and interwikiTable[pref2ProjectCode].takes_lang_prefix then
                        u.project = pref2
                        u.lang = pref1
                        u.projectCode = pref2ProjectCode
                        table.insert(
                            trackingCategories,
                            '[[Category:UserLinks transclusions with project parameters containing language codes]]'
                        )
                    else
                        return err(
                            '"' .. u.project .. '" is not a valid interwiki prefix',
                            'Not a valid interwiki prefix'
                        )
                    end
                else
                    return err(
                        '"' .. u.project .. '" is not a valid interwiki prefix',
                        'Not a valid interwiki prefix'
                    )
                end
            end
        end
    end
   
    -- Generate the interwiki prefix. This includes colons.
    if u.project or u.lang then
        u.interwiki = ''
        if u.project then
            u.interwiki = u.interwiki .. ':' .. u.project
        end
        if u.lang then
            u.interwiki = u.interwiki .. ':' .. u.lang
        end
        u.interwiki = u.interwiki .. ':'
    else
        u.interwiki = ''
    end


    -- Generate the other helper strings.
local function makeInvokeFunction(funcName)
    u.usernameHtml = mw.uri.encode(u.username) -- Html-encoded username. Spaces are encoded as pluses.
-- Makes a function that can be accessed from #invoke. This is only required
    if u.project then
-- for functions that need to access arguments.
        local prefixes = interwikiTable[u.projectCode].iw_prefix
return function (frame)
        u.projectLong = prefixes[2] or prefixes[1] -- A bit of a hack, but should find the long prefix name most of the time.
mArguments = require('Module:Arguments')
    else
local args = mArguments.getArgs(frame)
        u.projectLong = 'wikipedia'
return p[funcName](args)
    end
end
    u.toolLang = u.lang or 'en' -- set the default language for tools on the toolserver or labs.
end
end


local function generateTrackingCategories()
p.main = makeInvokeFunction('_main')
    if demo == 'yes' then
 
        return ''
function p._main(args)
    else
-- The main function. This is the one called from [[Template:User-multi]],
        return table.concat(trackingCategories)
-- via p.main.
    end
local options = p.getOptions(args)
local snippets = p.getSnippets(args)
local codes = p.getCodes(args)
local links = p.getLinks(snippets)
-- Overload the built-in Lua error function to generate wikitext errors
-- meant for end users to see. This makes things harder to debug when
-- real errors occur, but it is the only realistic way to show wikitext
-- errors and and still have sane code when using metatables, etc.
local success, result = pcall(p.export, codes, links, options)
if success then
return result
else
return makeWikitextError(result, options.isDemo)
end
end
end


-- This function generates a table of all available link types, with their previews.
function p.getOptions(args)
-- It is used in the module documentation.
-- Gets the options from the args table, so that we don't have to pass
local function getLinkTable(args)
-- around the whole args table all the time.
    demo = args.demo -- Set the demo variable.
local options = {}
    -- Generate the user data strings and return any errors.
options.isDemo = yesno(args.demo) or false
    local dataStringError = generateUserDataStrings(args)
options.toolbarStyle = yesno(args.small) and 'font-size: 90%;' or nil
    if dataStringError then
options.sup = yesno(args.sup, true)
        return dataStringError
options.separator = args.separator
    end
options.span = args.span
   
return options
    -- Build a table of all of the links.
    local result = '<table class="wikitable plainlinks sortable">'
        .. '\n<tr><th>Code</th><th>Preview</th></tr>'
    for i, value in ipairs(linktypes) do
        local code = value[1]
        result = result .. "\n<tr><td>'''" .. code .. "'''</td><td>" .. getLink(code) .. '</td></tr>'
    end
    result = result .. '\n</table>'
   
    return result
end
end


local function getSingleLink(args)
function p.getCodes(args)
    demo = args.demo -- Set the demo variable.
-- Gets the link codes from the arguments. The codes aren't validated
    -- Generate the user data strings and return any errors.
-- at this point.
    local dataStringError = generateUserDataStrings(args)
mTableTools = maybeLoadModule('Module:TableTools')
    if dataStringError then
local codes
        return dataStringError
if mTableTools then
    end
codes = mTableTools.compressSparseArray(args)
   
else
    local linktype = args[1]
codes = {}
    if not linktype then  
for i, code in ipairs(args) do
        return err('no link type specified')
codes[i] = code
    end
end
    local result = getLink(linktype)
end
    result = result .. generateTrackingCategories()
return codes
    return result
end
end


local function getLinks(args)
function p.export(codes, links, options)
    demo = args.demo -- Set the demo variable.
-- Make the user link.
    -- Generate the user data strings and return any errors.
local userLink = links.u
    local dataStringError = generateUserDataStrings(args)
 
    if dataStringError then
-- If we weren't passed any link codes, just return the user link.
        return dataStringError
if #codes < 1 then
    end  
return userLink
   
end
    -- Build the template output.
 
    local result = makeToolbar(args) -- Get the toolbar contents.
-- Make the toolbar.
    if result then
mToolbar = require('Module:Toolbar')
        if args.sup then
local toolbarArgs = {}
            result = '<sup>' .. result .. '</sup>'
for i, code in ipairs(codes) do
        end
local link = links[code]
        result = '&nbsp;' .. result
toolbarArgs[#toolbarArgs + 1] = link
    else
end
        result = '' -- If there are no links specified, don't return the toolbar at all.
toolbarArgs.style = options.toolbarStyle
    end
toolbarArgs.separator = options.separator or 'dot'
    result = '<span>' .. makeUserLink() .. result .. '</span>'
toolbarArgs.span = options.span
    result = result .. generateTrackingCategories()
local toolbar = mToolbar.main(toolbarArgs)
   
 
    return result
-- Apply the sup option.
if options.sup then
toolbar = '<sup>' .. toolbar .. '</sup>'
end
-- If we are transcluding, add a non-breaking space, but if we are substing
-- just use a normal space
local space = mw.isSubsting() and ' ' or '&nbsp;'
return userLink .. space .. toolbar
end
end


local function getExampleLinks(args)
--------------------------------------------------------------------------------
    -- This function enables example output without having to specify any
-- Single link function
    -- parameters to #invoke.
--------------------------------------------------------------------------------
    args.demo = 'yes'
 
    args.user = 'Example'
p.single = makeInvokeFunction('_single')
    args.User = nil
 
    return getLinks(args)
function p._single(args)
-- Fetches a single link from the link table.
local options = p.getOptions(args)
local snippets = p.getSnippets(args)
local links = p.getLinks(snippets)
local code = args[1]
local success, link = pcall(p.exportSingle, links, code)
if success then
return link
else
return makeWikitextError(link, options.isDemo)
end
end
end


local function makeWrapper(func)
function p.exportSingle(links, code)
    return function (frame)
-- If any errors occur, they will probably occur here. This function
        -- If called via #invoke, use the args passed into the invoking template.
-- exists purely so that all the errors that will occur in p._single can
        -- Otherwise, for testing purposes, assume args are being passed directly in.
-- be handled using a single pcall.
        local origArgs
if not code then
        if frame == mw.getCurrentFrame() then
raiseError(
            origArgs = frame:getParent().args
message('error-nolinkcode'),
            for k, v in pairs(frame.args) do
message('error-nolinkcode-section')
                origArgs = frame.args
)
                break
end
            end
return links[code]
        else
            origArgs = frame
        end
        -- Strip whitespace, and treat blank arguments as nil.
        -- 'user', 'User', and 'separator' have different behaviour depending on
        -- whether they are blank or nil, so keep them as they are.
        local args = {}
        for k, v in pairs(origArgs) do
            v = mw.text.trim(v)
            if v ~= '' or k == 'user' or k == 'User' or k == 'separator' then
                args[k] = v
            end
        end
   
        return func(args)
    end
end
end


return {
--------------------------------------------------------------------------------
    main = makeWrapper(getLinks),
-- Link table
    single = makeWrapper(getSingleLink),
--------------------------------------------------------------------------------
    linktable = makeWrapper(getLinkTable),
 
    example = makeWrapper(getExampleLinks)
function p.linktable()
}
-- Returns a wikitext table of link codes, with an example link for each
-- one. This function doesn't take any arguments, so it can be accessed
-- directly from wiki pages without using makeInvokeFunction.
local args = {user = 'Example'}
local snippets = p.getSnippets(args)
local links = p.getLinks(snippets)
 
-- Assemble the codes and links in order
local firstCodes = {'u', 't', 'c'}
local firstLinks, firstCodesKeys = {}, {}
for i, code in ipairs(firstCodes) do
firstCodesKeys[code] = true
firstLinks[#firstLinks + 1] = {code, links[code]}
end
local secondLinks = {}
for code, link in pairs(links) do
if not firstCodesKeys[code] then
secondLinks[#secondLinks + 1] = {code, link}
end
end
table.sort(secondLinks, function(t1, t2)
return t1[1] < t2[1]
end)
local links = {}
for i, t in ipairs(firstLinks) do
links[#links + 1] = t
end
for i, t in ipairs(secondLinks) do
links[#links + 1] = t
end
 
-- Output the code table in table format
local ret = {}
ret[#ret + 1] = '{| class="wikitable plainlinks sortable"'
ret[#ret + 1] = '|-'
ret[#ret + 1] = '! ' .. message('linktable-codeheader')
ret[#ret + 1] = '! ' .. message('linktable-previewheader')
for i, t in ipairs(links) do
local code = t[1]
local link = t[2]
ret[#ret + 1] = '|-'
ret[#ret + 1] = "| '''" .. code .. "'''"
ret[#ret + 1] = '| ' .. link
end
ret[#ret + 1] = '|}'
return table.concat(ret, '\n')
end
 
return p

Latest revision as of 09:37, 16 April 2014


-- UserLinks -- -- This module creates a list of links about a given user. It can be used on -- -- its own or from a template. See the /doc page for more documentation. --


-- Require necessary modules local yesno = require('Module:Yesno')

-- Lazily initialise modules that we might or might not need local mExtra -- Module:UserLinks/extra local mArguments -- Module:Arguments local mToolbar -- Module:Toolbar local mCategoryHandler -- Module:Category handler local mTableTools -- Module:TableTools local interwikiTable -- Module:InterwikiTable, loaded with mw.loadData

-- Load shared helper functions local mShared = require('Module:UserLinks/shared') local raiseError = mShared.raiseError local maybeLoadModule = mShared.maybeLoadModule local makeWikitextError = mShared.makeWikitextError local makeWikilink = mShared.makeWikilink local makeUrlLink = mShared.makeUrlLink local makeFullUrlLink = mShared.makeFullUrlLink local message = mShared.message

local p = {}


-- Link table


function p.getLinks(snippets) --[=[ -- Get a table of links that can be indexed with link codes. The table -- returned is blank, but links are added to it on demand when it is -- indexed. This is made possible by the metatable and by the various link -- functions, some of which are defined here, and some of which are defined -- at Module:UserLinks/extra. --]=] local links, linkFunctions = {}, {}

---------------------------------------------------------------------------- -- Link functions -- -- The following functions make the links from the link codes and the user -- data snippets. New link functions should be added below the existing -- functions. -- -- For documentation on how to add new link functions, please see -- Module:UserLinks#Adding new links. ----------------------------------------------------------------------------

function linkFunctions.u(snippets) -- User page return makeWikilink( snippets.interwiki, 2, snippets.username, snippets.username ) end

function linkFunctions.t(snippets) -- User talk page return makeWikilink( snippets.interwiki, 3, snippets.username, message('display-talk') ) end

function linkFunctions.c(snippets) -- Contributions return makeWikilink( snippets.interwiki, -1, 'Contributions/' .. snippets.username, message('display-contributions') ) end

function linkFunctions.ct(snippets) -- Edit count return makeUrlLink( { host = 'tools.wmflabs.org', path = '/supercount/index.php', query = { user = snippets.username, project = snippets.toolLang .. '.' .. snippets.projectLong } }, message('display-count') ) end

function linkFunctions.m(snippets) -- Page moves return makeWikilink( snippets.interwiki, -1, 'Log/move/' .. snippets.username, message('display-moves') ) end

function linkFunctions.l(snippets) -- Logs return makeWikilink( snippets.interwiki, -1, 'Log/' .. snippets.username, message('display-logs') ) end

function linkFunctions.bl(snippets) -- Block log return makeFullUrlLink( snippets.interwiki, -1, 'Log/block', {page = 'User:' .. snippets.username}, message('display-blocklog') ) end

function linkFunctions.bls(snippets) -- Blocks return makeWikilink( snippets.interwiki, -1, 'Log/block/' .. snippets.username, message('display-blocks') ) end

function linkFunctions.bu(snippets) -- Block user return makeWikilink( snippets.interwiki, -1, 'Block/' .. snippets.username, message('display-blockuser') ) end

function linkFunctions.ca(snippets) -- Central auth return makeWikilink( snippets.interwiki, -1, 'CentralAuth/' .. snippets.username, message('display-centralauth') ) end

function linkFunctions.dc(snippets) -- Deleted contribs return makeWikilink( snippets.interwiki, -1, 'DeletedContributions/' .. snippets.username, message('display-deletedcontributions') ) end

function linkFunctions.e(snippets) -- Email return makeWikilink( snippets.interwiki, -1, 'EmailUser/' .. snippets.username, message('display-email') ) end

function linkFunctions.es(snippets) -- Edit summaries return makeUrlLink( { host = 'tools.wmflabs.org', path = '/xtools/editsummary/index.php', query = { name = snippets.username, lang = snippets.toolLang, wiki = snippets.projectLong } }, message('display-editsummaries') ) end

function linkFunctions.del(snippets) -- Deletions return makeWikilink( snippets.interwiki, -1, 'Log/delete/' .. snippets.username, message('display-deletions') ) end

function linkFunctions.lu(snippets) -- List user return makeFullUrlLink( snippets.interwiki, -1, 'ListUsers', {limit = 1, username = snippets.username}, message('display-listuser') ) end

function linkFunctions.sul(snippets) -- SUL return makeWikilink( nil, nil, 'sulutil:' .. snippets.username, message('display-sul') ) end

function linkFunctions.tl(snippets) -- Target logs return makeFullUrlLink( snippets.interwiki, -1, 'Log', {page = mw.site.namespaces[2].name .. ':' .. snippets.username}, message('display-targetlogs') ) end

function linkFunctions.efl(snippets) -- Edit filter log return makeFullUrlLink( snippets.interwiki, -1, 'AbuseLog', {wpSearchUser = snippets.username}, message('display-abuselog') ) end

function linkFunctions.pr(snippets) -- Protections return makeWikilink( snippets.interwiki, -1, 'Log/protect/' .. snippets.username, message('display-protections') ) end

function linkFunctions.rl(snippets) -- User rights return makeWikilink( snippets.interwiki, -1, 'Log/rights/' .. snippets.username, message('display-rights') ) end

function linkFunctions.ren(snippets) -- Renames return makeWikilink( snippets.interwiki, -1, 'Log/renameuser/' .. snippets.username, message('display-renames') ) end

function linkFunctions.rfa(snippets) -- Requests for adminship return makeWikilink( nil, -1, 'PrefixIndex/' .. message('page-rfa') .. '/' .. snippets.username, message('display-rfa') ) end

function linkFunctions.api(snippets) -- API user data return makeUrlLink( { host = snippets.fullDomain, path = '/w/api.php', query = { action = 'query', list = 'users', usprop = 'groups|editcount', ususers = snippets.username } }, message('display-api') ) end

function linkFunctions.up(snippets) -- Uploads return makeWikilink( snippets.interwiki, -1, 'ListFiles/' .. snippets.username, message('display-uploads') ) end

---------------------------------------------------------------------------- -- End of link functions ----------------------------------------------------------------------------

-- Define the metatable that memoizes the link functions, and fetches link -- functions from Module:UserLinks/extra if necessary.

-- Lazily initialise the extraLinkFunctions table. We only want to load -- Module:UserLinks/extra as necessary, so it has a low transclusion -- count. local extraLinkFunctions

-- Define functions for shared code in the metatable. local function validateCode(code) -- Checks whether code is a valid link code - i.e. checks that it is a -- string and that it is not the blank string. Returns the code if -- the check passes, and nil if not. if type(code) == 'string' and code ~= then return code else return nil end end

local function getExtraLinkFunctions() -- Loads the table of extra link functions from the /extra module. -- If there is a problem with loading it, return false. We use the -- distinction between false and nil to record whether we have already -- tried to load it. if extraLinkFunctions ~= nil then return extraLinkFunctions end if mExtra == nil then -- If loading the module fails, maybeLoadModule returns false. -- Here we use the distinction between false and nil to record -- whether we have already tried to load the /extra module. mExtra = maybeLoadModule('Module:UserLinks/extra') end if type(mExtra) == 'table' and type(mExtra.linkFunctions) == 'table' then extraLinkFunctions = mExtra.linkFunctions else extraLinkFunctions = false end return extraLinkFunctions end

local function memoizeExtraLink(code, func) local success, link = pcall(func, snippets) if success and type(link) == 'string' then links[code] = link return link end return nil end

-- Define the metatable. setmetatable(links, { __index = function (t, key) local code = validateCode(key) if not code then raiseError( message('error-malformedlinkcode'), message('error-malformedlinkcode-section') ) end local linkFunction = linkFunctions[code] local link if linkFunction then link = linkFunction(snippets) links[code] = link else extraLinkFunctions = getExtraLinkFunctions() if extraLinkFunctions then local extraLinkFunction = extraLinkFunctions[code] if type(extraLinkFunction) == 'function' then link = memoizeExtraLink(code, extraLinkFunction) end end end if link then return link else raiseError( message('error-invalidlinkcode', code), message('error-invalidlinkcode-section') ) end end, __pairs = function () extraLinkFunctions = getExtraLinkFunctions() if extraLinkFunctions then for code, func in pairs(extraLinkFunctions) do if validateCode(code) and type(func) == 'function' then memoizeExtraLink(code, func) end end end -- Allow built-in functions to overwrite extra functions. for code, func in pairs(linkFunctions) do local link = func(snippets) links[code] = link end return function (t, key) return next(links, key) end end }) return links end


-- User data snippets


function p.getSnippets(args) --[=[ -- This function gets user data snippets from the arguments, and from -- Module:InterwikiTable. The data is loaded as necessary and memoized -- in the snippets table for performance. -- -- Snippets default to the blank string, , so they can be used in -- concatenation operations without coders having to worry about raising -- errors. Because of this, the local functions snippetExists and -- getSnippet have been written to aid people writing new snippets. These -- functions treat the blank string as false. It is not necessary to return -- the blank string from a snippet function, as nil and false values are -- automatically converted into the blank string by the metatable. -- -- If you add a new snippet, please document it at -- Module:UserLinks#Adding new links. --]=] local snippets, snippetFunctions = {}, {} setmetatable(snippets, { __index = function (t, key) local snippetFunction = snippetFunctions[key] if snippetFunction then snippets[key] = snippetFunction() or return snippets[key] else raiseError( message('error-nosnippet', key), message('error-nosnippet-section') ) end end })

-- Define helper functions for writting the snippet functions. local function snippetExists(key) -- We have set the metatable up to make snippets default to , so we -- don't have to test for false or nil. return snippets[key] ~= end

local function getSnippet(key) local ret = snippets[key] if ret == then return nil else return ret end end

-- Start snippet functions.

function snippetFunctions.username() -- The username. local username = args.user or args.User return username or raiseError( message('error-nousername'), message('error-nousername-section') ) end

function snippetFunctions.usernameHtml() -- The username html-encoded. Spaces are encoded as pluses. return mw.uri.encode(snippets.username) end

function snippetFunctions.project() -- The project name. -- Also does the work for snippetFunctions.interwikiTableKey, and adds -- the project value to snippets.lang if it is a valid language code. local project = args.Project or args.project if not project then return nil end local projectValidated, interwikiTableKey = p.validateProjectCode(project) if not projectValidated then if mw.language.isKnownLanguageTag(project) then if not snippetExists('lang') then snippets.lang = project end else raiseError( message('error-invalidproject', project), message('error-invalidproject-section') ) end end snippets.interwikiTableKey = interwikiTableKey return project end

function snippetFunctions.interwikiTableKey() -- The key for the project in Module:InterwikiTable. -- Relies on snippetFunctions.project to do the real work. local temp = snippets.project -- required; puts key in snippets table return rawget(snippets, 'interwikiTableKey') end

function snippetFunctions.toolProject() -- The short project code for use with toolserver or labs. It is always -- present, even if the "project" argument is absent. The default value -- is the "snippet-project-default" message. local project = getSnippet('project') if not project then return message('snippet-project-default') else return project end end

function snippetFunctions.projectLong() -- The long form of the project name, e.g. "wikipedia" or "wikibooks". local key = getSnippet('interwikiTableKey') if not key then return message('snippet-projectlong-default') end interwikiTable = interwikiTable or mw.loadData('Module:InterwikiTable') local prefixes = interwikiTable[key].iw_prefix -- Using prefixes[2] is a bit of a hack, but should find the long name -- most of the time. return prefixes[2] or prefixes[1] end

function snippetFunctions.lang() -- The language code. local lang = args.lang or args.Lang if not lang then return nil end if mw.language.isKnownLanguageTag(lang) then return lang else raiseError( message('error-invalidlanguage', lang), message('error-invalidlanguage-section') ) end end

function snippetFunctions.toolLang() -- The language code for use with toolserver or labs tools. It is always -- present, even if the "lang" argument is absent. The default value is -- the "snippet-lang-default" message. return getSnippet('lang') or message('snippet-lang-default') end

function snippetFunctions.interwiki() -- The interwiki prefix, consisting of the project and language values, -- separated by colons, e.g. ":wikt:es:". local project = getSnippet('project') local lang = getSnippet('lang') if not project and not lang then return nil end local ret = {} ret[#ret + 1] = project ret[#ret + 1] = lang return table.concat(ret, ':') end

function snippetFunctions.fullDomain() -- The full domain name of the site, e.g. www.mediawiki.org, -- en.wikpedia.org, or ja.wikibooks.org. local fullDomain local lang = getSnippet('toolLang') local key = getSnippet('interwikiTableKey') if key then interwikiTable = interwikiTable or mw.loadData('Module:InterwikiTable') local domain = interwikiTable[key].domain local takesLangPrefix = interwikiTable[key].takes_lang_prefix if takesLangPrefix then fullDomain = lang .. '.' .. domain else fullDomain = domain end else fullDomain = lang .. '.wikipedia.org' end return fullDomain end

-- End snippet functions. If you add a new snippet function, please -- document it at Module:UserLinks#Adding new links.

return snippets end

function p.validateProjectCode(s) -- Validates a project code, by seeing whether it is present in -- Module:InterwikiTable. If it is present, returns the code and the -- InterwikiTable key for the corresponding site. If not present, -- returns nil for both. interwikiTable = interwikiTable or mw.loadData('Module:InterwikiTable')

   for key, t in pairs(interwikiTable) do
       for i, prefix in ipairs(t.iw_prefix) do
           if s == prefix then
               return s, key
           end
       end
   end
   return nil, nil

end


-- Main functions


local function makeInvokeFunction(funcName) -- Makes a function that can be accessed from #invoke. This is only required -- for functions that need to access arguments. return function (frame) mArguments = require('Module:Arguments') local args = mArguments.getArgs(frame) return p[funcName](args) end end

p.main = makeInvokeFunction('_main')

function p._main(args) -- The main function. This is the one called from Template:User-multi, -- via p.main. local options = p.getOptions(args) local snippets = p.getSnippets(args) local codes = p.getCodes(args) local links = p.getLinks(snippets) -- Overload the built-in Lua error function to generate wikitext errors -- meant for end users to see. This makes things harder to debug when -- real errors occur, but it is the only realistic way to show wikitext -- errors and and still have sane code when using metatables, etc. local success, result = pcall(p.export, codes, links, options) if success then return result else return makeWikitextError(result, options.isDemo) end end

function p.getOptions(args) -- Gets the options from the args table, so that we don't have to pass -- around the whole args table all the time. local options = {} options.isDemo = yesno(args.demo) or false options.toolbarStyle = yesno(args.small) and 'font-size: 90%;' or nil options.sup = yesno(args.sup, true) options.separator = args.separator options.span = args.span return options end

function p.getCodes(args) -- Gets the link codes from the arguments. The codes aren't validated -- at this point. mTableTools = maybeLoadModule('Module:TableTools') local codes if mTableTools then codes = mTableTools.compressSparseArray(args) else codes = {} for i, code in ipairs(args) do codes[i] = code end end return codes end

function p.export(codes, links, options) -- Make the user link. local userLink = links.u

-- If we weren't passed any link codes, just return the user link. if #codes < 1 then return userLink end

-- Make the toolbar. mToolbar = require('Module:Toolbar') local toolbarArgs = {} for i, code in ipairs(codes) do local link = links[code] toolbarArgs[#toolbarArgs + 1] = link end toolbarArgs.style = options.toolbarStyle toolbarArgs.separator = options.separator or 'dot' toolbarArgs.span = options.span local toolbar = mToolbar.main(toolbarArgs)

-- Apply the sup option. if options.sup then toolbar = '' .. toolbar .. '' end

-- If we are transcluding, add a non-breaking space, but if we are substing -- just use a normal space local space = mw.isSubsting() and ' ' or ' '

return userLink .. space .. toolbar end


-- Single link function


p.single = makeInvokeFunction('_single')

function p._single(args) -- Fetches a single link from the link table. local options = p.getOptions(args) local snippets = p.getSnippets(args) local links = p.getLinks(snippets) local code = args[1] local success, link = pcall(p.exportSingle, links, code) if success then return link else return makeWikitextError(link, options.isDemo) end end

function p.exportSingle(links, code) -- If any errors occur, they will probably occur here. This function -- exists purely so that all the errors that will occur in p._single can -- be handled using a single pcall. if not code then raiseError( message('error-nolinkcode'), message('error-nolinkcode-section') ) end return links[code] end


-- Link table


function p.linktable() -- Returns a wikitext table of link codes, with an example link for each -- one. This function doesn't take any arguments, so it can be accessed -- directly from wiki pages without using makeInvokeFunction. local args = {user = 'Example'} local snippets = p.getSnippets(args) local links = p.getLinks(snippets)

-- Assemble the codes and links in order local firstCodes = {'u', 't', 'c'} local firstLinks, firstCodesKeys = {}, {} for i, code in ipairs(firstCodes) do firstCodesKeys[code] = true firstLinks[#firstLinks + 1] = {code, links[code]} end local secondLinks = {} for code, link in pairs(links) do if not firstCodesKeys[code] then secondLinks[#secondLinks + 1] = {code, link} end end table.sort(secondLinks, function(t1, t2) return t1[1] < t2[1] end) local links = {} for i, t in ipairs(firstLinks) do links[#links + 1] = t end for i, t in ipairs(secondLinks) do links[#links + 1] = t end

-- Output the code table in table format local ret = {} ret[#ret + 1] = '{| class="wikitable plainlinks sortable"' ret[#ret + 1] = '|-' ret[#ret + 1] = '! ' .. message('linktable-codeheader') ret[#ret + 1] = '! ' .. message('linktable-previewheader') for i, t in ipairs(links) do local code = t[1] local link = t[2] ret[#ret + 1] = '|-' ret[#ret + 1] = "| " .. code .. "" ret[#ret + 1] = '| ' .. link end ret[#ret + 1] = '|}' return table.concat(ret, '\n') end

return p