Module:UserLinks/sandbox: Difference between revisions

Content deleted Content added
return the user link rather than the blank string if we aren't passed any link codes
others
 
(77 intermediate revisions by 14 users not shown)
Line 1:
--------------------------------------------------------------------------------
-- 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
local mArguments -- [[Module:Arguments]]
local mToolbar
local mToolbar -- [[Module:Toolbar]]
local mCategoryHandler
local mCategoryHandler -- [[Module:Category handler]]
local mTableTools
local interwikiTablemTableTools -- [[Module:InterwikiTable, loaded with mw.loadDataTableTools]]
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 functionstable
--------------------------------------------------------------------------------
 
function p.getLinkFunctionsgetLinks(snippets)
--[=[
local linkFunctions = {}
-- 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 = {}, {}
 
----------------------------------------------------------------------------
-- Get extra link functions first, and allow the built-in link functions to
-- Link functions
-- overwrite them. Extra link functions are stored in /extra. We need to
--
-- give the built-in functions priority, or a vandal could change high-risk
-- linkThe following functions make the links from the /extralink codes and the module.user
-- data snippets. New link functions should be added below the existing
local success, mExtra = pcall(require, 'Module:UserLinks/extra')
-- functions.
if success then
----------------------------------------------------------------------------
local extraLinkFunctions = type(mExtra) == 'table' and mExtra.linkFunctions
if type(extraLinkFunctions) == 'table' then
for code, func in pairs(extraLinkFunctions) do
if type(code) == 'string'
and code ~= ''
and type(func) == 'function'
then
linkFunctions[code] = func
end
end
end
end
 
-- Built-in link functions
function linkFunctions.u(snippets)
-- User page
return makeWikilink(
snippets.interwiki .. 'User:' .. ,
2,
snippets.username,
snippets.username)
)
end
function linkFunctions.np(snippets)
-- User page (no ping)
return '<span class="plainlinks">' .. makeFullUrlLink(
snippets.interwiki,
2,
snippets.username,
'',
snippets.username
) .. '</span>'
end
 
function linkFunctions.t(snippets)
-- User talk page
return makeWikilink(snippets.interwiki .. 'User talk:' .. snippets.username, 'talk')
snippets.interwiki,
3,
snippets.username,
message('display-talk')
)
end
 
function linkFunctions.c(snippets)
-- Contributions
return makeWikilink(snippets.interwiki .. 'Special:Contributions/' .. snippets.username, 'contribs')
snippets.interwiki,
-1,
'Contribs/' .. snippets.username,
message('display-contributions')
)
end
function linkFunctions.c64(snippets)
-- Contributions
local 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 ''
end
 
function linkFunctions.ct(snippets)
-- Edit count
return string.formatmakeWikilink(
'xtools',
'[//tools.wmflabs.org/supercount/index.php?user=%s&project=%s.%s count]',
0,
snippets.usernameHtml, snippets.toolLang, snippets.projectLong
'ec/' .. snippets.toolLang .. '.' .. snippets.projectLong .. '.org/' .. snippets.username,
message('display-count')
)
end
Line 70 ⟶ 117:
function linkFunctions.m(snippets)
-- Page moves
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:Log/move/' .. snippets.username, 'page moves')
message('display-moves')
)
end
 
function linkFunctions.l(snippets)
-- Logs
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:Log/' .. snippets.username, 'logs')
message('display-logs')
)
end
 
function linkFunctions.ae(snippets)
-- Automated edits (and non-automated contributions).
return makeWikilink(
'xtools',
0,
'autoedits/' .. snippets.toolLang .. '.' .. snippets.projectLong .. '.org/' .. snippets.username,
message('display-autoedits')
)
end
 
function linkFunctions.bl(snippets)
-- Block log
return makeFullUrlLink(
return makeUrlLink(snippets.interwiki .. 'Special:Log/block', {page = 'User:' .. snippets.username}, 'block log')
snippets.interwiki,
-1,
'Log/block',
{page = 'User:' .. snippets.username},
message('display-blocklog')
)
end
 
function linkFunctions.bls(snippets)
-- Blocks
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:Log/block/' .. snippets.username, 'blocks')
message('display-blocks')
)
end
 
function linkFunctions.bu(snippets)
-- Block user
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:Block/' .. snippets.username, 'block user')
message('display-blockuser')
)
end
 
function linkFunctions.ca(snippets)
-- Central auth
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:CentralAuth/' .. snippets.username, 'central auth')
message('display-centralauth')
)
end
 
function linkFunctions.dc(snippets)
-- Deleted contribs
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:DeletedContributions/' .. snippets.username, 'deleted contribs')
message('display-deletedcontributions')
)
end
 
