This module generates links about a given user. It is used to generate templates such as {{user}}, {{user5}}, and {{admin}}, usually through its wrapper template {{user-multi}}.
Functions
Main
The main
function implements the {{user-multi}} template. It generates a list of links about a given user. Please see the template page for documentation.
Single
The single
function generates a single link about a given user. See {{user-multi/link}} for documentation.
Porting to other wikis
If you want to use this module on another wiki, there are a few modules that you must also copy across, and some that can be used but are not essential.
Required modules:
- Module:UserLinks
- Module:UserLinks/shared
- Module:UserLinks/config
- Module:Arguments
- Module:Yesno
- Module:Toolbar
- Module:InterwikiTable
- Module:TableTools (optional in Module:UserLinks, but required by Module:Toolbar)
Optional modules:
- Module:UserLinks/extra - used for testing new link functions before they are moved to the main module.
- Module:Category handler - if an error occurs, and this module is present, pages are not categorised if they match the module's blacklist.
After you have copied over the necessary modules, you should adjust the configuration settings in Module:UserLinks/config for your language and for your wiki's setup.
---------------------------------------------------------------------------------- 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 moduleslocal yesno = require('Module:Yesno')-- Lazily initialise modules that we might or might not needlocal 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 functionslocal mShared = require('Module:UserLinks/shared')local raiseError = mShared.raiseErrorlocal maybeLoadModule = mShared.maybeLoadModulelocal makeWikitextError = mShared.makeWikitextErrorlocal makeWikilink = mShared.makeWikilinklocal makeUrlLink = mShared.makeUrlLinklocal makeFullUrlLink = mShared.makeFullUrlLinklocal message = mShared.messagelocal 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.----------------------------------------------------------------------------function linkFunctions.u(snippets)-- User pagereturn makeWikilink(snippets.interwiki,2,snippets.username,snippets.username)endfunction linkFunctions.np(snippets)-- User page (no ping)return '<span class="plainlinks">' .. makeFullUrlLink(snippets.interwiki,2,snippets.username,'',snippets.username) .. '</span>'endfunction linkFunctions.t(snippets)-- User talk pagereturn makeWikilink(snippets.interwiki,3,snippets.username,message('display-talk'))endfunction linkFunctions.c(snippets)-- Contributionsreturn makeWikilink(snippets.interwiki,-1,'Contribs/' .. snippets.username,message('display-contributions'))endfunction linkFunctions.c64(snippets)-- Contributionslocal first64 = snippets.username:match('^%x+:%x+:%x+:%x+:')or snippets.username:match('^%x+:%x+:%x+:')or snippets.username:match('^%x+:%x+:')or snippets.username:match('^%x+:')return first64 and makeWikilink(snippets.interwiki,-1,'Contribs/' .. first64 .. ':/64','(/64)') or ''endfunction linkFunctions.ct(snippets)-- Edit countreturn makeUrlLink({host = 'xtools.wmflabs.org',path = '/ec/',query = {username = snippets.username,project = snippets.toolLang .. '.' .. snippets.projectLong .. '.org'}},message('display-count'))endfunction linkFunctions.m(snippets)-- Page movesreturn makeWikilink(snippets.interwiki,-1,'Log/move/' .. snippets.username,message('display-moves'))endfunction linkFunctions.l(snippets)-- Logsreturn makeWikilink(snippets.interwiki,-1,'Log/' .. snippets.username,message('display-logs'))endfunction linkFunctions.ae(snippets)-- Automated edits (and non-automated contributions).return makeUrlLink({host = 'xtools.wmflabs.org',path = '/autoedits/',query = {username = snippets.username,project = snippets.toolLang .. '.' .. snippets.projectLong .. '.org'}},message('display-autoedits'))endfunction linkFunctions.bl(snippets)-- Block logreturn makeFullUrlLink(snippets.interwiki,-1,'Log/block',{page = 'User:' .. snippets.username},message('display-blocklog'))endfunction linkFunctions.bls(snippets)-- Blocksreturn makeWikilink(snippets.interwiki,-1,'Log/block/' .. snippets.username,message('display-blocks'))endfunction linkFunctions.bu(snippets)-- Block userreturn makeWikilink(snippets.interwiki,-1,'Block/' .. snippets.username,message('display-blockuser'))endfunction linkFunctions.ca(snippets)-- Central authreturn makeWikilink(snippets.interwiki,-1,'CentralAuth/' .. snippets.username,message('display-centralauth'))endfunction linkFunctions.dc(snippets)-- Deleted contribsreturn makeWikilink(snippets.interwiki,-1,'DeletedContributions/' .. snippets.username,message('display-deletedcontributions'))endfunction linkFunctions.e(snippets)-- Emailreturn makeWikilink(snippets.interwiki,-1,'EmailUser/' .. snippets.username,message('display-email'))endfunction linkFunctions.es(snippets)-- Edit summariesreturn makeUrlLink({host = 'xtools.wmflabs.org',path = '/editsummary/',query = {username = snippets.username,project = snippets.toolLang .. '.' .. snippets.projectLong .. '.org'}},message('display-editsummaries'))endfunction linkFunctions.del(snippets)-- Deletionsreturn makeWikilink(snippets.interwiki,-1,'Log/delete/' .. snippets.username,message('display-deletions'))endfunction linkFunctions.lu(snippets)-- List userreturn makeFullUrlLink(snippets.interwiki,-1,'ListUsers',{limit = 1, username = snippets.username},message('display-listuser'))endfunction linkFunctions.sul(snippets)-- SULreturn makeWikilink(nil,nil,'sulutil:' .. snippets.username,message('display-sul'))endfunction linkFunctions.tl(snippets)-- Target logsreturn makeFullUrlLink(snippets.interwiki,-1,'Log',{page = mw.site.namespaces[2].name .. ':' .. snippets.username},message('display-targetlogs'))endfunction linkFunctions.efl(snippets)-- Edit filter logreturn makeFullUrlLink(snippets.interwiki,-1,'AbuseLog',{wpSearchUser = snippets.username},message('display-abuselog'))endfunction linkFunctions.pr(snippets)-- Protectionsreturn makeWikilink(snippets.interwiki,-1,'Log/protect/' .. snippets.username,message('display-protections'))endfunction linkFunctions.rl(snippets)-- User rightsreturn makeWikilink(snippets.interwiki,-1,'Log/rights/' .. snippets.username,message('display-rights'))endfunction linkFunctions.ren(snippets)-- Renamesreturn makeWikilink(snippets.interwiki,-1,'Log/renameuser/' .. snippets.username,message('display-renames'))endfunction linkFunctions.rfa(snippets)-- Requests for adminshipreturn makeWikilink(nil,-1,'PrefixIndex/' .. message('page-rfa') .. '/' .. snippets.username,message('display-rfa'))endfunction linkFunctions.api(snippets)-- API user datareturn makeUrlLink({host = snippets.fullDomain,path = '/w/api.php',query = {action = 'query',list = 'users',usprop = 'groups|editcount',ususers = snippets.username}},message('display-api'))endfunction linkFunctions.up(snippets)-- Uploadsreturn makeWikilink(snippets.interwiki,-1,'ListFiles/' .. snippets.username,message('display-uploads'))endfunction linkFunctions.nuke(snippets)-- Mass delete/Special:Nukereturn makeWikilink(snippets.interwiki,-1,'Nuke/' .. snippets.username,message('display-nuke'))endfunction linkFunctions.gender(snippets)-- Genderreturn mw.getCurrentFrame():callParserFunction('GENDER',snippets.username,'he/him','she/her','they/them')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 ~= '' thenreturn codeelsereturn nilendendlocal 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 thenreturn extraLinkFunctionsendif 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')endif type(mExtra) == 'table'and type(mExtra.linkFunctions) == 'table'thenextraLinkFunctions = mExtra.linkFunctionselseextraLinkFunctions = falseendreturn extraLinkFunctionsendlocal function memoizeExtraLink(code, func)local success, link = pcall(func, snippets)if success and type(link) == 'string' thenlinks[code] = linkreturn linkendreturn nilend-- Define the metatable.setmetatable(links, {__index = function (t, key)local code = validateCode(key)if not code thenraiseError(message('error-malformedlinkcode'),message('error-malformedlinkcode-section'))endlocal linkFunction = linkFunctions[code]local linkif linkFunction thenlink = linkFunction(snippets)links[code] = linkelseextraLinkFunctions = getExtraLinkFunctions()if extraLinkFunctions thenlocal extraLinkFunction = extraLinkFunctions[code]if type(extraLinkFunction) == 'function' thenlink = memoizeExtraLink(code, extraLinkFunction)endendendif link thenreturn linkelseraiseError(message('error-invalidlinkcode', code),message('error-invalidlinkcode-section'))endend,__pairs = function ()extraLinkFunctions = getExtraLinkFunctions()if extraLinkFunctions thenfor code, func in pairs(extraLinkFunctions) doif validateCode(code) and type(func) == 'function' thenmemoizeExtraLink(code, func)endendend-- Allow built-in functions to overwrite extra functions.for code, func in pairs(linkFunctions) dolocal link = func(snippets)links[code] = linkendreturn function (t, key)return next(links, key)endend})return linksend---------------------------------------------------------------------------------- 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 thensnippets[key] = snippetFunction() or ''return snippets[key]elseraiseError(message('error-nosnippet', key),message('error-nosnippet-section'))endend})-- 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] ~= ''endlocal function getSnippet(key)local ret = snippets[key]if ret == '' thenreturn nilelsereturn retendend-- Start snippet functions.function snippetFunctions.username()-- The username.local username = args.user or args.Userreturn username or raiseError(message('error-nousername'),message('error-nousername-section'))endfunction snippetFunctions.usernameHtml()-- The username html-encoded. Spaces are encoded as pluses.return mw.uri.encode(snippets.username)endfunction 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.projectif not project thenreturn nilendlocal projectValidated, interwikiTableKey = p.validateProjectCode(project)if not projectValidated thenif mw.language.isKnownLanguageTag(project) thenif not snippetExists('lang') thensnippets.lang = projectendelseraiseError(message('error-invalidproject', project),message('error-invalidproject-section'))endendsnippets.interwikiTableKey = interwikiTableKeyreturn projectendfunction 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 tablereturn rawget(snippets, 'interwikiTableKey')endfunction 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 thenreturn message('snippet-project-default')elsereturn projectendendfunction snippetFunctions.projectLong()-- The long form of the project name, e.g. "wikipedia" or "wikibooks".local key = getSnippet('interwikiTableKey')if not key thenreturn message('snippet-projectlong-default')endinterwikiTable = 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] endfunction snippetFunctions.lang()-- The language code.local lang = args.lang or args.Langif not lang thenreturn nilendif mw.language.isKnownLanguageTag(lang) thenreturn langelseraiseError(message('error-invalidlanguage', lang),message('error-invalidlanguage-section'))endendfunction 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')endfunction 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 thenreturn nilendlocal ret = {}ret[#ret + 1] = projectret[#ret + 1] = langreturn table.concat(ret, ':')endfunction snippetFunctions.fullDomain()-- The full domain name of the site, e.g. www.mediawiki.org,-- en.wikpedia.org, or ja.wikibooks.org.local fullDomainlocal lang = getSnippet('toolLang')local key = getSnippet('interwikiTableKey')if key theninterwikiTable = interwikiTable or mw.loadData('Module:InterwikiTable')local domain = interwikiTable[key].domainlocal takesLangPrefix = interwikiTable[key].takes_lang_prefixif takesLangPrefix thenfullDomain = lang .. '.' .. domainelsefullDomain = domainendelsefullDomain = lang .. '.wikipedia.org'endreturn fullDomainend-- End snippet functions. If you add a new snippet function, please-- document it at [[Module:UserLinks#Adding new links]].return snippetsend 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) dofor i, prefix in ipairs(t.iw_prefix) doif s == prefix thenreturn s, keyendendendreturn nil, nilend---------------------------------------------------------------------------------- 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)endendp.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 thenreturn resultelsereturn makeWikitextError(result, options.isDemo)endendfunction 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 falseoptions.noPing = yesno(args.noPing) or yesno(args.noping) or yesno(args.np) or falseoptions.toolbarStyle = yesno(args.small) and 'font-size: 90%;' or niloptions.sup = yesno(args.sup, true)options.separator = args.separatoroptions.span = args.spanreturn optionsendfunction p.getCodes(args)-- Gets the link codes from the arguments. The codes aren't validated-- at this point.mTableTools = maybeLoadModule('Module:TableTools')local codesif mTableTools thencodes = mTableTools.compressSparseArray(args)elsecodes = {}for i, code in ipairs(args) docodes[i] = codeendendreturn codesendfunction p.export(codes, links, options)-- Make the user link.local userLink = options.noPing and links.np or links.u-- If we weren't passed any link codes, just return the user link.if #codes < 1 thenreturn userLinkend-- Make the toolbar.mToolbar = require('Module:Toolbar')local toolbarArgs = {}for i, code in ipairs(codes) dolocal link = links[code]toolbarArgs[#toolbarArgs + 1] = linkendtoolbarArgs.style = options.toolbarStyletoolbarArgs.separator = options.separator or 'dot'toolbarArgs.span = options.spanlocal toolbar = mToolbar.main(toolbarArgs)-- Apply the sup option.if options.sup thentoolbar = '<sup>' .. toolbar .. '</sup>'end-- If we are transcluding, add a non-breaking space, but if we are substing-- just use a normal spacelocal space = mw.isSubsting() and ' ' or ' 'return userLink .. space .. toolbarend---------------------------------------------------------------------------------- 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 thenreturn linkelsereturn makeWikitextError(link, options.isDemo)endendfunction 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 thenraiseError(message('error-nolinkcode'),message('error-nolinkcode-section'))endreturn links[code]endreturn p