Difference between revisions of "Module:Extension"
Jump to navigation
Jump to search
>Ricordisamoa (+WTFPL) |
>P858snake m (Alpha sort licenses) |
||
(58 intermediate revisions by 15 users not shown) | |||
Line 1: | Line 1: | ||
local lang = mw.language.getContentLanguage() | local lang = mw.language.getContentLanguage() | ||
local translation = mw.getCurrentFrame(): | local translation = mw.getCurrentFrame():callParserFunction{name='#translation', args="1"} | ||
local addr = { | local addr = { | ||
GNU = '//www.gnu.org/licenses/', | GNU = '//www.gnu.org/licenses/', | ||
OSI = ' | OSI = '//opensource.org/licenses/', | ||
CC = '//creativecommons.org/licenses/', | CC = '//creativecommons.org/licenses/', | ||
Mozilla = '//www.mozilla.org/' | Mozilla = '//www.mozilla.org/' | ||
Line 9: | Line 9: | ||
local cats = { | local cats = { | ||
GPL = 'GPL licensed extensions', | GPL = 'GPL licensed extensions', | ||
FDL = 'FDL licensed extensions', | |||
LGPL = 'LGPL licensed extensions', | LGPL = 'LGPL licensed extensions', | ||
AGPL = 'AGPL licensed extensions', | AGPL = 'AGPL licensed extensions', | ||
MIT = 'MIT licensed extensions', | MIT = 'MIT licensed extensions', | ||
ISC = 'ISC licensed extensions', | |||
BSD = 'BSD licensed extensions', | BSD = 'BSD licensed extensions', | ||
MPL = 'MPL licensed extensions', | MPL = 'MPL licensed extensions', | ||
Line 19: | 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' }, | ||
['FDL'] = { | ['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 = { | local types = { | ||
ajax = { '[[w:AJAX|Ajax]]', 'Ajax extensions' }, | ajax = { '[[w:AJAX|Ajax]]', 'Ajax extensions' }, | ||
api = { '[[API: | api = { '[[API:Action API|API]]', 'API extensions' }, | ||
['beta feature'] = { '[[Beta Features|Beta Feature]]', 'Beta Feature extensions' }, | ['beta feature'] = { '[[Beta Features|Beta Feature]]', 'Beta Feature extensions' }, | ||
contenthandler = { '[[Manual:ContentHandler|ContentHandler]]', 'ContentHandler extensions' }, | contenthandler = { '[[Manual:ContentHandler|ContentHandler]]', 'ContentHandler extensions' }, | ||
database = { '[[Manual:Database layout|Database]]', 'Database extensions' }, | database = { '[[Manual:Database layout|Database]]', 'Database extensions' }, | ||
['data extraction'] = { 'Data extraction', 'Data extraction extensions' }, | ['data extraction'] = { 'Data extraction', 'Data extraction extensions' }, | ||
example = { 'Example', 'Extension examples' }, | example = { 'Example', 'Extension examples' }, | ||
Line 65: | Line 87: | ||
parser = { '[[Manual:Extending wiki markup|Parser extension]]', 'Parser extensions' }, | parser = { '[[Manual:Extending wiki markup|Parser extension]]', 'Parser extensions' }, | ||
['parser function'] = { '[[Manual:Parser functions|Parser function]]', 'Parser function extensions' }, | ['parser function'] = { '[[Manual:Parser functions|Parser function]]', 'Parser function extensions' }, | ||
php = { 'PHP', 'PHP extensions' }, | |||
search = { 'Search', 'Search extensions' }, | search = { 'Search', 'Search extensions' }, | ||
skin = { '[[Manual:Skins|Skin]]', 'Skin extensions' }, | skin = { '[[Manual:Skins|Skin]]', 'Skin extensions' }, | ||
['special page'] = { '[[Manual:Special pages|Special page]]', 'Special page extensions' }, | ['special page'] = { '[[Manual:Special pages|Special page]]', 'Special page extensions' }, | ||
locale = { '[[Manual:Localization|Locale]]', 'Internationalization extensions' }, | locale = { '[[Manual:Localization|Locale]]', 'Internationalization extensions' }, | ||
Line 77: | 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' }, | ||
} | } | ||
local typeAliases = { | |||
local | 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 ) | local function cat( title ) | ||
Line 106: | Line 124: | ||
local function getType( str, str2 ) | local function getType( str, str2 ) | ||
local str = mw.ustring.lower( str ) | local str = mw.ustring.lower( str ) | ||
if typeAliases[str] then | |||
str = typeAliases[str] | |||
end | |||
local cnf = types[str] | local cnf = types[str] | ||
local res | local res | ||
if cnf then | if cnf then | ||
res = cnf[1] .. '[[Category:' .. cnf[2] .. translation .. ']]' | res = cnf[1] .. '[[Category:' .. cnf[2] .. translation .. ']]' | ||
else | else | ||
if str == '_missing_' then | |||
if | |||
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 137: | Line 142: | ||
else | else | ||
res = ( str or '\'\'unknown\'\'' ) .. | res = ( str or '\'\'unknown\'\'' ) .. | ||
' [[Template:Extension/doc#type|(\'\'\'\'\'invalid type\'\'\'\'\')]]' .. | ' [[Special:MyLanguage/Template:Extension/doc#type|(\'\'\'\'\'invalid type\'\'\'\'\')]]' .. | ||
tcat( 'Extensions with invalid or missing type' ) | tcat( 'Extensions with invalid or missing type' ) | ||
end | end | ||
Line 144: | 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 | |||
str = mw.ustring.sub( str, 1, -2 ) | |||
end | |||
local cnf = licenses[str] | local cnf = licenses[str] | ||
if cnf then | if cnf then | ||
if #cnf > | if #cnf > 2 then | ||
return tcat( cats[cnf[ | return tcat( cats[cnf[3]] ) | ||
end | end | ||
else | else | ||
Line 155: | Line 187: | ||
end | end | ||
local function getFormattedLicense( str ) | local function getFormattedLicense( str, orlatertext ) | ||
local cnf = licenses[ | 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 | if cnf then | ||
return cnf[1] | return (cnf[1] ~= '' and ('[' .. cnf[1] .. ' ' .. cnf[2] .. ']') or cnf[2]) .. orlater | ||
else | else | ||
return | return license | ||
end | end | ||
end | end | ||
Line 167: | Line 205: | ||
function p.getTypes( frame ) | function p.getTypes( frame ) | ||
setI18n( frame.args, types, 1 ) | |||
local args = frame:getParent().args | local args = frame:getParent().args | ||
local types = {} | local types = {} | ||
Line 192: | Line 231: | ||
function p.getType( frame ) | function p.getType( frame ) | ||
setI18n( frame.args, types, 1 ) | |||
return getType( frame.args[1] ) | return getType( frame.args[1] ) | ||
end | end | ||
Line 200: | Line 240: | ||
function p.getFormattedLicense( frame ) | function p.getFormattedLicense( frame ) | ||
return getFormattedLicense( frame.args[1] ) | 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 | 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