function linkFunctions.e(snippets)
-- Email
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:EmailUser/' .. snippets.username, 'email')
message('display-email')
)
end
 
function linkFunctions.es(snippets)
-- Edit summaries
return string.formatmakeWikilink(
'xtools',
'[//tools.wmflabs.org/xtools/editsummary/index.php?name=%s&lang=%s&wiki=%s edit&nbsp;summaries]',
0,
snippets.usernameHtml, snippets.toolLang, snippets.projectLong
'editsummary/' .. snippets.toolLang .. '.' .. snippets.projectLong .. '.org/' .. snippets.username,
message('display-editsummaries')
)
end
Line 118 ⟶ 218:
function linkFunctions.del(snippets)
-- Deletions
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:Log/delete/' .. snippets.username, 'deletions')
message('display-deletions')
)
end
 
function linkFunctions.lu(snippets)
-- List user
return makeFullUrlLink(
return makeUrlLink(snippets.interwiki .. 'Special:ListUsers', {limit = 1, user = snippets.username}, 'list user')
snippets.interwiki,
-1,
'ListUsers',
{limit = 1, username = snippets.username},
message('display-listuser')
)
end
 
function linkFunctions.sul(snippets)
-- SUL
return makeWikilink('sulutil:' .. snippets.username, 'global contribs')
nil,
nil,
'sulutil:' .. snippets.username,
message('display-sul')
)
end
 
function linkFunctions.tl(snippets)
-- Target logs
return makeFullUrlLink(
return makeUrlLink(snippets.interwiki .. 'Special:Log', {page = 'User:' .. snippets.username}, 'target logs')
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(
return makeUrlLink(snippets.interwiki .. 'Special:AbuseLog', {wpSearchUser = snippets.username}, 'edit filter log')
snippets.interwiki,
-1,
'AbuseLog',
{wpSearchUser = snippets.username},
message('display-abuselog')
)
end
 
function linkFunctions.pr(snippets)
-- Protections
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:Log/protect/' .. snippets.username,
message('display-protections')
)
end
 
function linkFunctions.rl(snippets)
-- User rights
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:Log/rights/' .. snippets.username, 'rights')
message('display-rights')
)
end
 
function linkFunctions.ren(snippets)
-- Renames
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:Log/renameuser/' .. snippets.username, 'renames')
message('display-renames')
)
end
 
function linkFunctions.rfa(snippets)
-- Requests for adminship
return makeWikilink('Special:PrefixIndex/Wikipedia:Requests for adminship/' .. snippets.username, 'RfA')
nil,
-1,
'PrefixIndex/' .. message('page-rfa') .. '/' .. snippets.username,
message('display-rfa')
)
end
 
function linkFunctions.api(snippets)
-- API user data
return string.formatmakeUrlLink(
{
'[//%s/w/api.php?action=query&list=users&usprop=editcount&ususers=%s api]',
host = snippets.fullDomain, snippets.usernameHtml
path = '/w/api.php',
query = {
action = 'query',
list = 'users',
usprop = 'groups|editcount',
ususers = snippets.username
}
},
message('display-api')
)
end
Line 171 ⟶ 328:
function linkFunctions.up(snippets)
-- Uploads
return makeWikilink(
snippets.interwiki .. ,
-1,
'Special:ListFiles/' .. snippets.username, 'uploads')
message('display-uploads')
)
end
function linkFunctions.nuke(snippets)
-- Mass delete/Special:Nuke
return makeWikilink(
snippets.interwiki,
-1,
'Nuke/' .. snippets.username,
message('display-nuke')
 
)
return linkFunctions
end
function linkFunctions.gender(snippets)
-- Gender
return 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 ~= '' 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
 
