Module:UserLinks/sandbox: Difference between revisions

Content deleted Content added
detect project arguments that are in fact lang arguments
others
 
(76 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
-- 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 236 ⟶ 555:
end
else
raiseError(
raiseError('"' .. project .. '" is not a valid interwiki prefix', 'Not a valid interwiki prefix')
message('error-invalidproject', project),
message('error-invalidproject-section')
)
end
end
Line 246 ⟶ 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 254 ⟶ 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 270 ⟶ 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 276 ⟶ 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 291 ⟶ 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 314 ⟶ 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 324 ⟶ 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 339 ⟶ 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 349 ⟶ 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 362 ⟶ 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 372 ⟶ 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 390 ⟶ 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 402 ⟶ 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 416 ⟶ 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