This is the Banner class of Module:WikiProjectBanner. It does all of the heavy lifting; it renders the banner, calls the row objects, and calls the hooks.
HooksEdit
This is the normal HTML structure. Everything that is not a tag or an HTML comment is an HTML object that you can add to.
preWrapper
<table>
<tr>
<td>headerName</td>
<th>headerRating</th>
</tr>
<tr>
<td>
<table>
preBlurb
<tr>
<td>blurbImageLeft</td>
<td>blurbText</td>
<td>blurbImageRight</td>
</tr>
postBlurb
<!-- The quality row goes here -->
postQuality
<!-- The importance row goes here -->
postImportance
<!-- Task forces go here -->
postTaskForces
<!-- Requests go here. -->
postRequests
<!-- Notices go here. -->
postNotices
postContent
</table>
</td>
</tr>
</table>
postWrapper
This is the HTML structure if the requests and notices are collapsed.
preWrapper
<table>
<tr>
<td>headerName</td>
<th>headerRating</th>
</tr>
<tr>
<td>
<table>
preBlurb
<tr>
<td>blurbImageLeft</td>
<td>blurbText</td>
<td>blurbImageRight</td>
</tr>
postBlurb
<!-- The quality row goes here -->
postQuality
<!-- The importance row goes here -->
postImportance
<!-- Task forces go here -->
postTaskForces
<tr>
<td>
<table>
<tr>
<td></td>
</tr>
preCollapsedContent
<!-- Requests go here. -->
postRequests
<!-- Notices go here. -->
postNotices
</table>
</td>
</tr>
postContent
</table>
</td>
</tr>
</table>
postWrapper
-------------------------------------------------------------------------------
-- Banner class --
-- This module contains the Banner class used in Module:WikiProjectBanner. --
-- It is used to generate the banner HTML and categories. --
-------------------------------------------------------------------------------
-- Load required modules.
local yesno = require('Module:Yesno')
local mShared = require('Module:WikiProjectBanner/shared')
-- Lazily load modules we might not need.
local Grade
local Banner = {}
Banner.__index = Banner
Banner.rowModules = {
quality = 'Module:WikiProjectBanner/AssessmentRow',
importance = 'Module:WikiProjectBanner/AssessmentRow',
taskForces = 'Module:WikiProjectBanner/TaskForce',
requests = 'Module:WikiProjectBanner/Note',
notices = 'Module:WikiProjectBanner/Note'
}
-- Define the classes table. This loads modules containing the row classes
-- on demand; if it is not indexed, the class is not loaded.
Banner.rowClasses = setmetatable({}, {
__index = function (t, key)
local module = Banner.rowModules[key]
if module then
module = require(module)
Banner.rowClasses[key] = module
return module
end
end
})
function Banner.new(bannerName, args, cfg, bannerCfg)
-- Set data we were passed.
local obj, data = {}, {}
data.bannerName = bannerName
obj.args = args
obj.cfg = cfg
-- Set title objects.
data.currentTitle = mw.title.getCurrentTitle()
data.subjectTitle = data.currentTitle.subjectPageTitle
-- Set banner config.
-- We use a metatable to make it read-only, to try and limit hook
-- interaction.
bannerCfg = bannerCfg or mShared.maybeRequire(
'Module:WikiProjectBanner/banners/' .. data.bannerName
)
if not bannerCfg then
error(
'banner data page [[Module:WikiProjectBanner/banners/' ..
bannerName ..
']] does not exist',
0
)
end
bannerCfg.hooks = bannerCfg.hooks or {}
obj.bannerCfg = setmetatable({}, {
__index = bannerCfg,
__newindex = function ()
error('the banner config is read-only', 2)
end,
__pairs = function ()
return pairs(bannerCfg)
end,
__ipairs = function ()
return ipairs(bannerCfg)
end
})
-- Set banner data.
do
-- Find if we are in demo mode.
local currentPage = data.currentTitle.prefixedText
local template = mw.site.namespaces[10].name .. ':' .. data.bannerName
if currentPage == template
or currentPage == template .. '/sandbox'
then
data.isDemo = true
end
end
data.pageType = require('Module:Pagetype')._main{}
data.project = bannerCfg.project or bannerName:gsub('^WikiProject ', '')
data.projectLink = bannerCfg.projectLink
or 'Wikipedia:WikiProject ' .. data.project
data.projectName = bannerCfg.projectName
or 'WikiProject ' .. data.project
data.projectScope = bannerCfg.projectScope or '[[' .. data.project .. ']]'
data.projectLinkTalk = mw.title.new(data.projectLink).talkPageTitle.prefixedText
data.isSmall = yesno(args.small)
-- Assessment link
if bannerCfg.assessmentLink ~= nil then
-- Custom link or false for no link
data.assessmentLink = bannerCfg.assessmentLink
else
local assessmentPage = data.projectLink .. '/Assessment'
local assessmentTitle = mw.title.new(assessmentPage)
data.assessmentLink = assessmentTitle
and assessmentTitle.exists
and assessmentTitle.prefixedText
end
-- Create the HTML fragments.
do
local fragmentKeys = {
'headerName',
'headerRating',
'blurbImageLeft',
'blurbText',
'blurbImageRight',
}
local html = {}
local create = mw.html.create
for _, key in ipairs(fragmentKeys) do
html[key] = create()
end
obj.html = html
end
-- Define the rest of the object structure.
data.grades = {}
obj.data = data
obj.categories = {}
obj.rows = {
quality = {},
importance = {},
taskForces = {},
requests = {},
notices = {}
}
return setmetatable(obj, Banner)
end
function Banner:addRow(key, isActiveByDefault, rowCfg, ...)
-- This function adds one row object to the banner object's rows subtable.
if not key or not Banner.rowModules[key] then
error(string.format(
"'%s' is not a valid key for Banner:addRow",
key
), 2)
end
local isActive = isActiveByDefault or mShared.isActiveRow(
self.args,
self.data,
self.cfg,
rowCfg
)
if isActive then
-- Create the object and add it to the banner's rows table.
local aClass = Banner.rowClasses[key]
local obj = aClass.new(self.args, self.data, self.cfg, rowCfg, ...)
table.insert(self.rows[key], obj)
-- Add the categories to the banner object's categories table.
for i, category in ipairs(obj:exportCategories()) do
table.insert(self.categories, category)
end
end
end
function Banner:addRows()
local bannerCfg = self.bannerCfg
-- Quality
if bannerCfg.quality ~= false then
Grade = Grade or require('Module:WikiProjectBanner/Grade')
local gradeCfg = {}
local gradeObj = Grade.new(
'quality',
self.args,
self.data,
self.cfg,
gradeCfg
)
for i, category in ipairs(gradeObj:exportCategories()) do
table.insert(self.categories, category)
end
local rowCfg = bannerCfg.quality or {}
self:addRow('quality', true, rowCfg, gradeObj)
self.data.grades.quality = gradeObj
end
-- Importance
if bannerCfg.importance ~= false then
Grade = Grade or require('Module:WikiProjectBanner/Grade')
local gradeCfg = {}
local qualityGrade = self.data.grades.quality
if qualityGrade then
gradeCfg.forceGrade = qualityGrade:exportData().forceImportance
end
local gradeObj = Grade.new(
'importance',
self.args,
self.data,
self.cfg,
gradeCfg
)
for i, category in ipairs(gradeObj:exportCategories()) do
table.insert(self.categories, category)
end
local rowCfg = bannerCfg.importance or {}
if gradeObj:exportData().short ~= 'NA' then
self:addRow('importance', true, rowCfg, gradeObj)
end
end
-- Task forces, requests, and notices
for i, key in ipairs{'taskForces', 'requests', 'notices'} do
for j, rowCfg in ipairs(bannerCfg[key] or {}) do
self:addRow(key, false, rowCfg)
end
end
-- Count the requests and notices.
local nNotes = #self.rows.requests + #self.rows.notices
if nNotes > (bannerCfg.collapsed or self.cfg.collapsed or 3) then
self.data.isCollapsed = true
end
end
function Banner:callHook(key)
local hookFunc = self.bannerCfg.hooks[key]
if type(hookFunc) == 'function' then
return hookFunc{
categories = self.categories,
data = self.data,
args = self.args,
cfg = self.cfg,
bannerCfg = self.bannerCfg
}
end
end
function Banner:renderBlurb()
self:renderImage('Left')
if self.bannerCfg.portal and not self.data.isSmall then
local portal = mShared.makePortal(self.bannerCfg.portal)
self.html.blurbText:wikitext(portal)
end
self:renderBlurbText()
self:renderImage('Right')
end
function Banner:renderImage(position)
-- Valid positions are "Left" and "Right". They must be capitalised.
local filename = self.bannerCfg['image' .. position]
if filename then
local size = self.data.isSmall
and (self.bannerCfg['image' .. position .. 'Small'] or "40px")
or (self.bannerCfg['image' .. position .. 'Large'] or "80px")
local fileLink = string.format('[[File:%s|%s]]', filename, size)
self.html['blurbImage' .. position]:wikitext(fileLink)
end
end
function Banner:renderBlurbText()
local blurbText = self:callHook('blurb') or self.bannerCfg.blurb
if blurbText then
self.html.blurbText:wikitext(blurbText)
else
local data = self.data
local msg
if data.isSmall then
msg = "This $5 is within the scope of '''[[$2|$1]]''', a collaborative effort " ..
"to improve the coverage of $3 on Wikipedia."
else
msg = "This $5 is within the scope of '''[[$2|$1]]''', a collaborative effort " ..
"to improve the coverage of $3 on Wikipedia. If you would like to participate, " ..
"please visit the project page, where you can join the [[$4|discussion]] and " ..
"see a list of open tasks."
end
msg = mShared.substituteParams(
msg,
data.projectName,
data.projectLink,
data.projectScope,
data.projectLinkTalk,
data.pageType
)
self.html.blurbText:wikitext(msg)
end
end
function Banner:getRowHtml(rowType)
-- Render the rows.
local rowHtml = mw.html.create()
for i, obj in ipairs(self.rows[rowType] or {}) do
rowHtml:node(obj:exportHtml())
end
return rowHtml
end
function Banner:renderCategories()
local sortKey = self.args.listas
local ret = {}
if yesno(self.args.category) ~= false then
local categoryNsText = mw.site.namespaces[14].name
for _, category in ipairs(self.categories) do
if sortKey then
ret[#ret + 1] = string.format(
'[[%s:%s|%s]]',
categoryNsText,
category,
sortKey
)
else
ret[#ret + 1] = string.format(
'[[%s:%s]]',
categoryNsText,
category
)
end
end
end
return table.concat(ret)
end
function Banner:bindHtmlFragments()
local html = self.html
local root = mw.html.create()
local wrapper = root
:node(self:callHook('preWrapper'))
:tag('table')
:addClass('tmbox tmbox-notice collapsible innercollapse wpb')
:addClass(self.data.isSmall and 'mbox-small' or nil)
wrapper
:tag('tr')
:addClass('wpb-header')
:tag('td')
:css('text-align', 'right')
:css('padding', '0.3em 1em 0.3em 0.3em')
:css('width', '50%')
:css('font-weight', 'bold')
:node(self:callHook('headerName'))
:done()
:tag('th')
:css('text-align', 'left')
:css('width', '50%')
:css('padding', '0.3em 0.3em 0.3em 0')
:node(self:callHook('headerRating'))
local content = wrapper
:tag('tr')
:tag('td')
:addClass('mbox-text')
:css('padding', '3px 0 3px 5px')
:attr('colspan', '2')
:tag('table')
:css('background', 'transparent')
:css('border', 'none')
:css('padding', '0')
:css('width', '100%')
:attr('cellspacing', '0')
content
:node(self:callHook('preBlurb'))
local blurbRow = content:tag('tr')
-- blurb image left
if self.bannerCfg.imageLeft then
blurbRow:tag('td')
:addClass('mbox-image')
:node(html.blurbImageLeft)
end
-- blurb text
do
local colspan = (self.bannerCfg.imageLeft and 1 or 0) +
(self.bannerCfg.imageRight and 1 or 0) +
1
colspan = colspan > 1 and colspan or nil
blurbRow:tag('td')
:addClass('mbox-text')
:attr('colspan', colspan)
:node(html.blurbText)
end
-- blurb image right
if self.bannerCfg.imageRight then
blurbRow:tag('td')
:addClass('mbox-imageright')
:node(html.blurbImageRight)
end
content
:node(self:callHook('postBlurb'))
:node(self:getRowHtml('quality'))
:node(self:callHook('postQuality'))
:node(self:getRowHtml('importance'))
:node(self:callHook('postImportance'))
:node(self:getRowHtml('taskForces'))
:node(self:callHook('postTaskForces'))
if self.data.isCollapsed then
-- We are collapsing requests and notices, so define the collapsible
-- table and add the requests and notices to it.
content
:tag('tr')
:tag('td')
:attr('colspan', '3')
:css('padding', '0')
:tag('table')
:addClass('collapsible collapsed')
:css('width', '100%')
:css('background', 'transparent')
:tag('tr')
:tag('th')
:attr('colspan', '3')
:css('text-align', 'left')
:css('padding', '0.2em 2px 0.2em 0')
:wikitext(
self.bannerCfg.moreHeader
or 'More information'
)
:done()
:done()
:node(self:callHook('preCollapsedContent'))
:node(self:getRowHtml('requests'))
:node(self:callHook('postRequests'))
:node(self:getRowHtml('notices'))
:node(self:callHook('postNotices'))
else
-- We aren't collapsing requests and notices, so add them to the normal
-- content node.
content
:node(self:getRowHtml('requests'))
:node(self:callHook('postRequests'))
:node(self:getRowHtml('notices'))
:node(self:callHook('postNotices'))
end
content
:node(self:callHook('postContent'))
root
:wikitext(self:renderCategories())
:node(self:callHook('postWrapper'))
html.root = root
end
function Banner:__tostring()
self:addRows()
self:callHook()
self:renderBlurb()
self:bindHtmlFragments()
local root = self.html.root or mw.html.create()
return tostring(root)
end
return Banner