Module:UserLinks

----------------------------------------------------------------------------------                                 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 '&nbsp;'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