Line 182 ⟶ 476:
 
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, {
Line 190 ⟶ 500:
return snippets[key]
else
raiseError(
raiseError('no snippet exists for the key "' .. key .. '"', 'No snippet exists')
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
Line 209 ⟶ 523:
end
end
 
-- Start snippet functions.
 
function snippetFunctions.username()
-- The username.
local username = args.user or args.User
return username or raiseError('no username detected', 'No username detected')
message('error-nousername'),
message('error-nousername-section')
)
end
 
Line 223 ⟶ 542:
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
Line 230 ⟶ 550:
local projectValidated, interwikiTableKey = p.validateProjectCode(project)
if not projectValidated then
if mw.language.isKnownLanguageTag(project) then
raiseError('"' .. project .. '" is not a valid interwiki prefix', 'Not a valid interwiki prefix')
if not snippetExists('lang') then
snippets.lang = project
end
else
raiseError(
message('error-invalidproject', project),
message('error-invalidproject-section')
)
end
end
snippets.interwikiTableKey = interwikiTableKey
Line 239 ⟶ 568:
-- The key for the project in Module:InterwikiTable.
-- Relies on snippetFunctions.project to do the real work.
local temp = snippets.project -- required, as it; puts the key in the 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
 
Line 247 ⟶ 588:
local key = getSnippet('interwikiTableKey')
if not key then
return nilmessage('snippet-projectlong-default')
end
interwikiTable = interwikiTable or mw.loadData('Module:InterwikiTable')
local prefixes = interwikiTable[key].iw_prefix
return-- Using prefixes[2] oris prefixes[1] -- Aa bit of a hack, but should find the long name
-- most of the time.
return prefixes[2] or prefixes[1]
end
 
Line 263 ⟶ 606:
return lang
else
raiseError(
raiseError('"' .. lang .. '" is not a valid language code', 'Not a valid language code')
message('error-invalidlanguage', lang),
message('error-invalidlanguage-section')
)
end
end
Line 269 ⟶ 615:
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 "en".
-- the "snippet-lang-default" message.
return getSnippet('lang') or 'en'
return getSnippet('lang') or message('snippet-lang-default')
end
 
Line 284 ⟶ 631:
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.wikpediawikipedia.org, or ja.wikibooks.org.
local fullDomain
local lang = getSnippet('toolLang')
Line 307 ⟶ 654:
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)
Line 317 ⟶ 667:
-- 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
end
return nil, nil
end
 
Line 332 ⟶ 682:
 
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')
Line 342 ⟶ 694:
 
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 linkFunctionslinks = p.getLinkFunctionsgetLinks(snippets)
-- Overload the built-in Lua error function to generate wikitext errors
local success, result = pcall(p.export, codes, snippets, linkFunctions, options)
-- 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
Line 355 ⟶ 713:
 
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.noPing = yesno(args.noPing) or yesno(args.noping) or yesno(args.np) or false
options.toolbarStyle = yesno(args.small) and 'font-size: 90%;' or nil
options.sup = yesno(args.sup, true)
Line 365 ⟶ 726:
 
function p.getCodes(args)
-- Gets the link codes from the arguments. The codes aren't validated
mTableTools = require('Module:TableTools')
-- at this point.
local codes = mTableTools.compressSparseArray(args)
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, snippets, linkFunctionslinks, options)
-- Make the user link.
local userLink = linkFunctionsoptions.noPing and links.np or links.u(snippets)
 
-- If we weren't passed any link codes, just return the user link.
Line 383 ⟶ 754:
local toolbarArgs = {}
for i, code in ipairs(codes) do
local linkFunctionlink = linkFunctionslinks[code]
if not linkFunction then
raiseError('"' .. code .. '" is not a valid link code', 'Not a valid link code')
end
local link = linkFunction(snippets)
toolbarArgs[#toolbarArgs + 1] = link
end
Line 395 ⟶ 762:
local toolbar = mToolbar.main(toolbarArgs)
 
-- Apply the sup option and return the result.
if options.sup then
toolbar = '<sup>' .. toolbar .. '</sup>'
end
return userLink .. '&nbsp;' .. toolbar
-- 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
 
Line 409 ⟶ 781:
 
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 not code then
return makeWikitextError(
'no link type specified|No link type specified',
options.isDemo
)
end
local snippets = p.getSnippets(args)
local linkFunctions = p.getLinkFunctions()
local linkFunction = linkFunctions[code]
local success, result = pcall(linkFunction, snippets)
if success then
return resultlink
else
return makeWikitextError(resultlink, options.isDemo)
end
end
 
function p.exportSingle(links, code)
--------------------------------------------------------------------------------
-- If any errors occur, they will probably occur here. This function
-- Link table
-- exists purely so that all the errors that will occur in p._single can
--------------------------------------------------------------------------------
-- be handled using a single pcall.
 
if not code then
function p.linktable()
raiseError(
local args = {user = 'Example'}
message('error-nolinkcode'),
local snippets = p.getSnippets(args)
message('error-nolinkcode-section')
local linkFunctions = p.getLinkFunctions()
)
 
-- 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
local success, link = pcall(linkFunctions[code], snippets)
if success then
firstLinks[#firstLinks + 1] = {code, link}
else
return makeWikitextError(result, true)
end
end
return links[code]
local secondLinks = {}
end
for code, linkFunction in pairs(linkFunctions) do
if not firstCodesKeys[code] then
local success, link = pcall(linkFunction, snippets)
if success then
secondLinks[#secondLinks + 1] = {code, link}
else
return makeWikitextError(result, true)
end
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] = '! Code'
ret[#ret + 1] = '! Preview'
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