Module:UserLinks: Difference between revisions
Content added Content deleted
(automatically detect project and language values from the project argument input) |
m (Changed protection level of Module:UserLinks: Allow template editors ([Edit=Allow only template editors and admins] (indefinite) [Move=Allow only template editors and admins] (indefinite))) |
||
Line 1: | Line 1: | ||
-------------------------------------------------------------------------------- |
|||
local ToolbarBuilder = require('Module:Toolbar') |
|||
-- UserLinks -- |
|||
local interwikiTable = mw.loadData("Module:InterwikiTable") |
|||
-- 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 u = {} -- Table for user-data helper strings. |
|||
local yesno = require('Module:Yesno') |
|||
local demo |
|||
-- Lazily initialise modules that we might or might not need |
|||
-- Define a custom error message for this module. |
|||
local mExtra -- [[Module:UserLinks/extra]] |
|||
local function err(msg) |
|||
local mArguments -- [[Module:Arguments]] |
|||
return '<span class="error">[[Module:UserLinks]] error: ' .. msg .. '.</span>[[Category:UserLinks transclusions with errors]]' |
|||
local mToolbar -- [[Module:Toolbar]] |
|||
end |
|||
local mCategoryHandler -- [[Module:Category handler]] |
|||
local mTableTools -- [[Module:TableTools]] |
|||
local interwikiTable -- [[Module:InterwikiTable]], loaded with mw.loadData |
|||
-- Load shared helper functions |
|||
---------------------------------------------------------------------------------------------- |
|||
local mShared = require('Module:UserLinks/shared') |
|||
-- Below are the helper strings available for writing user link functions. -- |
|||
local raiseError = mShared.raiseError |
|||
-- -- |
|||
local maybeLoadModule = mShared.maybeLoadModule |
|||
-- u.username The plain username. If the username is not present then the -- |
|||
local makeWikitextError = mShared.makeWikitextError |
|||
-- module returns an error. -- |
|||
local makeWikilink = mShared.makeWikilink |
|||
-- u.usernameHtml The username html-encoded. Spaces are encoded with plus signs. -- |
|||
local makeUrlLink = mShared.makeUrlLink |
|||
-- u.project The project name. Nil if not specified. -- |
|||
local makeFullUrlLink = mShared.makeFullUrlLink |
|||
-- u.lang The language code. Nil if not specified. -- |
|||
local message = mShared.message |
|||
-- 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, "". -- |
|||
-- -- |
|||
-- If you want more strings, you can define them in the generateUserDataStrings -- |
|||
-- function below. -- |
|||
---------------------------------------------------------------------------------------------- |
|||
local |
local p = {} |
||
return '[[' .. u.interwiki .. 'User:' .. u.username .. '|' .. u.username .. ']]' |
|||
end |
|||
-------------------------------------------------------------------------------- |
|||
local function makeTalkLink() |
|||
-- Link table |
|||
return '[[' .. u.interwiki .. 'User talk:' .. u.username .. '|talk]]' |
|||
-------------------------------------------------------------------------------- |
|||
end |
|||
function p.getLinks(snippets) |
|||
--[=[ |
|||
return '[[' .. u.interwiki .. 'Special:Contributions/' .. u.username .. '|contribs]]' |
|||
-- Get a table of links that can be indexed with link codes. The table |
|||
end |
|||
-- 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 = {}, {} |
|||
---------------------------------------------------------------------------- |
|||
local function makeCountLink() |
|||
-- Link functions |
|||
return '[//tools.wmflabs.org/xtools/pcount/index.php?name=' .. u.usernameHtml .. '&lang=en&wiki=wikipedia count]' |
|||
-- |
|||
end |
|||
-- 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. |
|||
-- |
|||
-- For documentation on how to add new link functions, please see |
|||
-- [[Module:UserLinks#Adding new links]]. |
|||
---------------------------------------------------------------------------- |
|||
function linkFunctions.u(snippets) |
|||
-- User page |
|||
return '[[' .. u.interwiki .. 'Special:Log/move/' .. u.username .. '|page moves]]' |
|||
return makeWikilink( |
|||
end |
|||
snippets.interwiki, |
|||
2, |
|||
snippets.username, |
|||
snippets.username |
|||
) |
|||
end |
|||
function linkFunctions.t(snippets) |
|||
-- User talk page |
|||
return '[[' .. u.interwiki .. 'Special:Log/' .. u.username .. '|logs]]' |
|||
return makeWikilink( |
|||
end |
|||
snippets.interwiki, |
|||
3, |
|||
snippets.username, |
|||
message('display-talk') |
|||
) |
|||
end |
|||
function linkFunctions.c(snippets) |
|||
-- Contributions |
|||
local url = mw.uri.fullUrl('Special:Log/block', 'page=User:' .. u.usernameHtml) |
|||
return makeWikilink( |
|||
return '[' .. tostring(url) .. ' block log]' |
|||
snippets.interwiki, |
|||
end |
|||
-1, |
|||
'Contribs/' .. snippets.username, |
|||
message('display-contributions') |
|||
) |
|||
end |
|||
function linkFunctions.ct(snippets) |
|||
-- Edit count |
|||
return '[[' .. u.interwiki .. 'Special:Log/block/' .. u.username .. '|blocks]]' |
|||
return makeUrlLink( |
|||
end |
|||
{ |
|||
host = 'tools.wmflabs.org', |
|||
path = '/xtools-ec/', |
|||
query = { |
|||
user = snippets.username, |
|||
project = snippets.toolLang .. '.' .. snippets.projectLong .. '.org' |
|||
} |
|||
}, |
|||
message('display-count') |
|||
) |
|||
end |
|||
function linkFunctions.m(snippets) |
|||
-- Page moves |
|||
return '[[' .. u.interwiki .. 'Special:Block/' .. u.username .. '|block user]]' |
|||
return makeWikilink( |
|||
end |
|||
snippets.interwiki, |
|||
-1, |
|||
'Log/move/' .. snippets.username, |
|||
message('display-moves') |
|||
) |
|||
end |
|||
function linkFunctions.l(snippets) |
|||
-- Logs |
|||
return '[[' .. u.interwiki .. 'Special:CentralAuth/' .. u.username .. '|central auth]]' |
|||
return makeWikilink( |
|||
end |
|||
snippets.interwiki, |
|||
-1, |
|||
'Log/' .. snippets.username, |
|||
message('display-logs') |
|||
) |
|||
end |
|||
function linkFunctions.bl(snippets) |
|||
-- Block log |
|||
return '[[' .. u.interwiki .. 'Special:DeletedContributions/' .. u.username .. '|deleted contribs]]' |
|||
return makeFullUrlLink( |
|||
end |
|||
snippets.interwiki, |
|||
-1, |
|||
'Log/block', |
|||
{page = 'User:' .. snippets.username}, |
|||
message('display-blocklog') |
|||
) |
|||
end |
|||
function linkFunctions.bls(snippets) |
|||
-- Blocks |
|||
return '[[' .. u.interwiki .. 'Special:Emailuser/' .. u.username .. '|email]]' |
|||
return makeWikilink( |
|||
end |
|||
snippets.interwiki, |
|||
-1, |
|||
'Log/block/' .. snippets.username, |
|||
message('display-blocks') |
|||
) |
|||
end |
|||
function linkFunctions.bu(snippets) |
|||
-- Block user |
|||
return '[http://toolserver.org/~tparis/editsummary/index.php?name=' .. u.usernameHtml .. '&lang=en&wiki=wikipedia edit summaries]' |
|||
return makeWikilink( |
|||
end |
|||
snippets.interwiki, |
|||
-1, |
|||
'Block/' .. snippets.username, |
|||
message('display-blockuser') |
|||
) |
|||
end |
|||
function linkFunctions.ca(snippets) |
|||
-- Central auth |
|||
return '[[' .. u.interwiki .. 'Special:Log/delete/' .. u.username .. '|deletions]]' |
|||
return makeWikilink( |
|||
end |
|||
snippets.interwiki, |
|||
-1, |
|||
'CentralAuth/' .. snippets.username, |
|||
message('display-centralauth') |
|||
) |
|||
end |
|||
function linkFunctions.dc(snippets) |
|||
-- Deleted contribs |
|||
local url = mw.uri.fullUrl('Special:ListUsers', 'limit=1&username=' .. u.usernameHtml) |
|||
return makeWikilink( |
|||
return '[' .. tostring(url) .. ' list user]' |
|||
snippets.interwiki, |
|||
end |
|||
-1, |
|||
'DeletedContributions/' .. snippets.username, |
|||
message('display-deletedcontributions') |
|||
) |
|||
end |
|||
function linkFunctions.e(snippets) |
|||
-- Email |
|||
return '[[sulutil:' .. u.username .. '|global contribs]]' |
|||
return makeWikilink( |
|||
end |
|||
snippets.interwiki, |
|||
-1, |
|||
'EmailUser/' .. snippets.username, |
|||
message('display-email') |
|||
) |
|||
end |
|||
function linkFunctions.es(snippets) |
|||
-- Edit summaries |
|||
local url = mw.uri.fullUrl('Special:Log', 'page=User:' .. u.usernameHtml) |
|||
return makeUrlLink( |
|||
return '[' .. tostring(url) .. ' target logs]' |
|||
{ |
|||
end |
|||
host = 'tools.wmflabs.org', |
|||
path = '/xtools/editsummary/index.php', |
|||
query = { |
|||
name = snippets.username, |
|||
lang = snippets.toolLang, |
|||
wiki = snippets.projectLong |
|||
} |
|||
}, |
|||
message('display-editsummaries') |
|||
) |
|||
end |
|||
function linkFunctions.del(snippets) |
|||
-- Deletions |
|||
local url = mw.uri.fullUrl('Special:AbuseLog', 'wpSearchUser=' .. u.usernameHtml) |
|||
return makeWikilink( |
|||
return '[' .. tostring(url) .. ' edit filter log]' |
|||
snippets.interwiki, |
|||
end |
|||
-1, |
|||
'Log/delete/' .. snippets.username, |
|||
message('display-deletions') |
|||
) |
|||
end |
|||
function linkFunctions.lu(snippets) |
|||
-- List user |
|||
return '[[' .. u.interwiki .. 'Special:Log/protect/' .. u.username .. '|protections]]' |
|||
return makeFullUrlLink( |
|||
end |
|||
snippets.interwiki, |
|||
-1, |
|||
'ListUsers', |
|||
{limit = 1, username = snippets.username}, |
|||
message('display-listuser') |
|||
) |
|||
end |
|||
function linkFunctions.sul(snippets) |
|||
-- SUL |
|||
return '[[' .. u.interwiki .. 'Special:Log/rights/' .. u.username .. '|rights]]' |
|||
return makeWikilink( |
|||
nil, |
|||
nil, |
|||
'sulutil:' .. snippets.username, |
|||
message('display-sul') |
|||
) |
|||
end |
|||
function linkFunctions.tl(snippets) |
|||
-- Target logs |
|||
return makeFullUrlLink( |
|||
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( |
|||
snippets.interwiki, |
|||
-1, |
|||
'AbuseLog', |
|||
{wpSearchUser = snippets.username}, |
|||
message('display-abuselog') |
|||
) |
|||
end |
|||
function linkFunctions.pr(snippets) |
|||
-- Protections |
|||
return makeWikilink( |
|||
snippets.interwiki, |
|||
-1, |
|||
'Log/protect/' .. snippets.username, |
|||
message('display-protections') |
|||
) |
|||
end |
|||
function linkFunctions.rl(snippets) |
|||
-- User rights |
|||
return makeWikilink( |
|||
snippets.interwiki, |
|||
-1, |
|||
'Log/rights/' .. snippets.username, |
|||
message('display-rights') |
|||
) |
|||
end |
|||
function linkFunctions.ren(snippets) |
|||
-- Renames |
|||
return makeWikilink( |
|||
snippets.interwiki, |
|||
-1, |
|||
'Log/renameuser/' .. snippets.username, |
|||
message('display-renames') |
|||
) |
|||
end |
|||
function linkFunctions.rfa(snippets) |
|||
-- Requests for adminship |
|||
return makeWikilink( |
|||
nil, |
|||
-1, |
|||
'PrefixIndex/' .. message('page-rfa') .. '/' .. snippets.username, |
|||
message('display-rfa') |
|||
) |
|||
end |
|||
function linkFunctions.api(snippets) |
|||
-- API user data |
|||
return makeUrlLink( |
|||
{ |
|||
host = snippets.fullDomain, |
|||
path = '/w/api.php', |
|||
query = { |
|||
action = 'query', |
|||
list = 'users', |
|||
usprop = 'groups|editcount', |
|||
ususers = snippets.username |
|||
} |
|||
}, |
|||
message('display-api') |
|||
) |
|||
end |
|||
function linkFunctions.up(snippets) |
|||
-- Uploads |
|||
return makeWikilink( |
|||
snippets.interwiki, |
|||
-1, |
|||
'ListFiles/' .. snippets.username, |
|||
message('display-uploads') |
|||
) |
|||
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 |
end |
||
-------------------------------------------------------------------------------- |
|||
local function makeRenamesLink() |
|||
-- User data snippets |
|||
return '[[' .. u.interwiki .. 'Special:Log/renameuser/' .. u.username .. '|renames]]' |
|||
-------------------------------------------------------------------------------- |
|||
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 then |
|||
snippets[key] = snippetFunction() or '' |
|||
return snippets[key] |
|||
else |
|||
raiseError( |
|||
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 |
|||
-- don't have to test for false or nil. |
|||
return snippets[key] ~= '' |
|||
end |
|||
local function getSnippet(key) |
|||
local ret = snippets[key] |
|||
if ret == '' then |
|||
return nil |
|||
else |
|||
return ret |
|||
end |
|||
end |
|||
-- Start snippet functions. |
|||
function snippetFunctions.username() |
|||
-- The username. |
|||
local username = args.user or args.User |
|||
return username or raiseError( |
|||
message('error-nousername'), |
|||
message('error-nousername-section') |
|||
) |
|||
end |
|||
function snippetFunctions.usernameHtml() |
|||
-- The username html-encoded. Spaces are encoded as pluses. |
|||
return mw.uri.encode(snippets.username) |
|||
end |
|||
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 |
|||
return nil |
|||
end |
|||
local projectValidated, interwikiTableKey = p.validateProjectCode(project) |
|||
if not projectValidated then |
|||
if mw.language.isKnownLanguageTag(project) then |
|||
if not snippetExists('lang') then |
|||
snippets.lang = project |
|||
end |
|||
else |
|||
raiseError( |
|||
message('error-invalidproject', project), |
|||
message('error-invalidproject-section') |
|||
) |
|||
end |
|||
end |
|||
snippets.interwikiTableKey = interwikiTableKey |
|||
return project |
|||
end |
|||
function 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 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 |
|||
function snippetFunctions.projectLong() |
|||
-- The long form of the project name, e.g. "wikipedia" or "wikibooks". |
|||
local key = getSnippet('interwikiTableKey') |
|||
if not key then |
|||
return message('snippet-projectlong-default') |
|||
end |
|||
interwikiTable = 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] |
|||
end |
|||
function snippetFunctions.lang() |
|||
-- The language code. |
|||
local lang = args.lang or args.Lang |
|||
if not lang then |
|||
return nil |
|||
end |
|||
if mw.language.isKnownLanguageTag(lang) then |
|||
return lang |
|||
else |
|||
raiseError( |
|||
message('error-invalidlanguage', lang), |
|||
message('error-invalidlanguage-section') |
|||
) |
|||
end |
|||
end |
|||
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 |
|||
-- the "snippet-lang-default" message. |
|||
return getSnippet('lang') or message('snippet-lang-default') |
|||
end |
|||
function 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 then |
|||
return nil |
|||
end |
|||
local ret = {} |
|||
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.wikpedia.org, or ja.wikibooks.org. |
|||
local fullDomain |
|||
local lang = getSnippet('toolLang') |
|||
local key = getSnippet('interwikiTableKey') |
|||
if key then |
|||
interwikiTable = interwikiTable or mw.loadData('Module:InterwikiTable') |
|||
local domain = interwikiTable[key].domain |
|||
local takesLangPrefix = interwikiTable[key].takes_lang_prefix |
|||
if takesLangPrefix then |
|||
fullDomain = lang .. '.' .. domain |
|||
else |
|||
fullDomain = domain |
|||
end |
|||
else |
|||
fullDomain = lang .. '.wikipedia.org' |
|||
end |
|||
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) |
|||
-- 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) do |
|||
for i, prefix in ipairs(t.iw_prefix) do |
|||
if s == prefix then |
|||
return s, key |
|||
end |
|||
end |
|||
end |
|||
return nil, nil |
|||
end |
end |
||
-------------------------------------------------------------------------------- |
|||
local function makeRfaLink() |
|||
-- Main functions |
|||
return '[[Special:PrefixIndex/Wikipedia:Requests for adminship/' .. u.username .. '|RfA]]' |
|||
-------------------------------------------------------------------------------- |
|||
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) |
|||
end |
|||
end |
end |
||
p.main = makeInvokeFunction('_main') |
|||
local function getLink(linktype) |
|||
local linktypes = { |
|||
function p._main(args) |
|||
t = makeTalkLink, |
|||
-- The main function. This is the one called from [[Template:User-multi]], |
|||
c = makeContribsLink, |
|||
-- via p.main. |
|||
ct = makeCountLink, |
|||
local options = p.getOptions(args) |
|||
m = makeMovesLink, |
|||
local snippets = p.getSnippets(args) |
|||
l = makeLogsLink, |
|||
local codes = p.getCodes(args) |
|||
bl = makeBlockLogLink, |
|||
local links = p.getLinks(snippets) |
|||
bls = makeBlocksLink, |
|||
-- Overload the built-in Lua error function to generate wikitext errors |
|||
bu = makeBlockUserLink, |
|||
-- meant for end users to see. This makes things harder to debug when |
|||
ca = makeCentralAuthLink, |
|||
-- real errors occur, but it is the only realistic way to show wikitext |
|||
dc = makeDeletedContribsLink, |
|||
-- errors and and still have sane code when using metatables, etc. |
|||
e = makeEmailLink, |
|||
local success, result = pcall(p.export, codes, links, options) |
|||
es = makeEditSummariesLink, |
|||
if success then |
|||
del = makeDeletionsLink, |
|||
return result |
|||
lu = makeListUserLink, |
|||
else |
|||
sul = makeSulLink, |
|||
return makeWikitextError(result, options.isDemo) |
|||
tl = makeTargetLogsLink, |
|||
end |
|||
efl = makeEditFilterLogLink, |
|||
pr = makeProtectionsLink, |
|||
rl = makeRightsLink, |
|||
ren = makeRenamesLink, |
|||
rfa = makeRfaLink |
|||
} |
|||
if not linktypes[linktype] then |
|||
return err('"' .. linktype .. '" is not a valid link code') |
|||
end |
|||
return linktypes[linktype]() |
|||
end |
end |
||
function p.getOptions(args) |
|||
-- Gets the options from the args table, so that we don't have to pass |
|||
local targs = {} |
|||
-- around the whole args table all the time. |
|||
local numArgsExist = false |
|||
local options = {} |
|||
for k, v in pairs(args) do |
|||
options.isDemo = yesno(args.demo) or false |
|||
if type(k) == 'number' then |
|||
options.toolbarStyle = yesno(args.small) and 'font-size: 90%;' or nil |
|||
numArgsExist = true |
|||
options.sup = yesno(args.sup, true) |
|||
targs[k] = getLink(v) |
|||
options.separator = args.separator |
|||
end |
|||
options.span = args.span |
|||
end |
|||
return options |
|||
targs.style = args.small and 'font-size: 90%;' |
|||
targs.separator = args.separator or 'dot' |
|||
if numArgsExist == false then |
|||
return -- Don't return a toolbar if no numeric arguments exist. |
|||
else |
|||
return ToolbarBuilder.main(targs) |
|||
end |
|||
end |
end |
||
function p.getCodes(args) |
|||
-- Gets the link codes from the arguments. The codes aren't validated |
|||
for projectKey, projectVal in pairs(interwikiTable) do |
|||
-- at this point. |
|||
for _, iwCode in ipairs(projectVal.iw_prefix) do |
|||
mTableTools = maybeLoadModule('Module:TableTools') |
|||
if iwCode == prefix then |
|||
local codes |
|||
return true |
|||
if mTableTools then |
|||
end |
|||
codes = mTableTools.compressSparseArray(args) |
|||
end |
|||
else |
|||
end |
|||
codes = {} |
|||
return false |
|||
for i, code in ipairs(args) do |
|||
codes[i] = code |
|||
end |
|||
end |
|||
return codes |
|||
end |
end |
||
function p.export(codes, links, options) |
|||
local function generateUserDataStrings(args) |
|||
-- Make the user link. |
|||
-- If the username is absent or blank, return an error and a tracking category. |
|||
local userLink = links.u |
|||
if args.user == '' or (not args.user and (not args.User or args.User == '')) then |
|||
return err('no username detected') |
|||
-- If we weren't passed any link codes, just return the user link. |
|||
else |
|||
if #codes < 1 then |
|||
u.username = args.user or args.User |
|||
return userLink |
|||
end |
|||
end |
|||
-- Get other basic user data strings. |
|||
-- Make the toolbar. |
|||
u.project = args.Project or args.project |
|||
mToolbar = require('Module:Toolbar') |
|||
u.lang = args.lang or args.Lang |
|||
local toolbarArgs = {} |
|||
for i, code in ipairs(codes) do |
|||
-- Process the project value if it is present. |
|||
local link = links[code] |
|||
if u.project then |
|||
toolbarArgs[#toolbarArgs + 1] = link |
|||
-- If u.project is a known project, we don't need to do anything. If the project isn't known, first |
|||
end |
|||
-- check whether it is a valid language code, and if not then see if it's an interwiki code |
|||
toolbarArgs.style = options.toolbarStyle |
|||
-- separated by colons, e.g. "wikt:es". |
|||
toolbarArgs.separator = options.separator or 'dot' |
|||
if not isKnownProject(u.project) then |
|||
toolbarArgs.span = options.span |
|||
if mw.language.isKnownLanguageTag(u.project) then |
|||
local toolbar = mToolbar.main(toolbarArgs) |
|||
u.lang = u.project |
|||
u.project = nil |
|||
else |
|||
-- Guess |
|||
local pref1, pref2 = mw.ustring.match( u.project, '(%w+):(%w+)' ) |
|||
if pref1 and pref2 then |
|||
if isKnownProject(pref1) and mw.language.isKnownLanguageTag(pref2) then |
|||
u.project = pref1 |
|||
u.lang = pref2 |
|||
elseif isKnownProject(pref2) and mw.language.isKnownLanguageTag(pref1) then |
|||
u.project = pref2 |
|||
u.lang = pref1 |
|||
else |
|||
return err('"' .. u.project .. '" is not a valid interwiki prefix') |
|||
end |
|||
else |
|||
return err('"' .. u.project .. '" is 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 |
|||
-- Apply the sup option. |
|||
if options.sup then |
|||
u.usernameHtml = mw.uri.encode(u.username) -- Html-encoded username. Spaces are encoded as pluses. |
|||
toolbar = '<sup>' .. toolbar .. '</sup>' |
|||
end |
|||
-- 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 ' ' |
|||
return userLink .. space .. toolbar |
|||
end |
end |
||
-------------------------------------------------------------------------------- |
|||
local function generateTrackingCategories(args) |
|||
-- Single link function |
|||
local ret = '' |
|||
-------------------------------------------------------------------------------- |
|||
if (args.Project or args.project) and not demo then |
|||
ret = ret .. '[[Category:UserLinks transclusions with project parameters]]' |
|||
p.single = makeInvokeFunction('_single') |
|||
end |
|||
return ret |
|||
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 then |
|||
return link |
|||
else |
|||
return makeWikitextError(link, options.isDemo) |
|||
end |
|||
end |
end |
||
function p.exportSingle(links, code) |
|||
-- If any errors occur, they will probably occur here. This function |
|||
local linktype = args[1] |
|||
-- exists purely so that all the errors that will occur in p._single can |
|||
if not linktype then |
|||
-- be handled using a single pcall. |
|||
return err('no link type specified') |
|||
if not code then |
|||
end |
|||
raiseError( |
|||
local result = getLink(linktype) |
|||
message('error-nolinkcode'), |
|||
result = result .. generateTrackingCategories(args) |
|||
message('error-nolinkcode-section') |
|||
return result |
|||
) |
|||
end |
|||
return links[code] |
|||
end |
end |
||
-------------------------------------------------------------------------------- |
|||
local function getLinks(args) |
|||
-- Link table |
|||
-- 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(args) |
|||
return result |
|||
end |
|||
function p.linktable() |
|||
-- Returns a wikitext table of link codes, with an example link for each |
|||
return function (frame) |
|||
-- one. This function doesn't take any arguments, so it can be accessed |
|||
-- If called via #invoke, use the args passed into the invoking template. |
|||
-- directly from wiki pages without using makeInvokeFunction. |
|||
-- Otherwise, for testing purposes, assume args are being passed directly in. |
|||
local args = {user = 'Example'} |
|||
local origArgs |
|||
local snippets = p.getSnippets(args) |
|||
if frame == mw.getCurrentFrame() then |
|||
local links = p.getLinks(snippets) |
|||
origArgs = frame:getParent().args |
|||
for k, v in pairs(frame.args) do |
|||
-- Assemble the codes and links in order |
|||
origArgs = frame.args |
|||
local firstCodes = {'u', 't', 'c'} |
|||
break |
|||
local firstLinks, firstCodesKeys = {}, {} |
|||
end |
|||
for i, code in ipairs(firstCodes) do |
|||
else |
|||
firstCodesKeys[code] = true |
|||
origArgs = frame |
|||
firstLinks[#firstLinks + 1] = {code, links[code]} |
|||
end |
|||
end |
|||
local secondLinks = {} |
|||
-- Strip whitespace, and treat blank arguments as nil. |
|||
for code, link in pairs(links) do |
|||
-- 'user', 'User', and 'separator' have different behaviour depending on |
|||
if not firstCodesKeys[code] then |
|||
-- whether they are blank or nil, so keep them as they are. |
|||
secondLinks[#secondLinks + 1] = {code, link} |
|||
local args = {} |
|||
end |
|||
for k, v in pairs(origArgs) do |
|||
end |
|||
v = mw.text.trim(v) |
|||
table.sort(secondLinks, function(t1, t2) |
|||
if v ~= '' or k == 'user' or k == 'User' or k == 'separator' then |
|||
return t1[1] < t2[1] |
|||
args[k] = v |
|||
end) |
|||
end |
|||
local links = {} |
|||
end |
|||
for i, t in ipairs(firstLinks) do |
|||
links[#links + 1] = t |
|||
demo = args.demo -- Set the demo variable. |
|||
end |
|||
-- Generate the user data strings and return any errors. |
|||
for i, t in ipairs(secondLinks) do |
|||
local dataStringError = generateUserDataStrings(args) |
|||
links[#links + 1] = t |
|||
if dataStringError then |
|||
end |
|||
return dataStringError |
|||
end |
|||
-- Output the code table in table format |
|||
local ret = {} |
|||
return func(args) |
|||
ret[#ret + 1] = '{| class="wikitable plainlinks sortable"' |
|||
end |
|||
ret[#ret + 1] = '|-' |
|||
end |
|||
ret[#ret + 1] = '! ' .. message('linktable-codeheader') |
|||
ret[#ret + 1] = '! ' .. message('linktable-previewheader') |
|||
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 |
return p |
||
main = makeWrapper(getLinks), |
|||
single = makeWrapper(getSingleLink) |
|||
} |
Revision as of 02:06, 26 August 2015
Documentation for this module may be created at Module:UserLinks/doc
--------------------------------------------------------------------------------
-- 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 -- [[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 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 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.
--
-- For documentation on how to add new link functions, please see
-- [[Module:UserLinks#Adding new links]].
----------------------------------------------------------------------------
function linkFunctions.u(snippets)
-- User page
return makeWikilink(
snippets.interwiki,
2,
snippets.username,
snippets.username
)
end
function linkFunctions.t(snippets)
-- User talk page
return makeWikilink(
snippets.interwiki,
3,
snippets.username,
message('display-talk')
)
end
function linkFunctions.c(snippets)
-- Contributions
return makeWikilink(
snippets.interwiki,
-1,
'Contribs/' .. snippets.username,
message('display-contributions')
)
end
function linkFunctions.ct(snippets)
-- Edit count
return makeUrlLink(
{
host = 'tools.wmflabs.org',
path = '/xtools-ec/',
query = {
user = snippets.username,
project = snippets.toolLang .. '.' .. snippets.projectLong .. '.org'
}
},
message('display-count')
)
end
function linkFunctions.m(snippets)
-- Page moves
return makeWikilink(
snippets.interwiki,
-1,
'Log/move/' .. snippets.username,
message('display-moves')
)
end
function linkFunctions.l(snippets)
-- Logs
return makeWikilink(
snippets.interwiki,
-1,
'Log/' .. snippets.username,
message('display-logs')
)
end
function linkFunctions.bl(snippets)
-- Block log
return makeFullUrlLink(
snippets.interwiki,
-1,
'Log/block',
{page = 'User:' .. snippets.username},
message('display-blocklog')
)
end
function linkFunctions.bls(snippets)
-- Blocks
return makeWikilink(
snippets.interwiki,
-1,
'Log/block/' .. snippets.username,
message('display-blocks')
)
end
function linkFunctions.bu(snippets)
-- Block user
return makeWikilink(
snippets.interwiki,
-1,
'Block/' .. snippets.username,
message('display-blockuser')
)
end
function linkFunctions.ca(snippets)
-- Central auth
return makeWikilink(
snippets.interwiki,
-1,
'CentralAuth/' .. snippets.username,
message('display-centralauth')
)
end
function linkFunctions.dc(snippets)
-- Deleted contribs
return makeWikilink(
snippets.interwiki,
-1,
'DeletedContributions/' .. snippets.username,
message('display-deletedcontributions')
)
end
function linkFunctions.e(snippets)
-- Email
return makeWikilink(
snippets.interwiki,
-1,
'EmailUser/' .. snippets.username,
message('display-email')
)
end
function linkFunctions.es(snippets)
-- Edit summaries
return makeUrlLink(
{
host = 'tools.wmflabs.org',
path = '/xtools/editsummary/index.php',
query = {
name = snippets.username,
lang = snippets.toolLang,
wiki = snippets.projectLong
}
},
message('display-editsummaries')
)
end
function linkFunctions.del(snippets)
-- Deletions
return makeWikilink(
snippets.interwiki,
-1,
'Log/delete/' .. snippets.username,
message('display-deletions')
)
end
function linkFunctions.lu(snippets)
-- List user
return makeFullUrlLink(
snippets.interwiki,
-1,
'ListUsers',
{limit = 1, username = snippets.username},
message('display-listuser')
)
end
function linkFunctions.sul(snippets)
-- SUL
return makeWikilink(
nil,
nil,
'sulutil:' .. snippets.username,
message('display-sul')
)
end
function linkFunctions.tl(snippets)
-- Target logs
return makeFullUrlLink(
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(
snippets.interwiki,
-1,
'AbuseLog',
{wpSearchUser = snippets.username},
message('display-abuselog')
)
end
function linkFunctions.pr(snippets)
-- Protections
return makeWikilink(
snippets.interwiki,
-1,
'Log/protect/' .. snippets.username,
message('display-protections')
)
end
function linkFunctions.rl(snippets)
-- User rights
return makeWikilink(
snippets.interwiki,
-1,
'Log/rights/' .. snippets.username,
message('display-rights')
)
end
function linkFunctions.ren(snippets)
-- Renames
return makeWikilink(
snippets.interwiki,
-1,
'Log/renameuser/' .. snippets.username,
message('display-renames')
)
end
function linkFunctions.rfa(snippets)
-- Requests for adminship
return makeWikilink(
nil,
-1,
'PrefixIndex/' .. message('page-rfa') .. '/' .. snippets.username,
message('display-rfa')
)
end
function linkFunctions.api(snippets)
-- API user data
return makeUrlLink(
{
host = snippets.fullDomain,
path = '/w/api.php',
query = {
action = 'query',
list = 'users',
usprop = 'groups|editcount',
ususers = snippets.username
}
},
message('display-api')
)
end
function linkFunctions.up(snippets)
-- Uploads
return makeWikilink(
snippets.interwiki,
-1,
'ListFiles/' .. snippets.username,
message('display-uploads')
)
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
--------------------------------------------------------------------------------
-- 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 then
snippets[key] = snippetFunction() or ''
return snippets[key]
else
raiseError(
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
-- don't have to test for false or nil.
return snippets[key] ~= ''
end
local function getSnippet(key)
local ret = snippets[key]
if ret == '' then
return nil
else
return ret
end
end
-- Start snippet functions.
function snippetFunctions.username()
-- The username.
local username = args.user or args.User
return username or raiseError(
message('error-nousername'),
message('error-nousername-section')
)
end
function snippetFunctions.usernameHtml()
-- The username html-encoded. Spaces are encoded as pluses.
return mw.uri.encode(snippets.username)
end
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
return nil
end
local projectValidated, interwikiTableKey = p.validateProjectCode(project)
if not projectValidated then
if mw.language.isKnownLanguageTag(project) then
if not snippetExists('lang') then
snippets.lang = project
end
else
raiseError(
message('error-invalidproject', project),
message('error-invalidproject-section')
)
end
end
snippets.interwikiTableKey = interwikiTableKey
return project
end
function 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 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
function snippetFunctions.projectLong()
-- The long form of the project name, e.g. "wikipedia" or "wikibooks".
local key = getSnippet('interwikiTableKey')
if not key then
return message('snippet-projectlong-default')
end
interwikiTable = 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]
end
function snippetFunctions.lang()
-- The language code.
local lang = args.lang or args.Lang
if not lang then
return nil
end
if mw.language.isKnownLanguageTag(lang) then
return lang
else
raiseError(
message('error-invalidlanguage', lang),
message('error-invalidlanguage-section')
)
end
end
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
-- the "snippet-lang-default" message.
return getSnippet('lang') or message('snippet-lang-default')
end
function 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 then
return nil
end
local ret = {}
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.wikpedia.org, or ja.wikibooks.org.
local fullDomain
local lang = getSnippet('toolLang')
local key = getSnippet('interwikiTableKey')
if key then
interwikiTable = interwikiTable or mw.loadData('Module:InterwikiTable')
local domain = interwikiTable[key].domain
local takesLangPrefix = interwikiTable[key].takes_lang_prefix
if takesLangPrefix then
fullDomain = lang .. '.' .. domain
else
fullDomain = domain
end
else
fullDomain = lang .. '.wikipedia.org'
end
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)
-- 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) do
for i, prefix in ipairs(t.iw_prefix) do
if s == prefix then
return s, key
end
end
end
return nil, nil
end
--------------------------------------------------------------------------------
-- 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)
end
end
p.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 then
return result
else
return makeWikitextError(result, options.isDemo)
end
end
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.toolbarStyle = yesno(args.small) and 'font-size: 90%;' or nil
options.sup = yesno(args.sup, true)
options.separator = args.separator
options.span = args.span
return options
end
function p.getCodes(args)
-- Gets the link codes from the arguments. The codes aren't validated
-- at this point.
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, links, options)
-- Make the user link.
local userLink = links.u
-- If we weren't passed any link codes, just return the user link.
if #codes < 1 then
return userLink
end
-- Make the toolbar.
mToolbar = require('Module:Toolbar')
local toolbarArgs = {}
for i, code in ipairs(codes) do
local link = links[code]
toolbarArgs[#toolbarArgs + 1] = link
end
toolbarArgs.style = options.toolbarStyle
toolbarArgs.separator = options.separator or 'dot'
toolbarArgs.span = options.span
local toolbar = mToolbar.main(toolbarArgs)
-- Apply the sup option.
if options.sup then
toolbar = '<sup>' .. toolbar .. '</sup>'
end
-- 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 ' '
return userLink .. space .. toolbar
end
--------------------------------------------------------------------------------
-- 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 then
return link
else
return makeWikitextError(link, options.isDemo)
end
end
function 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 then
raiseError(
message('error-nolinkcode'),
message('error-nolinkcode-section')
)
end
return links[code]
end
--------------------------------------------------------------------------------
-- Link table
--------------------------------------------------------------------------------
function p.linktable()
-- Returns a wikitext table of link codes, with an example link for each
-- one. This function doesn't take any arguments, so it can be accessed
-- directly from wiki pages without using makeInvokeFunction.
local args = {user = 'Example'}
local snippets = p.getSnippets(args)
local links = p.getLinks(snippets)
-- 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
firstLinks[#firstLinks + 1] = {code, links[code]}
end
local secondLinks = {}
for code, link in pairs(links) do
if not firstCodesKeys[code] then
secondLinks[#secondLinks + 1] = {code, link}
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] = '! ' .. message('linktable-codeheader')
ret[#ret + 1] = '! ' .. message('linktable-previewheader')
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