Module:Infobox/League
From Liquipedia Commons Wiki
Creates the infobox for a league.
Usage
Implement a Module on your wiki that calls run on this module, and then add new cells or override customizable cells as needed.
Parameters
[edit]|wiki=- The specifier for the game infobox, e.g.
rocketfor rocket league |name=- The tournament name
|shortname=- The tournament short name
|tickername=- The tournament ticker name
|image=- The tournament banner
|imagedark, imagedarkmode=- The tournament banner (darkmode compliant)
|icon=- The tournament icon
|icondark, icondarkmode=- The tournament icon (darkmode compliant)
|caption=- Caption for the image
|server=- The server the tournament is played on
|status=- The status of the tournament, e.g. cancelled
|series=- The series the tournament is part of
|type=- The tournament type, e.g. Online
|region=- The tournament region
|country, country2=- The tournament country
|city, city2=- The tournament city
|venue=- The tournament venue
|prizepool=- The tournament prizepool in local currency
|localcurrency=- The currency symbol
|prizepoolusd=- The tournament prizepool in USD
|sdate=- The tournament start date (YYYY-MM-DD)
|edate=- The tournament end date (YYYY-MM-DD)
|date=- The tournament date (YYYY-MM-DD)
|liquipediatier=- The tournaments liquipedia tier
|liquipediatiertype=- The tournaments liquipedia tier type
|sponsor, sponsor2, ...=- The tournament sponsor(s)
|organizer, organizer2, ...=- The tournament organizer(s)
|organizerX-name=- Name display for organizerX
|organizerX-link=- External link for organizerX
|organizerrefX=- Reference for organizerX
|footnotes=- footnotes
|website, discord, twitter, etc=- Social media links
| The above documentation is transcluded from Module:Infobox/League/doc. (edit | history) Editors can experiment in this module's sandbox (create | mirror) and testcases (create) pages. Subpages of this module. |
---
-- @Liquipedia
-- page=Module:Infobox/League
--
-- Please see https://github.com/Liquipedia/Lua-Modules to contribute
--
local Lua = require('Module:Lua')
local Array = Lua.import('Module:Array')
local BasicInfobox = Lua.import('Module:Infobox/Basic')
local Class = Lua.import('Module:Class')
local CountryCategory = Lua.import('Module:Infobox/Extension/CountryCategory')
local DateExt = Lua.import('Module:Date/Ext')
local Game = Lua.import('Module:Game')
local HighlightConditions = Lua.import('Module:HighlightConditions')
local Info = Lua.import('Module:Info', {loadData = true})
local InfoboxPrizePool = Lua.import('Module:Infobox/Extension/PrizePool')
local Json = Lua.import('Module:Json')
local LeagueIcon = Lua.import('Module:LeagueIcon')
local Links = Lua.import('Module:Links')
local Locale = Lua.import('Module:Locale')
local Logic = Lua.import('Module:Logic')
local Lpdb = Lua.import('Module:Lpdb')
local MatchTicker = Lua.import('Module:MatchTicker')
local MetadataGenerator = Lua.import('Module:MetadataGenerator')
local Namespace = Lua.import('Module:Namespace')
local Page = Lua.import('Module:Page')
local ReferenceCleaner = Lua.import('Module:ReferenceCleaner')
local String = Lua.import('Module:StringUtils')
local Table = Lua.import('Module:Table')
local TextSanitizer = Lua.import('Module:TextSanitizer')
local Tier = Lua.import('Module:Tier/Custom')
local TournamentService = Lua.import('Module:Tournament')
local Variables = Lua.import('Module:Variables')
local INVALID_TIER_WARNING = '${tierString} is not a known Liquipedia ${tierMode}'
local Widgets = Lua.import('Module:Widget/All')
local HtmlWidgets = Lua.import('Module:Widget/Html/All')
local Accommodation = Widgets.Accommodation
local Builder = Widgets.Builder
local Cell = Widgets.Cell
local Center = Widgets.Center
local Chronology = Widgets.Chronology
local Customizable = Widgets.Customizable
local Header = Widgets.Header
local Location = Widgets.Location
local Organizers = Widgets.Organizers
local Title = Widgets.Title
local Venue = Widgets.Venue
---@class InfoboxLeague: BasicInfobox
---@operator call(Frame): InfoboxLeague
local League = Class.new(BasicInfobox)
---@param frame Frame
---@return Widget
function League.run(frame)
local league = League(frame)
return league:createInfobox()
end
---@return Widget
function League:createInfobox()
local args = self.args
self:_parseArgs()
self:_definePageVariables(args)
local widgets = {
Header{
name = args.name,
image = args.image,
imageDark = args.imagedark or args.imagedarkmode,
size = args.imagesize,
},
Center{children = {args.caption}},
Title{children = 'League Information'},
Cell{
name = 'Series',
children = {
self:createSeriesDisplay({
displayManualIcons = Logic.readBool(args.display_series_icon_from_manual_input),
series = args.series,
abbreviation = args.abbreviation,
icon = args.icon,
iconDark = args.icondark or args.icondarkmode,
}, self.iconDisplay),
self:createSeriesDisplay{
series = args.series2,
abbreviation = args.abbreviation2,
},
}
},
Customizable{
id = 'organizers',
children = {Organizers{args = args}},
},
Customizable{
id = 'sponsors',
children = {
Cell{name = 'Sponsor(s)', children = self:getAllArgsForBase(args, 'sponsor')},
}
},
Customizable{
id = 'gamesettings',
children = {
Cell{name = 'Server', children = {args.server}}
}
},
Customizable{id = 'type', children = {
Builder{
builder = function()
local value = tostring(args.type):lower()
if self:shouldStore(args) then
if value == 'offline' then
self:categories('Offline Tournaments')
elseif value == 'online' then
self:categories('Online Tournaments')
elseif value:match('online') and value:match('offline') then
self:categories('Online/Offline Tournaments')
else
self:categories('Unknown Type Tournaments')
end
end
if not String.isEmpty(args.type) then
return {
Cell{
name = 'Type',
children = {
mw.language.getContentLanguage():ucfirst(args.type)
}
}
}
end
end
}
}
},
Location{args = args},
Venue{args = args},
Cell{name = 'Format', children = {args.format}},
Customizable{id = 'prizepool', children = {
Cell{
name = 'Prize Pool',
children = {self.prizepoolDisplay},
},
},
},
Customizable{id = 'dates', children = {
Cell{name = 'Date', children = {args.date}},
Cell{name = 'Start Date', children = {args.sdate}},
Cell{name = 'End Date', children = {args.edate}},
},
},
Customizable{id = 'custom', children = {}},
Customizable{id = 'liquipediatier', children = {
Cell{
name = 'Liquipedia Tier',
children = {self:createLiquipediaTierDisplay(args)},
classes = {self:liquipediaTierHighlighted(args) and 'valvepremier-highlighted' or ''},
},
},
},
Widgets.Links{links = self.links},
Customizable{id = 'customcontent', children = {}},
Center{children = {args.footnotes}},
Customizable{id = 'chronology', children = {
Chronology{args = args, showTitle = true},
}},
Accommodation{
args = args,
startDate = self.data.startDate,
endDate = self.data.endDate,
name = self.data.name,
},
}
self.name = TextSanitizer.stripHTML(self.name)
self:top(self:_createUpcomingMatches())
self:bottom(self:createBottomContent())
if self:shouldStore(args) then
self:_setLpdbData(args, self.links)
self:categories(unpack(self:_getCategories(args)))
self:_setSeoTags(args)
end
return HtmlWidgets.Fragment{children = Array.interleave({
self:build(widgets, 'Tournament'),
Logic.readBool(args.autointro) and self:seoText(args) or nil
}, HtmlWidgets.Br{})}
end
---@private
function League:_parseArgs()
local args = self.args
args.abbreviation = self:_fetchAbbreviation()
-- Split venue from legacy format to new format.
-- Legacy format is a wiki-code string that can include an external link
-- New format has |venue=, |venuename= and |venuelink= as different parameters.
-- This should be removed once there's been a bot run to change this.
if not args.venuename and args.venue and args.venue:sub(1, 2) == '[[' then
-- Remove [[]] and split on `|`
local splitVenue = mw.text.split(args.venue:gsub('%[%[', ''):gsub('%]%]', ''), '|')
args.venue = splitVenue[1]
args.venuename = splitVenue[2]
elseif not args.venuelink and args.venue and args.venue:sub(1, 1) == '[' then
-- Remove [] and split on space
local splitVenue = mw.text.split(args.venue:gsub('%[', ''):gsub('%]', ''), ' ')
args.venuelink = splitVenue[1]
table.remove(splitVenue, 1)
args.venue = table.concat(splitVenue, ' ')
end
local data = {
name = TextSanitizer.stripHTML(args.name),
shortName = TextSanitizer.stripHTML(args.shortname or args.abbreviation),
tickerName = TextSanitizer.stripHTML(args.tickername),
series = mw.ext.TeamLiquidIntegration.resolve_redirect(args.series or ''),
--might be set before infobox
status = args.status or Variables.varDefault('tournament_status'),
game = Game.toIdentifier{game = args.game},
-- If no parent is available, set pagename instead to ease querying
parent = (args.parent or mw.title.getCurrentTitle().prefixedText):gsub(' ', '_'),
startDate = ReferenceCleaner.cleanDateIfKnown{date = args.sdate}
or ReferenceCleaner.cleanDateIfKnown{date = args.date},
endDate = ReferenceCleaner.cleanDateIfKnown{date = args.edate}
or ReferenceCleaner.cleanDateIfKnown{date = args.date},
mode = args.mode,
patch = args.patch,
endPatch = args.endpatch or args.epatch or args.patch,
publishertier = Logic.readBool(args.highlighted),
}
data.liquipediatier, data.liquipediatiertype =
Tier.toValue(args.liquipediatier, args.liquipediatiertype)
self.data = data
self.prizepoolDisplay, self.data.prizepoolUsd, self.data.localCurrency = self:_parsePrizePool(args, data.endDate)
data.icon, data.iconDark, self.iconDisplay = self:getIcons{
displayManualIcons = Logic.readBool(args.display_series_icon_from_manual_input),
series = args.series,
abbreviation = args.abbreviation,
icon = args.icon,
iconDark = args.icondark or args.icondarkmode,
}
self.links = Links.transform(args)
self:customParseArguments(args)
end
---@private
---@param args table
---@param endDate string?
---@return number|string?, number?, string?
function League:_parsePrizePool(args, endDate)
if String.isEmpty(args.prizepool) and String.isEmpty(args.prizepoolusd) then
return
end
--need to get the display here since it sets variables we want/need to get the clean values
--overwritable since sometimes display is supposed to look a bit different
return self:displayPrizePool(args, endDate),
tonumber(Variables.varDefault('tournament_prizepoolusd')) or 0,
Variables.varDefault('tournament_currency', args.localcurrency)
end
---@param args table
---@param endDate string?
---@return number|string?
function League:displayPrizePool(args, endDate)
return InfoboxPrizePool.display{
prizepool = args.prizepool,
prizepoolusd = args.prizepoolusd,
currency = args.localcurrency,
rate = args.currency_rate,
date = Logic.emptyOr(args.currency_date, endDate),
setvariables = args.setvariables,
displayRoundPrecision = args.currencyDispPrecision,
varRoundPrecision = args.currencyVarPrecision
}
end
---@param args table
function League:customParseArguments(args)
end
---@private
function League:_tournamentPhaseCategory()
local phaseMapping = {
ONGOING = 'Live Tournaments',
UPCOMING = 'Upcoming Tournaments',
FINISHED = 'Finished Tournaments'
}
local tournamentPhase = TournamentService.tournamentFromRecord(self.lpdbData).phase
return phaseMapping[tournamentPhase]
end
---@private
---@param args table
---@return string[]
function League:_getCategories(args)
return Array.extend(
{'Tournaments'},
Logic.isEmpty(args.country) and 'Tournaments without location' or nil,
self:addParticipantTypeCategory(args),
self:addTierCategories(args),
self:_tournamentPhaseCategory(),
CountryCategory.run(args, 'Tournaments'),
self:getWikiCategories(args)
)
end
---@private
---@return Widget?
function League:_createUpcomingMatches()
if not self:shouldStore(self.args) then
return nil
end
if Info.config.match2.status == 0 then
return nil
end
local result = Logic.tryCatch(
function()
local matchTicker = MatchTicker{
tournament = self.pagename,
limit = 5,
upcoming = true,
ongoing = true,
hideTournament = true,
queryByParent = true,
}
matchTicker:query()
return matchTicker
end,
function()
return nil
end
)
if not result or not result.matches or #result.matches == 0 then
return nil
end
local EntityDisplay = Lua.import('Module:MatchTicker/DisplayComponents/Entity')
return EntityDisplay.Container{
config = result.config,
matches = result.matches,
}:create()
end
---@param args table
---@return string[]
function League:addParticipantTypeCategory(args)
local categories = {}
if not String.isEmpty(args.team_number) then
table.insert(categories, 'Team Tournaments')
end
if String.isNotEmpty(args.player_number) or String.isNotEmpty(args.individual) then
table.insert(categories, 'Individual Tournaments')
end
return categories
end
---@param args table
---@return string[]
function League:addTierCategories(args)
local categories = {}
local tier = args.liquipediatier
local tierType = args.liquipediatiertype
local tierCategory, tierTypeCategory = Tier.toCategory(tier, tierType)
local isValidTierTuple = Tier.isValid(tier, tierType)
table.insert(categories, tierCategory)
table.insert(categories, tierTypeCategory)
if not isValidTierTuple and not tierCategory and Logic.isNotEmpty(tier) then
table.insert(self.warnings, String.interpolate(INVALID_TIER_WARNING, {tierString = tier, tierMode = 'Tier'}))
table.insert(categories, 'Pages with invalid Tier')
end
if not isValidTierTuple and not tierTypeCategory and String.isNotEmpty(tierType) then
table.insert(self.warnings,
String.interpolate(INVALID_TIER_WARNING, {tierString = tierType, tierMode = 'Tiertype'}))
table.insert(categories, 'Pages with invalid Tiertype')
end
return categories
end
--- Allows for overriding this functionality
---@param args table
---@return boolean
function League:shouldStore(args)
return Namespace.isMain() and Lpdb.isStorageEnabled()
end
--- Allows for overriding this functionality
---@param args table
function League:defineCustomPageVariables(args)
end
--- Allows for overriding this functionality
---@param lpdbData table
---@param args table
---@return table
function League:addToLpdb(lpdbData, args)
return lpdbData
end
--- Allows for overriding this functionality
---@param args table
---@return string
function League:seoText(args)
return MetadataGenerator.tournament(args)
end
--- Allows for overriding this functionality
---@param args table
---@return boolean
function League:liquipediaTierHighlighted(args)
return HighlightConditions.tournament(self.data)
end
--- Allows for overriding this functionality
---@param args table
---@return string
function League:appendLiquipediatierDisplay(args)
return ''
end
---@param args table
---@return string?
function League:createLiquipediaTierDisplay(args)
local tierDisplay = Tier.display(args.liquipediatier, args.liquipediatiertype, {link = true})
if String.isEmpty(tierDisplay) then
return
end
return tierDisplay .. self:appendLiquipediatierDisplay(args)
end
---@private
---@param args table
function League:_definePageVariables(args)
Variables.varDefine('tournament_name', self.data.name)
Variables.varDefine('tournament_shortname', self.data.shortName)
Variables.varDefine('tournament_tickername', self.data.tickerName)
Variables.varDefine('tournament_series', self.data.series)
Variables.varDefine('tournament_icon', self.data.icon)
Variables.varDefine('tournament_icondark', self.data.iconDark)
Variables.varDefine('tournament_liquipediatier', self.data.liquipediatier)
Variables.varDefine('tournament_liquipediatiertype', self.data.liquipediatiertype)
Variables.varDefine('tournament_publishertier', tostring(self.data.publishertier or ''))
Variables.varDefine('tournament_type', args.type)
Variables.varDefine('tournament_mode', self.data.mode)
Variables.varDefine('tournament_status', self.data.status)
Variables.varDefine('tournament_region', args.region)
Variables.varDefine('tournament_country', args.country)
Variables.varDefine('tournament_location', args.location or args.city)
Variables.varDefine('tournament_location2', args.location2 or args.city2)
Variables.varDefine('tournament_venue', args.venue)
Variables.varDefine('tournament_game', self.data.game)
Variables.varDefine('tournament_parent', self.data.parent)
Variables.varDefine('tournament_parentname', args.parentname)
Variables.varDefine('tournament_subpage', args.subpage)
Variables.varDefine('tournament_startdate', self.data.startDate)
Variables.varDefine('tournament_enddate', self.data.endDate)
Variables.varDefine('tournament_patch', self.data.patch)
Variables.varDefine('tournament_endpatch ', self.data.endPatch)
Variables.varDefine('tournament_currency', self.data.localCurrency or '')
Variables.varDefine('tournament_summary', self:seoText(args))
self:defineCustomPageVariables(args)
end
---@private
---@param args table
---@param links table
function League:_setLpdbData(args, links)
local lpdbData = {
name = self.name,
tickername = self.data.tickerName,
shortname = self.data.shortName,
banner = args.image,
bannerdark = args.imagedark or args.imagedarkmode,
icon = self.data.icon,
icondark = self.data.iconDark,
series = mw.ext.TeamLiquidIntegration.resolve_redirect(args.series or ''),
seriespage = Page.pageifyLink(args.series),
serieslist = {
Page.pageifyLink(args.series),
Page.pageifyLink(args.series2),
},
previous = self:_getPageNameFromChronology(args.previous),
previous2 = self:_getPageNameFromChronology(args.previous2),
next = self:_getPageNameFromChronology(args.next),
next2 = self:_getPageNameFromChronology(args.next2),
game = self.data.game,
mode = self.data.mode,
patch = self.data.patch,
endpatch = self.data.endPatch,
type = args.type,
organizers = Table.mapValues(
League:_getNamedTableofAllArgsForBase(args, 'organizer'),
mw.ext.TeamLiquidIntegration.resolve_redirect
),
startdate = self.data.startDate or self.data.endDate or DateExt.defaultDate,
enddate = self.data.endDate or DateExt.defaultDate,
sortdate = self.data.endDate or DateExt.defaultDate,
location = mw.text.decode(Locale.formatLocation({city = args.city or args.location, country = args.country})),
location2 = mw.text.decode(Locale.formatLocation({city = args.city2 or args.location2, country = args.country2})),
venue = args.venue,
locations = Locale.formatLocations(args),
prizepool = self.data.prizepoolUsd,
liquipediatier = self.data.liquipediatier,
liquipediatiertype = self.data.liquipediatiertype,
publishertier = tostring(self.data.publishertier or ''),
participantsnumber = tonumber(args.participants_number)
or tonumber(args.team_number)
or tonumber(args.player_number)
or -1,
status = self.data.status,
format = TextSanitizer.stripHTML(args.format),
sponsors = League:_getNamedTableofAllArgsForBase(args, 'sponsor'),
links = Links.makeFullLinksForTableItems(links or {}),
summary = self:seoText(args),
extradata = {
series2 = args.series2 and mw.ext.TeamLiquidIntegration.resolve_redirect(args.series2) or nil,
},
}
lpdbData = self:addToLpdb(lpdbData, args)
mw.ext.LiquipediaDB.lpdb_tournament('tournament_' .. self.name, Json.stringifySubTables(lpdbData))
self.lpdbData = lpdbData
end
---@private
---@param args table
function League:_setSeoTags(args)
local desc = self:seoText(args)
if desc then
mw.ext.SearchEngineOptimization.metadescl(desc)
end
end
---@private
---@param args table
---@param base string
---@return table
function League:_getNamedTableofAllArgsForBase(args, base)
local basedArgs = self:getAllArgsForBase(args, base)
local namedArgs = {}
for key, item in pairs(basedArgs) do
namedArgs[base .. key] = item
end
return namedArgs
end
---@param seriesArgs {displayManualIcons:boolean, series:string?, abbreviation:string?, icon:string?, iconDark:string?}
---@param iconDisplay string?
---@return string?
function League:createSeriesDisplay(seriesArgs, iconDisplay)
if String.isEmpty(seriesArgs.series) then
return nil
end
iconDisplay = iconDisplay or self:_createSeriesIcon(seriesArgs)
if String.isNotEmpty(iconDisplay) then
iconDisplay = iconDisplay .. ' '
end
local abbreviation = Logic.emptyOr(seriesArgs.abbreviation, seriesArgs.series)
local pageDisplay = Page.makeInternalLink({onlyIfExists = true}, abbreviation, seriesArgs.series)
or abbreviation
return iconDisplay .. pageDisplay
end
---@param iconArgs {displayManualIcons:boolean, series:string?, abbreviation:string?, icon:string?, iconDark:string?}
---@return string?
---@return string?
---@return string?
function League:getIcons(iconArgs)
local display = self:_createSeriesIcon(iconArgs)
if not display then
return iconArgs.icon, iconArgs.iconDark, nil
end
local icon, iconDark, trackingCategory = LeagueIcon.getIconFromTemplate{
icon = iconArgs.icon,
iconDark = iconArgs.iconDark,
stringOfExpandedTemplate = display
}
if String.isNotEmpty(trackingCategory) then
table.insert(self.warnings, 'Missing icon while icondark is set.')
end
return icon, iconDark, display
end
---@private
---@param iconArgs {displayManualIcons:boolean, series:string?, abbreviation:string?, icon:string?, iconDark:string?}
---@return string?
function League:_createSeriesIcon(iconArgs)
if String.isEmpty(iconArgs.series) then
return ''
end
local series = iconArgs.series
---@cast series -nil
local output = LeagueIcon.display{
icon = iconArgs.displayManualIcons and iconArgs.icon or nil,
iconDark = iconArgs.displayManualIcons and iconArgs.iconDark or nil,
series = series,
abbreviation = iconArgs.abbreviation,
date = self.data.endDate,
options = {noLink = not Page.exists(series)}
}
return output == LeagueIcon.display{} and '' or output
end
--- used in brawlstars, chess, counterstrike customs
---@param id string?
---@param name string?
---@param link string?
---@param desc string?
---@return string?
function League:createLink(id, name, link, desc)
if String.isEmpty(id) then
return nil
end
---@cast id -nil
local output
if Page.exists(id) or id:find('^[Ww]ikipedia:') then
output = '[[' .. id .. '|'
if String.isEmpty(name) then
output = output .. id .. ']]'
else
output = output .. name .. ']]'
end
elseif not String.isEmpty(link) then
if String.isEmpty(name) then
output = '[' .. link .. ' ' .. id .. ']'
else
output = '[' .. link .. ' ' .. name .. ']'
end
elseif String.isEmpty(name) then
output = id
else
output = name
end
if not String.isEmpty(desc) then
output = output .. desc
end
return output
end
-- Given the format `pagename|displayname`, returns pagename or the parameter, otherwise
---@private
---@param item string?
---@return string?
function League:_getPageNameFromChronology(item)
if item == nil then return end
return mw.ext.TeamLiquidIntegration.resolve_redirect(mw.text.split(item, '|')[1])
end
-- Given a series, query its abbreviation if abbreviation is not set manually
---@private
---@return string?
function League:_fetchAbbreviation()
if not String.isEmpty(self.args.abbreviation) then
return self.args.abbreviation
elseif String.isEmpty(self.args.series) then
return nil
end
local series = string.gsub(mw.ext.TeamLiquidIntegration.resolve_redirect(self.args.series), ' ', '_')
local seriesData = mw.ext.LiquipediaDB.lpdb('series', {
conditions = '[[pagename::' .. series .. ']] AND [[abbreviation::!]]',
query = 'abbreviation',
limit = 1
})
if type(seriesData) == 'table' and seriesData[1] then
return seriesData[1].abbreviation
end
end
return League