Module:WikiProjectBanner/Banner
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.
Hooks
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.
<syntaxhighlight lang="html5"> preWrapper
headerName | headerRating | ||
---|---|---|---|
preBlurb
postBlurb
postQuality
postImportance
postTaskForces
postRequests
postNotices
postContent
|
postWrapper </syntaxhighlight>
This is the HTML structure if the requests and notices are collapsed.
<syntaxhighlight lang="html5"> preWrapper
headerName | headerRating | ||||
---|---|---|---|---|---|
preBlurb
postBlurb
postQuality
postImportance
postTaskForces
postContent
|
postWrapper </syntaxhighlight>
------------------------------------------------------------------------------- -- 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