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