Difference between revisions of "Module:Extension"

From WPRDC Wiki
Jump to navigation Jump to search
>Tacsipacsi
(i18n using parameters from Template:Extension, define aliases for essentially same types)
>P858snake
m (Alpha sort licenses)
 
(29 intermediate revisions by 7 users not shown)
Line 1: Line 1:
local lang = mw.language.getContentLanguage()
local lang = mw.language.getContentLanguage()
--local translation = mw.getCurrentFrame():expandTemplate{ title = 'translation' }
local translation = mw.getCurrentFrame():callParserFunction{name='#translation', args="1"}
local translation = mw.getCurrentFrame():callParserFunction{name='#translation', args="1"}
local addr = {
local addr = {
Line 22: Line 21:
CC    = 'Creative Commons licensed extensions',
CC    = 'Creative Commons licensed extensions',
ECL    = 'Educational Community licensed extensions',
ECL    = 'Educational Community licensed extensions',
Unlicense = 'The Unlicense licensed extensions',
BLANK  = 'Extensions with no license specified'
}
}
local licenses = {
local licenses = {
['AGPL-3.0']    = { addr.GNU .. 'agpl-3.0.html', 'GNU Affero General Public License 3.0', 'AGPL' },
['AGPL-3.0-only']    = { 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' },
['Apache-2.0']  = { '//www.apache.org/licenses/LICENSE-2.0', 'Apache License 2.0', 'Apache' },
['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' },
['CC-BY-3.0']    = { addr.CC .. 'by/3.0/', 'Creative Commons Attribution 3.0', 'CC' },
['CC-BY-3.0-US'] = { addr.CC .. 'by/3.0/us/', 'Creative Commons Attribution 3.0 United States', 'CC' },
['CC-BY-NC-3.0'] = { addr.CC .. 'by-nc/3.0/', 'Creative Commons Attribution NonCommercial 3.0', '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-SA-4.0'] = { addr.CC .. 'by-nc-sa/4.0/', 'Creative Commons Attribution NonCommercial Share Alike 4.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.0-UK'] = { addr.CC .. 'by-sa/2.0/uk/', 'Creative Commons Attribution Share Alike 2.0 England and Wales', 'CC' },
['CC-BY-SA-2.5'] = { addr.CC .. 'by-sa/2.5/', 'Creative Commons Attribution Share Alike 2.5', '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' },
['CC0-1.0']      = { '//creativecommons.org/publicdomain/zero/1.0/', 'Creative Commons Zero v1.0 Universal', 'PD' },
['ECL-2.0']      = { '', '[[wikipedia:Educational Community License|Educational Community License 2.0]]', 'ECL' },
['FDL']          = { addr.GNU .. 'fdl.html', 'GNU Free Documentation License', 'FDL' },
['GPL-2.0']      = { addr.GNU .. 'old-licenses/gpl-2.0-standalone.html', 'GNU General Public License 2.0', 'GPL' },
['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-only'] = { addr.GNU .. 'old-licenses/gpl-2.0-standalone.html', 'GNU General Public License 2.0 only', 'GPL' },
Line 30: Line 52:
['GPL-3.0-only'] = { addr.GNU .. 'gpl-3.0-standalone.html', 'GNU General Public License 3.0 only', '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' },
['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' },
['ISC']         = { addr.OSI .. 'ISC', 'ISC License', 'ISC' },
['LGPL-2.1']    = { addr.GNU .. 'old-licenses/lgpl-2.1-standalone.html', 'GNU Lesser General Public License 2.1', 'LGPL' },
['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-only'] = { addr.GNU .. 'old-licenses/lgpl-2.1-standalone.html', 'GNU Lesser General Public License 2.1 only', 'LGPL' },
Line 37: Line 59:
['LGPL-3.0-only'] = { addr.GNU .. 'lgpl-3.0-standalone.html', 'GNU Lesser General Public License 3.0 only', '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' },
['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' },
['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-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' },
['MPL-2.0']      = { addr.Mozilla .. 'MPL/2.0/', 'Mozilla Public License 2.0', 'MPL' },
['PD']          = { '', '[[wikipedia:Public domain|Public domain]]', 'PD' },
['Unlicense']    = { 'https://unlicense.org/', 'The Unlicense', 'Unlicense' },
['WTFPL']        = { 'http://www.wtfpl.net', 'WTFPL 2.0', 'WTFPL' },
['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' },
['Zlib']        = { addr.OSI .. 'Zlib', 'zlib License' },
['CC0-1.0']     = { '//creativecommons.org/publicdomain/zero/1.0/', 'Creative Commons Zero v1.0 Universal', 'PD' },
['unspecified'] = { '', 'No license specified', 'BLANK'}
['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' },
}
}
local types = {
local types = {
Line 90: Line 98:
['user activity']  = { '[[Manual:Security|User activity]]',                      'User activity extensions' },
['user activity']  = { '[[Manual:Security|User activity]]',                      'User activity extensions' },
variable            = { '[[Manual:Variables|Variable]]',                          'Variable 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 },
}
}


Line 102: Line 104:
pfunc = 'parser function',
pfunc = 'parser function',
special = 'special page',
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' },
}
}


