local ToolbarBuilder = require('Module:Toolbar')
local interwikiTable = mw.loadData("Module:InterwikiTable")
local u = {} -- Table for user-data helper strings.
local trackingCategories = {} -- Table for storing the tracking categories.
local demo
-- Define a custom error message for this module.
local function err(msg, section)
local help
if section then
help = ' ([[Template:User-multi#' .. section .. '|help]])'
else
help = ''
end
local cat
if demo == 'yes' then
cat = ''
else
cat = '[[Category:UserLinks transclusions with errors]]'
end
return '<span class="error">[[Template:User-multi|User-multi]] error: ' .. msg
.. help .. '.</span>' .. cat
end
----------------------------------------------------------------------------------------------
-- To add more link types, write a function that produces an individual link, and put --
-- it at the bottom of the list below. Then, add a link code for your function to the --
-- "linktypes" table. Try and make the code three letters or less. There are a number --
-- of helper strings available for writing the functions: --
-- --
-- u.username The plain username. If the username is not present then the --
-- module returns an error. --
-- u.usernameHtml The username html-encoded. Spaces are encoded with plus signs. --
-- u.project The project name. Nil if not specified. --
-- u.lang The language code. Nil if not specified. --
-- u.interwiki The interwiki prefix, consisting of the project and language --
-- values, separated by colons, e.g. ":wikt:es:". If no project --
-- or language values are found, this is the blank string, "". --
-- u.projectCode If a valid project is specified, this is the code for that --
-- project in [[Module:InterwikiTable]]. Otherwise this is nil. --
-- u.projectLong The long project name, e.g. "wikipedia" or "wikibooks". If --
-- not specified the default is "wikipedia". --
-- u.toolLang The language code for use with toolserver or labs tools. The --
-- default is "en". --
-- --
-- If you want more helper strings, you can define them in the generateUserDataStrings --
-- function below. --
----------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------
-- LINK FUNCTIONS START --
----------------------------------------------------------------------------------------------
local function makeUserLink()
return '[[' .. u.interwiki .. 'User:' .. u.username .. '|' .. u.username .. ']]'
end
local function makeTalkLink()
return '[[' .. u.interwiki .. 'User talk:' .. u.username .. '|talk]]'
end
local function makeContribsLink()
return '[[' .. u.interwiki .. 'Special:Contributions/' .. u.username .. '|contribs]]'
end
local function makeCountLink()
return '[https://tools.wmflabs.org/supercount/index.php?user=' .. u.usernameHtml
.. '&project=' .. u.toolLang .. '.' .. u.projectLong
.. ' count]'
end
local function makeMovesLink()
return '[[' .. u.interwiki .. 'Special:Log/move/' .. u.username .. '|page moves]]'
end
local function makeLogsLink()
return '[[' .. u.interwiki .. 'Special:Log/' .. u.username .. '|logs]]'
end
local function makeBlockLogLink()
local url = mw.uri.fullUrl(u.interwiki .. 'Special:Log/block', 'page=User:' .. u.usernameHtml)
return '[' .. tostring(url) .. ' block log]'
end
local function makeBlocksLink()
return '[[' .. u.interwiki .. 'Special:Log/block/' .. u.username .. '|blocks]]'
end
local function makeBlockUserLink()
return '[[' .. u.interwiki .. 'Special:Block/' .. u.username .. '|block user]]'
end
local function makeCentralAuthLink()
return '[[' .. u.interwiki .. 'Special:CentralAuth/' .. u.username .. '|central auth]]'
end
local function makeDeletedContribsLink()
return '[[' .. u.interwiki .. 'Special:DeletedContributions/' .. u.username .. '|deleted contribs]]'
end
local function makeEmailLink()
return '[[' .. u.interwiki .. 'Special:Emailuser/' .. u.username .. '|email]]'
end
local function makeEditSummariesLink()
return '[https://tools.wmflabs.org/xtools/editsummary/index.php?name=' .. u.usernameHtml
.. '&lang=' .. u.toolLang
.. '&wiki=' .. u.projectLong
.. ' edit summaries]'
end
local function makeDeletionsLink()
return '[[' .. u.interwiki .. 'Special:Log/delete/' .. u.username .. '|deletions]]'
end
local function makeListUserLink()
local url = mw.uri.fullUrl(u.interwiki .. 'Special:ListUsers', 'limit=1&username=' .. u.usernameHtml)
return '[' .. tostring(url) .. ' list user]'
end
local function makeSulLink()
return '[[sulutil:' .. u.username .. '|global contribs]]'
end
local function makeTargetLogsLink()
local url = mw.uri.fullUrl(u.interwiki .. 'Special:Log', 'page=User:' .. u.usernameHtml)
return '[' .. tostring(url) .. ' target logs]'
end
local function makeEditFilterLogLink()
local url = mw.uri.fullUrl(u.interwiki .. 'Special:AbuseLog', 'wpSearchUser=' .. u.usernameHtml)
return '[' .. tostring(url) .. ' edit filter log]'
end
local function makeProtectionsLink()
return '[[' .. u.interwiki .. 'Special:Log/protect/' .. u.username .. '|protections]]'
end
local function makeRightsLink()
return '[[' .. u.interwiki .. 'Special:Log/rights/' .. u.username .. '|rights]]'
end
local function makeRenamesLink()
return '[[' .. u.interwiki .. 'Special:Log/renameuser/' .. u.username .. '|renames]]'
end
local function makeRfaLink()
if u.project or u.lang then
table.insert( trackingCategories, '[[Category:UserLinks transclusions with unresolvable interwiki links]]' )
end
return '[[Special:PrefixIndex/Wikipedia:Requests for adminship/' .. u.username .. '|RfA]]'
end
local function makeApiLink()
-- Find the full domain, as the API can't be accessed through the interwiki system.
local fulldomain
local lang = u.lang or 'en'
if u.projectCode then
local domain = interwikiTable[u.projectCode].domain
local takesLangPrefix = interwikiTable[u.projectCode].takes_lang_prefix
if not takesLangPrefix then
fulldomain = domain
else
fulldomain = lang .. '.' .. domain
end
else
fulldomain = lang .. '.wikipedia.org'
end
-- Return the API link
return '[https://' .. fulldomain .. '/w/api.php?action=query&list=users&usprop=editcount&ususers=' .. u.usernameHtml .. ' api]'
end
local function makeUploadsLink()
return '[[' .. u.interwiki .. 'Special:ListFiles/' .. u.username .. '|uploads]]'
end
----------------------------------------------------------------------------------------------
-- LINK FUNCTIONS END --
-- To enable new link functions, add the code to the "linktypes" table directly below. --
----------------------------------------------------------------------------------------------
local linktypes = {
{'t' , makeTalkLink},
{'c' , makeContribsLink},
{'ct' , makeCountLink},
{'m' , makeMovesLink},
{'l' , makeLogsLink},
{'bl' , makeBlockLogLink},
{'bls' , makeBlocksLink},
{'bu' , makeBlockUserLink},
{'ca' , makeCentralAuthLink},
{'dc' , makeDeletedContribsLink},
{'e' , makeEmailLink},
{'es' , makeEditSummariesLink},
{'del' , makeDeletionsLink},
{'lu' , makeListUserLink},
{'sul' , makeSulLink},
{'tl' , makeTargetLogsLink},
{'efl' , makeEditFilterLogLink},
{'pr' , makeProtectionsLink},
{'rl' , makeRightsLink},
{'ren' , makeRenamesLink},
{'rfa' , makeRfaLink},
{'api' , makeApiLink},
{'up' , makeUploadsLink}
}
local function getLink(linktype)
local linkNumber
for i, value in ipairs(linktypes) do
if value[1] == linktype then
linkNumber = i
break
end
end
if not linkNumber then
return err('"' .. linktype .. '" is not a valid link code', 'Not a valid link code')
end
local result = linktypes[linkNumber][2]()
if type(result) ~= 'string' then
return err(
'the function for code "' .. linktype .. '" did not return a string value',
'Function did not return a string value'
)
end
return result
end
local function makeToolbar(args)
local targs = {}
local numArgsExist = false
for k, v in pairs(args) do
if type(k) == 'number' then
numArgsExist = true
targs[k] = getLink(v)
end
end
targs.style = args.small and 'font-size: 90%;'
targs.separator = args.separator or 'dot'
if numArgsExist == false then
return nil -- Don't return a toolbar if no numeric arguments exist.
else
return ToolbarBuilder.main(targs)
end
end
-- This function finds whether a string is a valid interwiki project prefix.
-- If the string is valid, the function outputs two values: true, and the site code
-- used in [[Module:InterwikiTable]]. If the string is valid, the function outputs
-- false and nil.
local function isKnownProject(prefix)
for projectCode, projectVal in pairs(interwikiTable) do
for _, iwCode in ipairs(projectVal.iw_prefix) do
if iwCode == prefix then
return true, projectCode
end
end
end
return false, nil
end
local function generateUserDataStrings(args)
-- If the username is absent or blank, return an error and a tracking category.
if args.user == '' or (not args.user and (not args.User or args.User == '')) then
return err('no username detected', 'No username detected')
else
u.username = args.user or args.User
end
-- Get other basic user data strings.
u.project = args.Project or args.project
u.lang = args.lang or args.Lang
if u.lang then
if mw.language.isKnownLanguageTag(u.lang) then
table.insert(trackingCategories, '[[Category:UserLinks transclusions with language parameters]]')
else
return err('"' .. u.lang .. '" is not a valid language code', 'Not a valid language code')
end
end
-- Process the project value if it is present.
if u.project then
table.insert( trackingCategories, '[[Category:UserLinks transclusions with project parameters]]' )
-- If u.project is a known project, we only need to get the project code. If the project
-- isn't known, first check whether it is a valid language code, and if not then see if it's
-- an interwiki code separated by colons, e.g. "wikt:es".
local uprojectIsKnownProject, uprojectProjectCode = isKnownProject(u.project)
if uprojectIsKnownProject then
u.projectCode = uprojectProjectCode
else
if mw.language.isKnownLanguageTag(u.project) then
u.lang = u.project
u.project = nil
else
local pref1, pref2 = mw.ustring.match( u.project, '^(%w+):(%w+)$' )
if pref1 and pref2 then
local pref1IsKnownProject, pref1ProjectCode = isKnownProject(pref1)
local pref2IsKnownProject, pref2ProjectCode = isKnownProject(pref2)
if pref1IsKnownProject
and mw.language.isKnownLanguageTag(pref2)
and interwikiTable[pref1ProjectCode].takes_lang_prefix then
u.project = pref1
u.lang = pref2
u.projectCode = pref1ProjectCode
table.insert(
trackingCategories,
'[[Category:UserLinks transclusions with project parameters containing language codes]]'
)
elseif pref2IsKnownProject
and mw.language.isKnownLanguageTag(pref1)
and interwikiTable[pref2ProjectCode].takes_lang_prefix then
u.project = pref2
u.lang = pref1
u.projectCode = pref2ProjectCode
table.insert(
trackingCategories,
'[[Category:UserLinks transclusions with project parameters containing language codes]]'
)
else
return err(
'"' .. u.project .. '" is not a valid interwiki prefix',
'Not a valid interwiki prefix'
)
end
else
return err(
'"' .. u.project .. '" is not a valid interwiki prefix',
'Not a valid interwiki prefix'
)
end
end
end
end
-- Generate the interwiki prefix. This includes colons.
if u.project or u.lang then
u.interwiki = ''
if u.project then
u.interwiki = u.interwiki .. ':' .. u.project
end
if u.lang then
u.interwiki = u.interwiki .. ':' .. u.lang
end
u.interwiki = u.interwiki .. ':'
else
u.interwiki = ''
end
-- Generate the other helper strings.
u.usernameHtml = mw.uri.encode(u.username) -- Html-encoded username. Spaces are encoded as pluses.
if u.project then
local prefixes = interwikiTable[u.projectCode].iw_prefix
u.projectLong = prefixes[2] or prefixes[1] -- A bit of a hack, but should find the long prefix name most of the time.
else
u.projectLong = 'wikipedia'
end
u.toolLang = u.lang or 'en' -- set the default language for tools on the toolserver or labs.
end
local function generateTrackingCategories()
if demo == 'yes' then
return ''
else
return table.concat(trackingCategories)
end
end
-- This function generates a table of all available link types, with their previews.
-- It is used in the module documentation.
local function getLinkTable(args)
demo = args.demo -- Set the demo variable.
-- Generate the user data strings and return any errors.
local dataStringError = generateUserDataStrings(args)
if dataStringError then
return dataStringError
end
-- Build a table of all of the links.
local result = '<table class="wikitable plainlinks sortable">'
.. '\n<tr><th>Code</th><th>Preview</th></tr>'
for i, value in ipairs(linktypes) do
local code = value[1]
result = result .. "\n<tr><td>'''" .. code .. "'''</td><td>" .. getLink(code) .. '</td></tr>'
end
result = result .. '\n</table>'
return result
end
local function getSingleLink(args)
demo = args.demo -- Set the demo variable.
-- Generate the user data strings and return any errors.
local dataStringError = generateUserDataStrings(args)
if dataStringError then
return dataStringError
end
local linktype = args[1]
if not linktype then
return err('no link type specified')
end
local result = getLink(linktype)
result = result .. generateTrackingCategories()
return result
end
local function getLinks(args)
demo = args.demo -- Set the demo variable.
-- Generate the user data strings and return any errors.
local dataStringError = generateUserDataStrings(args)
if dataStringError then
return dataStringError
end
-- Build the template output.
local result = makeToolbar(args) -- Get the toolbar contents.
if result then
if args.sup then
result = '<sup>' .. result .. '</sup>'
end
result = ' ' .. result
else
result = '' -- If there are no links specified, don't return the toolbar at all.
end
result = '<span>' .. makeUserLink() .. result .. '</span>'
result = result .. generateTrackingCategories()
return result
end
local function getExampleLinks(args)
-- This function enables example output without having to specify any
-- parameters to #invoke.
args.demo = 'yes'
args.user = 'Example'
args.User = nil
return getLinks(args)
end
local function makeWrapper(func)
return function (frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
local origArgs
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
for k, v in pairs(frame.args) do
origArgs = frame.args
break
end
else
origArgs = frame
end
-- Strip whitespace, and treat blank arguments as nil.
-- 'user', 'User', and 'separator' have different behaviour depending on
-- whether they are blank or nil, so keep them as they are.
local args = {}
for k, v in pairs(origArgs) do
v = mw.text.trim(v)
if v ~= '' or k == 'user' or k == 'User' or k == 'separator' then
args[k] = v
end
end
return func(args)
end
end
return {
main = makeWrapper(getLinks),
single = makeWrapper(getSingleLink),
linktable = makeWrapper(getLinkTable),
example = makeWrapper(getExampleLinks)
}