Module:Extension
Revision as of 01:50, 24 June 2020 by >Pppery (Undid revision 3925041 by André Costa (WMSE) (talk) Premature, change should not be pushed to module until template change is about to happen)
Documentation for this module may be created at Module:Extension/doc
local lang = mw.language.getContentLanguage()
local translation = mw.getCurrentFrame():callParserFunction{name='#translation', args="1"}
local addr = {
GNU = '//www.gnu.org/licenses/',
OSI = '//opensource.org/licenses/',
CC = '//creativecommons.org/licenses/',
Mozilla = '//www.mozilla.org/'
}
local cats = {
GPL = 'GPL licensed extensions',
FDL = 'FDL licensed extensions',
LGPL = 'LGPL licensed extensions',
AGPL = 'AGPL licensed extensions',
MIT = 'MIT licensed extensions',
ISC = 'ISC licensed extensions',
BSD = 'BSD licensed extensions',
MPL = 'MPL licensed extensions',
WTFPL = 'WTFPL licensed extensions',
Apache = 'Apache licensed extensions',
PD = 'Public domain licensed extensions',
CC = 'Creative Commons licensed extensions',
ECL = 'Educational Community licensed extensions',
BLANK = 'Extensions with no license specified'
}
local licenses = {
['GPL-2.0'] = { addr.GNU .. 'old-licenses/gpl-2.0-standalone.html', 'GNU General Public License 2.0', 'GPL' },
['GPL-2.0-only'] = { addr.GNU .. 'old-licenses/gpl-2.0-standalone.html', 'GNU General Public License 2.0 only', 'GPL' },
['GPL-2.0-or-later'] = { addr.GNU .. 'old-licenses/gpl-2.0-standalone.html', 'GNU General Public License 2.0 or later', 'GPL' },
['GPL-3.0'] = { addr.GNU .. 'gpl-3.0-standalone.html', 'GNU General Public License 3.0', 'GPL' },
['GPL-3.0-only'] = { addr.GNU .. 'gpl-3.0-standalone.html', 'GNU General Public License 3.0 only', 'GPL' },
['GPL-3.0-or-later'] = { addr.GNU .. 'gpl-3.0-standalone.html', 'GNU General Public License 3.0 or later', 'GPL' },
['AGPL-3.0'] = { addr.GNU .. 'agpl-3.0.html', 'GNU Affero General Public License 3.0', 'AGPL' },
['AGPL-3.0-or-later'] = { addr.GNU .. 'agpl-3.0.html', 'GNU Affero General Public License 3.0 or later', 'AGPL' },
['LGPL-2.1'] = { addr.GNU .. 'old-licenses/lgpl-2.1-standalone.html', 'GNU Lesser General Public License 2.1', 'LGPL' },
['LGPL-2.1-only'] = { addr.GNU .. 'old-licenses/lgpl-2.1-standalone.html', 'GNU Lesser General Public License 2.1 only', 'LGPL' },
['LGPL-2.1-or-later'] = { addr.GNU .. 'old-licenses/lgpl-2.1-standalone.html', 'GNU Lesser General Public License 2.1 or later', 'LGPL' },
['LGPL-3.0'] = { addr.GNU .. 'lgpl-3.0-standalone.html', 'GNU Lesser General Public License 3.0', 'LGPL' },
['LGPL-3.0-only'] = { addr.GNU .. 'lgpl-3.0-standalone.html', 'GNU Lesser General Public License 3.0 only', 'LGPL' },
['LGPL-3.0-or-later'] = { addr.GNU .. 'lgpl-3.0-standalone.html', 'GNU Lesser General Public License 3.0 or later', 'LGPL' },
['FDL'] = { addr.GNU .. 'fdl.html', 'GNU Free Documentation License', 'FDL' },
['MIT'] = { addr.OSI .. 'mit-license.php', 'MIT License', 'MIT' },
['ISC'] = { addr.OSI .. 'ISC', 'ISC License', 'ISC' },
['BSD-2-Clause'] = { addr.OSI .. 'BSD-2-Clause', 'BSD 2-clause "Simplified" License', 'BSD' },
['BSD-3-Clause'] = { addr.OSI .. 'BSD-3-Clause', 'BSD 3-clause "Modified" License', 'BSD' },
['BSD-4-Clause'] = { addr.GNU .. 'license-list.html#OriginalBSD', 'BSD 4-clause "Original" License', 'BSD' },
['MPL-1.0'] = { addr.Mozilla .. 'MPL/1.0/', 'Mozilla Public License 1.0', 'MPL' },
['MPL-2.0'] = { addr.Mozilla .. 'MPL/2.0/', 'Mozilla Public License 2.0', 'MPL' },
['WTFPL'] = { 'http://www.wtfpl.net', 'WTFPL 2.0', 'WTFPL' },
['Apache-2.0'] = { '//www.apache.org/licenses/LICENSE-2.0', 'Apache License 2.0', 'Apache' },
['Zlib'] = { addr.OSI .. 'Zlib', 'zlib License' },
['CC0-1.0'] = { '//creativecommons.org/publicdomain/zero/1.0/', 'Creative Commons Zero v1.0 Universal', 'PD' },
['CC-BY-3.0'] = { addr.CC .. 'by/3.0/', 'Creative Commons Attribution 3.0', 'CC' },
['CC-BY-SA-2.0'] = { addr.CC .. 'by-sa/2.0/', 'Creative Commons Attribution Share Alike 2.0', 'CC' },
['CC-BY-SA-2.5'] = { addr.CC .. 'by-sa/2.5/', 'Creative Commons Attribution Share Alike 2.5', 'CC' },
['CC-BY-NC-SA-2.5'] = { addr.CC .. 'by-nc-sa/2.5/', 'Creative Commons Attribution NonCommercial Share Alike 2.5', 'CC' },
['CC-BY-NC-SA-3.0'] = { addr.CC .. 'by-nc-sa/3.0/', 'Creative Commons Attribution NonCommercial Share Alike 3.0', 'CC' },
['CC-BY-NC-3.0'] = { addr.CC .. 'by-nc/3.0/', 'Creative Commons Attribution NonCommercial 3.0', 'CC' },
['CC-BY-SA-3.0'] = { addr.CC .. 'by-sa/3.0/', 'Creative Commons Attribution Share Alike 3.0', 'CC' },
['CC-BY-SA-4.0'] = { addr.CC .. 'by-sa/4.0/', 'Creative Commons Attribution Share Alike 4.0', 'CC' },
['ECL-2.0'] = { '', '[[wikipedia:Educational Community License|Educational Community License 2.0]]', 'ECL' },
['PD'] = { '', '[[wikipedia:Public domain|Public domain]]', 'PD' },
['unspecified'] = { '', 'No license specified', 'BLANK'}
}
local types = {
ajax = { '[[w:AJAX|Ajax]]', 'Ajax extensions' },
api = { '[[API:Action API|API]]', 'API extensions' },
['beta feature'] = { '[[Beta Features|Beta Feature]]', 'Beta Feature extensions' },
contenthandler = { '[[Manual:ContentHandler|ContentHandler]]', 'ContentHandler extensions' },
database = { '[[Manual:Database layout|Database]]', 'Database extensions' },
['data extraction'] = { 'Data extraction', 'Data extraction extensions' },
example = { 'Example', 'Extension examples' },
['extended syntax'] = { '[[Manual:Extending wiki markup|Extended syntax]]', 'Extended syntax extensions' },
filerepo = { 'File repository', 'File repository extensions' },
hook = { '[[Manual:Hooks|Hook]]', 'Hook extensions' },
interface = { 'User interface', 'User interface extensions' },
link = { '[[Manual:Extending wiki markup|Link markup]]', 'Link markup extensions' },
media = { 'Media', 'Media handling extensions' },
mywiki = { '[[Manual:Personalization|MyWiki]]', 'Personalization extensions' },
notify = { 'Notify', 'Notification extensions' },
['page action'] = { '[[Manual:Parameters to index.php#Actions|Page action]]', 'Page action extensions' },
parser = { '[[Manual:Extending wiki markup|Parser extension]]', 'Parser extensions' },
['parser function'] = { '[[Manual:Parser functions|Parser function]]', 'Parser function extensions' },
php = { 'PHP', 'PHP extensions' },
search = { 'Search', 'Search extensions' },
skin = { '[[Manual:Skins|Skin]]', 'Skin extensions' },
['special page'] = { '[[Manual:Special pages|Special page]]', 'Special page extensions' },
locale = { '[[Manual:Localization|Locale]]', 'Internationalization extensions' },
tag = { '[[Manual:Tag extensions|Tag]]', 'Tag extensions' },
['user access'] = { '[[Manual:Security|User access]]', 'User access extensions' },
['user identity'] = { '[[Manual:Security|User identity]]', 'User identity extensions' },
['user rights'] = { '[[Manual:Security|User rights]]', 'User rights extensions' },
['user activity'] = { '[[Manual:Security|User activity]]', 'User activity extensions' },
variable = { '[[Manual:Variables|Variable]]', 'Variable extensions' },
-- DEPRECATED TYPES (as per 2007-09 taxonomy discussion)
category = { 'Category', 'Category extensions', deprecated = true },
form = { 'Form', 'Form extensions', deprecated = true },
list = { 'List', 'List extensions', deprecated = true },
namespace = { '[[Manual:Namespaces|Namespace]]', 'Namespace extensions', deprecated = true },
['table'] = { 'Table', 'Table extensions', deprecated = true },
}
local typeAliases = {
db = 'database',
pfunc = 'parser function',
special = 'special page',
}
-- DEPRECATED TYPES (multiple types handled via type1,type2,...)
local deprecatedTypes = {
['link, tag, special'] = { 'link', 'tag', 'special page' },
['parser function, special'] = { 'parser function', 'special page' },
['tag, parser function'] = { 'tag', 'parser function' },
['tag, parser function, special'] = { 'tag', 'parser function', 'special page' },
['tag, special'] = { 'tag', 'special page' },
['parser, pfunc'] = { 'parser function' },
['parser, hook, special'] = { 'tag' --[[ sic ]], 'hook', 'special page' },
}
local function setI18n( from, to, index )
for n, v in pairs( from ) do
if to[n] then
to[n][index] = v
end
end
end
local function cat( title )
return '[[Category:' .. title .. ']]'
end
local function tcat( title )
return cat( title .. translation )
end
local function getType( str, str2 )
local str = mw.ustring.lower( str )
if typeAliases[str] then
str = typeAliases[str]
end
local cnf = types[str]
local res
if cnf then
res = cnf[1] .. '[[Category:' .. cnf[2] .. translation .. ']]'
if cnf.deprecated then
res = res .. '\'\'-deprecated\'\'' ..
tcat( 'Extensions with deprecated types' )
end
else
cnf = deprecatedTypes[str]
if cnf then
local dtypes = {}
for _, sstr in ipairs( cnf ) do
table.insert( dtypes, getType( sstr ) )
end
res = table.concat( dtypes, ', ' ) ..
'<br />\'\'(deprecated, please use [[Special:MyLanguage/Template:Extension/doc#type|type1,type2'
if #cnf > 2 then
res = res .. ',type3'
end
res = res .. ']] instead)\'\'' .. tcat( 'Extensions with deprecated types' )
elseif str == '_missing_' then
res = tcat( 'Extensions with invalid or missing type' )
elseif str == '_demomode_' then
if str2 then
res = lang:ucfirst( str2 )
else
res = "''unknown''"
end
else
res = ( str or '\'\'unknown\'\'' ) ..
' [[Special:MyLanguage/Template:Extension/doc#type|(\'\'\'\'\'invalid type\'\'\'\'\')]]' ..
tcat( 'Extensions with invalid or missing type' )
end
end
return res
end
local function getExtData()
local pg
local pframe = mw.getCurrentFrame():getParent()
if pframe and pframe.args.repo then
pg = pframe.args.repo
else
pg = mw.title.getCurrentTitle().rootPageTitle:partialUrl() -- need to get rid of translation subpage.
end
return mw.loadData( 'Module:ExtensionJson' )[pg]
end
local function getLicenseString (str)
str = mw.text.trim(str)
if str == "" or str == nil then
local data = getExtData()
if data and data["license-name"] then
str = data["license-name"]
else
str = "unspecified"
end
end
return str
end
local function getLicenseCategory( str )
str = getLicenseString(str)
if mw.ustring.sub( str, -1 ) == '+' then
str = mw.ustring.sub( str, 1, -2 )
end
local cnf = licenses[str]
if cnf then
if #cnf > 2 then
return tcat( cats[cnf[3]] )
end
else
return tcat( 'Extensions with unknown license' )
end
end
local function getFormattedLicense( str, orlatertext )
local orlater = ''
local license = getLicenseString(str)
if mw.ustring.sub( license, -1 ) == '+' then
license = mw.ustring.sub( license, 1, -2 )
orlater = orlatertext
end
local cnf = licenses[license]
if cnf then
return (cnf[1] ~= '' and ('[' .. cnf[1] .. ' ' .. cnf[2] .. ']') or cnf[2]) .. orlater
else
return license
end
end
local p = {}
function p.getTypes( frame )
setI18n( frame.args, types, 1 )
local args = frame:getParent().args
local types = {}
local params = {
args.type1 or args['type'] or 'missing',
args.type2,
args.type3,
args.type4,
args.type5,
args.type6,
}
for _, param in ipairs( params ) do
if param == nil or mw.text.trim( param ) == '' then
break
end
local param = mw.text.trim( param )
if args.templatemode == 'nocats' then
table.insert( types, getType( '_demomode_', param ) )
else
table.insert( types, getType( param ) )
end
end
return table.concat( types, ', ' )
end
function p.getType( frame )
setI18n( frame.args, types, 1 )
return getType( frame.args[1] )
end
function p.getLicenseCategory( frame )
return getLicenseCategory( frame.args[1] )
end
function p.getFormattedLicense( frame )
setI18n( frame.args, licenses, 2 )
return getFormattedLicense( frame.args[1], frame.args['+'] or ' or later' )
end
-- Return if the extension does schema updates
-- Only answer yes. For now be silent on no or unknown, as its unclear
-- if this info should be in infobox if the answer is not yes.
function p.getNeedsUpdates( frame )
local data = getExtData()
if data ~= nil and data.Hooks ~= nil and data.Hooks.LoadExtensionSchemaUpdates ~= nil then
return 'yes'
end
return ''
end
function p.getVersion( frame )
if frame.args[1] ~= nil and mw.text.trim(frame.args[1]) ~= "" then
return frame.args[1]
end
local data = getExtData()
if data ~= nil and data.version ~= nil then
return data.version
end
return ''
end
function p.getHooks(frame)
local hookOutput = frame.args.header
local hooks = {}
local index = 1
local pframe = frame:getParent()
local foundLocalHooks = false
while true do
local argument = pframe.args["hook" .. index]
if argument and mw.text.trim(argument) ~= "" then
hooks[#hooks + 1] = mw.text.trim(argument)
foundLocalHooks = true
else
break
end
index = index + 1
end
if not foundLocalHooks then
local data = getExtData()
if data == nil or data.Hooks == nil then
return ""
end
for hook, _ in pairs(data.Hooks) do
hooks[#hooks + 1] = hook
end
table.sort(hooks)
end
local first = true
for _, hook in ipairs(hooks) do
if first then
first = false
else
hookOutput = hookOutput .. frame.args.delim
end
hookOutput = hookOutput .. frame:expandTemplate{title="Extension/HookInUse",args={hook,templatemode=pframe.args.templatemode}}
end
return hookOutput .. frame.args.footer
end
function p.getParameters(frame)
local data = getExtData()
if data == nil then
return ""
end
local config = data.config
if config == nil then
return ""
end
local prefix = "wg"
local skip_prefix = false
if data.manifest_version and data.manifest_version >= 2 then
if data.config_prefix then
prefix = data.config_prefix
end
else
if config._prefix then
prefix = config._prefix
skip_prefix = true
end
end
local out = ""
for key, _ in pairs(config) do
if key ~= '_prefix' or not skip_prefix then
out = out .. "* $" .. prefix .. key .. "\n"
end
end
return out
end
function p.getRights(frame)
local data = getExtData()
if data == nil then
return ""
end
local rights = data.AvailableRights
if rights == nil then
return ""
end
local out = ""
-- [[Module:ExtensionJson]] starts at zero, but Lua tables start at 1
local iter, table_ = ipairs(rights)
for _, right in iter, table_, -1 do
out = out .. "* " .. right .. "\n"
end
return out
end
function p.unmaintained(frame)
local content = mw.title.getCurrentTitle():getContent()
if not content:find("{{[uU]nmaintained extension") and not content:find("{{TNT|[uU]nmaintained extension")
and not content:find("{{User:Jeroen[ _]De[ _]Dauw/unmaintained") then
local args = {}
if frame:getParent().args.templatemode == "nocats" then
args.nocat = "yes"
end
return frame:expandTemplate{title="Unmaintained extension",args=args}
end
end
function p.maintenanceLinks(frame)
local base = frame:expandTemplate{title="translatable"}
if base == mw.title.getCurrentTitle().prefixedText then
return
end
local out = ""
local content = mw.title.new(base):getContent()
-- Check if the source page was deleted or not
if content:find("{{[aA]rchived ?[Ee]xtension") or content:find("{{TNT|[Aa]rchived ?[Ee]xtension") then
return "<span style='display:none'>[[Template:Extension/archived]]</span>"
--Check if the source page was archived or not
elseif content:find("{{[dD]eleted extension security warning") then
return "<span style='display:none'>[[Template:Extension/vulnerabilities]]</span>"
end
end
function p.isOnGerrit(frame)
local title = mw.title.getCurrentTitle()
if not title:inNamespace("Extension") then
return "n/a"
end
local base = frame:expandTemplate{title="translatable"}
local content = mw.title.new(base):getContent()
if content:find("{{WikimediaDownload") or content:find("TNT|WikimediaDownload") or content:find("|repo%s*=") then
return "yes"
end
end
return p