Line 140: Line 131:
if cnf then
if cnf then
res = cnf[1] .. '[[Category:' .. cnf[2] .. translation .. ']]'
res = cnf[1] .. '[[Category:' .. cnf[2] .. translation .. ']]'
if cnf.deprecated then
res = res .. '\'\'-deprecated\'\'' ..
tcat( 'Extensions with deprecated types' )
end
else
else
cnf = deprecatedTypes[str]
if str == '_missing_' then
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' )
res = tcat( 'Extensions with invalid or missing type' )
elseif str == '_demomode_' then
elseif str == '_demomode_' then
Line 174: Line 149:
end
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 )
local function getLicenseCategory( str )
str = getLicenseString(str)
if mw.ustring.sub( str, -1 ) == '+' then
if mw.ustring.sub( str, -1 ) == '+' then
str = mw.ustring.sub( str, 1, -2 )
str = mw.ustring.sub( str, 1, -2 )
Line 190: Line 189:
local function getFormattedLicense( str, orlatertext )
local function getFormattedLicense( str, orlatertext )
local orlater = ''
local orlater = ''
local license = str
local license = getLicenseString(str)
if mw.ustring.sub( str, -1 ) == '+' then
if mw.ustring.sub( license, -1 ) == '+' then
license = mw.ustring.sub( str, 1, -2 )
license = mw.ustring.sub( license, 1, -2 )
orlater = orlatertext
orlater = orlatertext
end
end
Line 199: Line 198:
return (cnf[1] ~= '' and ('[' .. cnf[1] .. ' ' .. cnf[2] .. ']') or cnf[2]) .. orlater
return (cnf[1] ~= '' and ('[' .. cnf[1] .. ' ' .. cnf[2] .. ']') or cnf[2]) .. orlater
else
else
return str
return license
end
end
end
end
Line 242: Line 241:
function p.getFormattedLicense( frame )
function p.getFormattedLicense( frame )
setI18n( frame.args, licenses, 2 )
setI18n( frame.args, licenses, 2 )
return getFormattedLicense( frame.args[1] and mw.text.trim( frame.args[1] ) or nil, frame.args['+'] or ' or later' )
return getFormattedLicense( frame.args[1], frame.args['+'] or ' or later' )
end
end


function getExtData()
local pg = mw.title.getCurrentTitle().rootPageTitle:partialUrl() -- need to get rid of translation subpage.
return mw.loadData( 'Module:ExtensionJson' )[pg]
end


-- Return if the extension does schema updates
-- Return if the extension does schema updates
Line 271: Line 266:
     end
     end
     return ''
     return ''
end
-- --
-- Get the requires.MediaWiki value from extension.json
-- @link https://www.mediawiki.org/wiki/Manual:Extension.json/Schema#requires
-- --
function p.getMediaWikiRequirement( frame )
-- If the first arg is given, it'll be the manual override value.
    if frame.args[1] ~= nil and mw.text.trim( frame.args[1] ) ~= "" then
    return frame.args[1] .. cat( 'Extensions with manual MediaWiki version' )
    end
