---------------------------------------------------------------------------------- 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 moduleslocalyesno=require('Module:Yesno')-- Lazily initialise modules that we might or might not needlocalmExtra-- [[Module:UserLinks/extra]]localmArguments-- [[Module:Arguments]]localmToolbar-- [[Module:Toolbar]]localmCategoryHandler-- [[Module:Category handler]]localmTableTools-- [[Module:TableTools]]localinterwikiTable-- [[Module:InterwikiTable]], loaded with mw.loadData-- Load shared helper functionslocalmShared=require('Module:UserLinks/shared')localraiseError=mShared.raiseErrorlocalmaybeLoadModule=mShared.maybeLoadModulelocalmakeWikitextError=mShared.makeWikitextErrorlocalmakeWikilink=mShared.makeWikilinklocalmakeUrlLink=mShared.makeUrlLinklocalmakeFullUrlLink=mShared.makeFullUrlLinklocalmessage=mShared.messagelocalp={}---------------------------------------------------------------------------------- Link table--------------------------------------------------------------------------------functionp.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]].--]=]locallinks,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.----------------------------------------------------------------------------functionlinkFunctions.u(snippets)-- User pagereturnmakeWikilink(snippets.interwiki,2,snippets.username,snippets.username)endfunctionlinkFunctions.np(snippets)-- User page (no ping)return'<span class="plainlinks">'..makeFullUrlLink(snippets.interwiki,2,snippets.username,'',snippets.username)..'</span>'endfunctionlinkFunctions.t(snippets)-- User talk pagereturnmakeWikilink(snippets.interwiki,3,snippets.username,message('display-talk'))endfunctionlinkFunctions.c(snippets)-- ContributionsreturnmakeWikilink(snippets.interwiki,-1,'Contribs/'..snippets.username,message('display-contributions'))endfunctionlinkFunctions.c64(snippets)-- Contributionslocalfirst64=snippets.username:match('^%x+:%x+:%x+:%x+:')orsnippets.username:match('^%x+:%x+:%x+:')orsnippets.username:match('^%x+:%x+:')orsnippets.username:match('^%x+:')returnfirst64andmakeWikilink(snippets.interwiki,-1,'Contribs/'..first64..':/64','(/64)')or''endfunctionlinkFunctions.ct(snippets)-- Edit countreturnmakeWikilink('xtools',0,'ec/'..snippets.toolLang..'.'..snippets.projectLong..'.org/'..snippets.username,message('display-count'))endfunctionlinkFunctions.m(snippets)-- Page movesreturnmakeWikilink(snippets.interwiki,-1,'Log/move/'..snippets.username,message('display-moves'))endfunctionlinkFunctions.l(snippets)-- LogsreturnmakeWikilink(snippets.interwiki,-1,'Log/'..snippets.username,message('display-logs'))endfunctionlinkFunctions.ae(snippets)-- Automated edits (and non-automated contributions).returnmakeWikilink('xtools',0,'autoedits/'..snippets.toolLang..'.'..snippets.projectLong..'.org/'..snippets.username,message('display-autoedits'))endfunctionlinkFunctions.bl(snippets)-- Block logreturnmakeFullUrlLink(snippets.interwiki,-1,'Log/block',{page='User:'..snippets.username},message('display-blocklog'))endfunctionlinkFunctions.bls(snippets)-- BlocksreturnmakeWikilink(snippets.interwiki,-1,'Log/block/'..snippets.username,message('display-blocks'))endfunctionlinkFunctions.bu(snippets)-- Block userreturnmakeWikilink(snippets.interwiki,-1,'Block/'..snippets.username,message('display-blockuser'))endfunctionlinkFunctions.ca(snippets)-- Central authreturnmakeWikilink(snippets.interwiki,-1,'CentralAuth/'..snippets.username,message('display-centralauth'))endfunctionlinkFunctions.dc(snippets)-- Deleted contribsreturnmakeWikilink(snippets.interwiki,-1,'DeletedContributions/'..snippets.username,message('display-deletedcontributions'))endfunctionlinkFunctions.e(snippets)-- EmailreturnmakeWikilink(snippets.interwiki,-1,'EmailUser/'..snippets.username,message('display-email'))endfunctionlinkFunctions.es(snippets)-- Edit summariesreturnmakeWikilink('xtools',0,'editsummary/'..snippets.toolLang..'.'..snippets.projectLong..'.org/'..snippets.username,message('display-editsummaries'))endfunctionlinkFunctions.del(snippets)-- DeletionsreturnmakeWikilink(snippets.interwiki,-1,'Log/delete/'..snippets.username,message('display-deletions'))endfunctionlinkFunctions.lu(snippets)-- List userreturnmakeFullUrlLink(snippets.interwiki,-1,'ListUsers',{limit=1,username=snippets.username},message('display-listuser'))endfunctionlinkFunctions.sul(snippets)-- SULreturnmakeWikilink(nil,nil,'sulutil:'..snippets.username,message('display-sul'))endfunctionlinkFunctions.tl(snippets)-- Target logsreturnmakeFullUrlLink(snippets.interwiki,-1,'Log',{page=mw.site.namespaces[2].name..':'..snippets.username},message('display-targetlogs'))endfunctionlinkFunctions.efl(snippets)-- Edit filter logreturnmakeFullUrlLink(snippets.interwiki,-1,'AbuseLog',{wpSearchUser=snippets.username},message('display-abuselog'))endfunctionlinkFunctions.pr(snippets)-- ProtectionsreturnmakeWikilink(snippets.interwiki,-1,'Log/protect/'..snippets.username,message('display-protections'))endfunctionlinkFunctions.rl(snippets)-- User rightsreturnmakeWikilink(snippets.interwiki,-1,'Log/rights/'..snippets.username,message('display-rights'))endfunctionlinkFunctions.ren(snippets)-- RenamesreturnmakeWikilink(snippets.interwiki,-1,'Log/renameuser/'..snippets.username,message('display-renames'))endfunctionlinkFunctions.rfa(snippets)-- Requests for adminshipreturnmakeWikilink(nil,-1,'PrefixIndex/'..message('page-rfa')..'/'..snippets.username,message('display-rfa'))endfunctionlinkFunctions.api(snippets)-- API user datareturnmakeUrlLink({host=snippets.fullDomain,path='/w/api.php',query={action='query',list='users',usprop='groups|editcount',ususers=snippets.username}},message('display-api'))endfunctionlinkFunctions.up(snippets)-- UploadsreturnmakeWikilink(snippets.interwiki,-1,'ListFiles/'..snippets.username,message('display-uploads'))endfunctionlinkFunctions.nuke(snippets)-- Mass delete/Special:NukereturnmakeWikilink(snippets.interwiki,-1,'Nuke/'..snippets.username,message('display-nuke'))endfunctionlinkFunctions.gender(snippets)-- Genderreturnmw.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.localextraLinkFunctions-- Define functions for shared code in the metatable.localfunctionvalidateCode(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.iftype(code)=='string'andcode~=''thenreturncodeelsereturnnilendendlocalfunctiongetExtraLinkFunctions()-- 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.ifextraLinkFunctions~=nilthenreturnextraLinkFunctionsendifmExtra==nilthen-- 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')endiftype(mExtra)=='table'andtype(mExtra.linkFunctions)=='table'thenextraLinkFunctions=mExtra.linkFunctionselseextraLinkFunctions=falseendreturnextraLinkFunctionsendlocalfunctionmemoizeExtraLink(code,func)localsuccess,link=pcall(func,snippets)ifsuccessandtype(link)=='string'thenlinks[code]=linkreturnlinkendreturnnilend-- Define the metatable.setmetatable(links,{__index=function(t,key)localcode=validateCode(key)ifnotcodethenraiseError(message('error-malformedlinkcode'),message('error-malformedlinkcode-section'))endlocallinkFunction=linkFunctions[code]locallinkiflinkFunctionthenlink=linkFunction(snippets)links[code]=linkelseextraLinkFunctions=getExtraLinkFunctions()ifextraLinkFunctionsthenlocalextraLinkFunction=extraLinkFunctions[code]iftype(extraLinkFunction)=='function'thenlink=memoizeExtraLink(code,extraLinkFunction)endendendiflinkthenreturnlinkelseraiseError(message('error-invalidlinkcode',code),message('error-invalidlinkcode-section'))endend,__pairs=function()extraLinkFunctions=getExtraLinkFunctions()ifextraLinkFunctionsthenforcode,funcinpairs(extraLinkFunctions)doifvalidateCode(code)andtype(func)=='function'thenmemoizeExtraLink(code,func)endendend-- Allow built-in functions to overwrite extra functions.forcode,funcinpairs(linkFunctions)dolocallink=func(snippets)links[code]=linkendreturnfunction(t,key)returnnext(links,key)endend})returnlinksend---------------------------------------------------------------------------------- User data snippets--------------------------------------------------------------------------------functionp.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]].--]=]localsnippets,snippetFunctions={},{}setmetatable(snippets,{__index=function(t,key)localsnippetFunction=snippetFunctions[key]ifsnippetFunctionthensnippets[key]=snippetFunction()or''returnsnippets[key]elseraiseError(message('error-nosnippet',key),message('error-nosnippet-section'))endend})-- Define helper functions for writting the snippet functions.localfunctionsnippetExists(key)-- We have set the metatable up to make snippets default to '', so we-- don't have to test for false or nil.returnsnippets[key]~=''endlocalfunctiongetSnippet(key)localret=snippets[key]ifret==''thenreturnnilelsereturnretendend-- Start snippet functions.functionsnippetFunctions.username()-- The username.localusername=args.userorargs.UserreturnusernameorraiseError(message('error-nousername'),message('error-nousername-section'))endfunctionsnippetFunctions.usernameHtml()-- The username html-encoded. Spaces are encoded as pluses.returnmw.uri.encode(snippets.username)endfunctionsnippetFunctions.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.localproject=args.Projectorargs.projectifnotprojectthenreturnnilendlocalprojectValidated,interwikiTableKey=p.validateProjectCode(project)ifnotprojectValidatedthenifmw.language.isKnownLanguageTag(project)thenifnotsnippetExists('lang')thensnippets.lang=projectendelseraiseError(message('error-invalidproject',project),message('error-invalidproject-section'))endendsnippets.interwikiTableKey=interwikiTableKeyreturnprojectendfunctionsnippetFunctions.interwikiTableKey()-- The key for the project in Module:InterwikiTable.-- Relies on snippetFunctions.project to do the real work.localtemp=snippets.project-- required; puts key in snippets tablereturnrawget(snippets,'interwikiTableKey')endfunctionsnippetFunctions.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.localproject=getSnippet('project')ifnotprojectthenreturnmessage('snippet-project-default')elsereturnprojectendendfunctionsnippetFunctions.projectLong()-- The long form of the project name, e.g. "wikipedia" or "wikibooks".localkey=getSnippet('interwikiTableKey')ifnotkeythenreturnmessage('snippet-projectlong-default')endinterwikiTable=interwikiTableormw.loadData('Module:InterwikiTable')localprefixes=interwikiTable[key].iw_prefix-- Using prefixes[2] is a bit of a hack, but should find the long name-- most of the time.returnprefixes[2]orprefixes[1]endfunctionsnippetFunctions.lang()-- The language code.locallang=args.langorargs.Langifnotlangthenreturnnilendifmw.language.isKnownLanguageTag(lang)thenreturnlangelseraiseError(message('error-invalidlanguage',lang),message('error-invalidlanguage-section'))endendfunctionsnippetFunctions.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.returngetSnippet('lang')ormessage('snippet-lang-default')endfunctionsnippetFunctions.interwiki()-- The interwiki prefix, consisting of the project and language values,-- separated by colons, e.g. ":wikt:es:".localproject=getSnippet('project')locallang=getSnippet('lang')ifnotprojectandnotlangthenreturnnilendlocalret={}ret[#ret+1]=projectret[#ret+1]=langreturntable.concat(ret,':')endfunctionsnippetFunctions.fullDomain()-- The full domain name of the site, e.g. www.mediawiki.org,-- en.wikipedia.org, or ja.wikibooks.org.localfullDomainlocallang=getSnippet('toolLang')localkey=getSnippet('interwikiTableKey')ifkeytheninterwikiTable=interwikiTableormw.loadData('Module:InterwikiTable')localdomain=interwikiTable[key].domainlocaltakesLangPrefix=interwikiTable[key].takes_lang_prefixiftakesLangPrefixthenfullDomain=lang..'.'..domainelsefullDomain=domainendelsefullDomain=lang..'.wikipedia.org'endreturnfullDomainend-- End snippet functions. If you add a new snippet function, please-- document it at [[Module:UserLinks#Adding new links]].returnsnippetsendfunctionp.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=interwikiTableormw.loadData('Module:InterwikiTable')forkey,tinpairs(interwikiTable)dofori,prefixinipairs(t.iw_prefix)doifs==prefixthenreturns,keyendendendreturnnil,nilend---------------------------------------------------------------------------------- Main functions--------------------------------------------------------------------------------localfunctionmakeInvokeFunction(funcName)-- Makes a function that can be accessed from #invoke. This is only required-- for functions that need to access arguments.returnfunction(frame)mArguments=require('Module:Arguments')localargs=mArguments.getArgs(frame)returnp[funcName](args)endendp.main=makeInvokeFunction('_main')functionp._main(args)-- The main function. This is the one called from [[Template:User-multi]],-- via p.main.localoptions=p.getOptions(args)localsnippets=p.getSnippets(args)localcodes=p.getCodes(args)locallinks=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.localsuccess,result=pcall(p.export,codes,links,options)ifsuccessthenreturnresultelsereturnmakeWikitextError(result,options.isDemo)endendfunctionp.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.localoptions={}options.isDemo=yesno(args.demo)orfalseoptions.noPing=yesno(args.noPing)oryesno(args.noping)oryesno(args.np)orfalseoptions.toolbarStyle=yesno(args.small)and'font-size: 90%;'orniloptions.sup=yesno(args.sup,true)options.separator=args.separatoroptions.span=args.spanreturnoptionsendfunctionp.getCodes(args)-- Gets the link codes from the arguments. The codes aren't validated-- at this point.mTableTools=maybeLoadModule('Module:TableTools')localcodesifmTableToolsthencodes=mTableTools.compressSparseArray(args)elsecodes={}fori,codeinipairs(args)docodes[i]=codeendendreturncodesendfunctionp.export(codes,links,options)-- Make the user link.localuserLink=options.noPingandlinks.nporlinks.u-- If we weren't passed any link codes, just return the user link.if#codes<1thenreturnuserLinkend-- Make the toolbar.mToolbar=require('Module:Toolbar')localtoolbarArgs={}fori,codeinipairs(codes)dolocallink=links[code]toolbarArgs[#toolbarArgs+1]=linkendtoolbarArgs.style=options.toolbarStyletoolbarArgs.separator=options.separatoror'dot'toolbarArgs.span=options.spanlocaltoolbar=mToolbar.main(toolbarArgs)-- Apply the sup option.ifoptions.supthentoolbar='<sup>'..toolbar..'</sup>'end-- If we are transcluding, add a non-breaking space, but if we are substing-- just use a normal spacelocalspace=mw.isSubsting()and' 'or' 'returnuserLink..space..toolbarend---------------------------------------------------------------------------------- Single link function--------------------------------------------------------------------------------p.single=makeInvokeFunction('_single')functionp._single(args)-- Fetches a single link from the link table.localoptions=p.getOptions(args)localsnippets=p.getSnippets(args)locallinks=p.getLinks(snippets)localcode=args[1]localsuccess,link=pcall(p.exportSingle,links,code)ifsuccessthenreturnlinkelsereturnmakeWikitextError(link,options.isDemo)endendfunctionp.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.ifnotcodethenraiseError(message('error-nolinkcode'),message('error-nolinkcode-section'))endreturnlinks[code]endreturnp