-- Otherwise, look it up from extension.json.
local data = getExtData()
if data and data.requires and data.requires.MediaWiki then
return data.requires.MediaWiki
end
-- If neither are given, just categorize.
return cat( 'Extensions without MediaWiki version' )
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 = {}
local pargs = frame:getParent().args
if pargs.templatemode == "nocats" then
args.nocat = "yes"
end
args.alternative = pargs.alternative
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 archived 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 deleted 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") and not title:inNamespace("Skin") 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
-- --
-- Get a category if the extension isn't in Module:ExtensionJson.
--
function p.getExtensionJsonCategory( frame )
if getExtData() == nil then
return cat( 'Extensions not in ExtensionJson' )
end
end
end


return p
return p

Latest revision as of 13:40, 3 October 2021

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',
	Unlicense = 'The Unlicense licensed extensions',
	BLANK  = 'Extensions with no license specified'
}
local licenses = {
	['AGPL-3.0']     = { addr.GNU .. 'agpl-3.0.html', 'GNU Affero General Public License 3.0', 'AGPL' },
	['AGPL-3.0-only']     = { 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' },
	['Apache-2.0']   = { '//www.apache.org/licenses/LICENSE-2.0', 'Apache License 2.0', 'Apache' },
	['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' },
	['CC-BY-3.0']    = { addr.CC .. 'by/3.0/', 'Creative Commons Attribution 3.0', 'CC' },
	['CC-BY-3.0-US'] = { addr.CC .. 'by/3.0/us/', 'Creative Commons Attribution 3.0 United States', 'CC' },
	['CC-BY-NC-3.0'] = { addr.CC .. 'by-nc/3.0/', 'Creative Commons Attribution NonCommercial 3.0', '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-SA-4.0'] = { addr.CC .. 'by-nc-sa/4.0/', 'Creative Commons Attribution NonCommercial Share Alike 4.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.0-UK'] = { addr.CC .. 'by-sa/2.0/uk/', 'Creative Commons Attribution Share Alike 2.0 England and Wales', 'CC' },
	['CC-BY-SA-2.5'] = { addr.CC .. 'by-sa/2.5/', 'Creative Commons Attribution Share Alike 2.5', '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' },
	['CC0-1.0']      = { '//creativecommons.org/publicdomain/zero/1.0/', 'Creative Commons Zero v1.0 Universal', 'PD' },
	['ECL-2.0']      = { '', '[[wikipedia:Educational Community License|Educational Community License 2.0]]', 'ECL' },
	['FDL']          = { addr.GNU .. 'fdl.html', 'GNU Free Documentation License', 'FDL' },
	['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' },
	['ISC']          = { addr.OSI .. 'ISC', 'ISC License', 'ISC' },
	['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' },
	['MIT']          = { addr.OSI .. 'mit-license.php', 'MIT License', 'MIT' },
	['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' },
	['PD']           = { '', '[[wikipedia:Public domain|Public domain]]', 'PD' },
	['Unlicense']    = { 'https://unlicense.org/', 'The Unlicense', 'Unlicense' },
	['WTFPL']        = { 'http://www.wtfpl.net', 'WTFPL 2.0', 'WTFPL' },
	['Zlib']         = { addr.OSI .. 'Zlib', 'zlib License' },
	['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' },
}

local typeAliases = {
	db = 'database',
	pfunc = 'parser function',
	special = '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 .. ']]'
	else
		if 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

-- --
-- Get the requires.MediaWiki value from extension.json
-- @link https://www.mediawiki.org/wiki/Manual:Extension.json/Schema#requires
-- --
function p.getMediaWikiRequirement( frame )
	-- If the first arg is given, it'll be the manual override value.
    if frame.args[1] ~= nil and mw.text.trim( frame.args[1] ) ~= "" then
    	return frame.args[1] .. cat( 'Extensions with manual MediaWiki version' )
    end
	-- Otherwise, look it up from extension.json.
	local data = getExtData()
	if data and data.requires and data.requires.MediaWiki then
		return data.requires.MediaWiki
	end
	-- If neither are given, just categorize.
	return cat( 'Extensions without MediaWiki version' )
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 = {}
		local pargs = frame:getParent().args
		if pargs.templatemode == "nocats" then
			args.nocat = "yes"
		end
		args.alternative = pargs.alternative
		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 archived 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 deleted 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") and not title:inNamespace("Skin") 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

-- --
-- Get a category if the extension isn't in Module:ExtensionJson.
--
function p.getExtensionJsonCategory( frame )
	if getExtData() == nil then
		return cat( 'Extensions not in ExtensionJson' )
	end
end

return p