--------------------------------------------------------------------------------- Article history---- This module allows editors to link to all the significant events in an-- article's history, such as good article nominations and featured article-- nominations. It also displays its current status, as well as other-- information, such as the date it was featured on the main page.-------------------------------------------------------------------------------
local CONFIG_PAGE = 'Module:Article history/config'local WRAPPER_TEMPLATE = 'Template:Article history'local DEBUG_MODE = false -- If true, errors are not caught.
-- Load required modules.require('strict')local Category = require('Module:Article history/Category')local yesno = require('Module:Yesno')local lang = mw.language.getContentLanguage
--------------------------------------------------------------------------------- Helper functions-------------------------------------------------------------------------------
local function isPositiveInteger(num) return type(num)
num and num > 0 and num < math.hugeend
local function substituteParams(msg, ...) return mw.message.newRawMessage(msg, ...):plainend
local function makeUrlLink(url, display) return string.format('[%s %s]', url, display)end
local function maybeCallFunc(val, ...) -- Checks whether val is a function, and if so calls it with the specified -- arguments. Otherwise val is returned as-is. if type(val)
local function renderImage(image, caption, size) if caption then caption = '|' .. caption else caption = end return string.format('', image, size, caption)end
local function addMixin(class, mixin) -- Add a mixin to a class. The functions will be shared across classes, so -- don't use it for functions that keep state. for name, method in pairs(mixin) do class[name] = method endend
--------------------------------------------------------------------------------- Message mixin-- This mixin is used by all classes to add message-related methods.-------------------------------------------------------------------------------
local Message =
function Message:message(key, ...) -- This fetches the message from the config with the specified key, and -- substitutes parameters $1, $2 etc. with the subsequent values it is -- passed. local msg = self.cfg.msg[key] if select('#', ...) > 0 then return substituteParams(msg, ...) else return msg endend
function Message:raiseError(msg, help) -- Raises an error with the specified message and help link. Execution -- stops unless the error is caught. This is used for errors where -- subsequent processing becomes impossible. local errorText if help then errorText = self:message('error-message-help', msg, help) else errorText = self:message('error-message-nohelp', msg) end error(errorText, 0)end
function Message:addWarning(msg, help) -- Adds a warning to the object's warnings table. Execution continues as -- normal. This is used for errors that should be fixed but that do not -- prevent the module from outputting something useful. self.warnings = self.warnings or local warningText if help then warningText = self:message('warning-help', msg, help) else warningText = self:message('warning-nohelp', msg) end table.insert(self.warnings, warningText)end
function Message:getWarnings return self.warnings or end
--------------------------------------------------------------------------------- Row class-- This class represents one row in the template.-------------------------------------------------------------------------------
local Row = Row.__index = RowaddMixin(Row, Message)
function Row.new(data) local obj = setmetatable(Row) obj.cfg = data.cfg obj.currentTitle = data.currentTitle obj.makeData = data.makeData -- used by Row:getData return objend
function Row:_cachedTry(cacheKey, errorCacheKey, func) -- This method is for use in Row object methods that are called more than -- once. The results of such methods should be cached to avoid unnecessary -- processing. We also cache any errors found and abort if an error was -- raised previously, otherwise error messages could be displayed multiple -- times. -- -- We use false as a key to cache nil results, so func cannot return false. -- -- @param cacheKey The key to cache successful results with -- @param errorCacheKey The key to cache errors with -- @param func an anonymous function that returns the method result if self[errorCacheKey] then return nil end local ret = self[cacheKey] if ret then return ret elseif ret
function Row:getData(articleHistoryObj) return self:_cachedTry('_dataCache', '_isDataError', function return self.makeData(articleHistoryObj) end)end
function Row:setIconValues(icon, caption, size) self.icon = icon self.iconCaption = caption self.iconSize = sizeend
function Row:getIcon(articleHistoryObj) return maybeCallFunc(self.icon, articleHistoryObj, self)end
function Row:getIconCaption(articleHistoryObj) return maybeCallFunc(self.iconCaption, articleHistoryObj, self)end
function Row:getIconSize return self.iconSize or self.cfg.defaultIconSize or '30px'end
function Row:renderIcon(articleHistoryObj) local icon = self:getIcon(articleHistoryObj) if not icon then return nil end return renderImage(icon, self:getIconCaption(articleHistoryObj), self:getIconSize )end
function Row:setNoticeBarIconValues(icon, caption, size) self.noticeBarIcon = icon self.noticeBarIconCaption = caption self.noticeBarIconSize = sizeend
function Row:getNoticeBarIcon(articleHistoryObj) local icon = maybeCallFunc(self.noticeBarIcon, articleHistoryObj, self) if icon
function Row:getNoticeBarIconCaption(articleHistoryObj) local caption = maybeCallFunc(self.noticeBarIconCaption, articleHistoryObj, self ) if not caption then caption = self:getIconCaption(articleHistoryObj) end return captionend
function Row:getNoticeBarIconSize return self.noticeBarIconSize or self.cfg.defaultNoticeBarIconSize or '15px'end
function Row:exportNoticeBarIcon(articleHistoryObj) local icon = self:getNoticeBarIcon(articleHistoryObj) if not icon then return nil end return renderImage(icon, self:getNoticeBarIconCaption(articleHistoryObj), self:getNoticeBarIconSize )end
function Row:setText(text) self.text = textend
function Row:getText(articleHistoryObj) return maybeCallFunc(self.text, articleHistoryObj, self)end
function Row:exportHtml(articleHistoryObj) if self._html then return self._html end local text = self:getText(articleHistoryObj) if not text then return nil end local html = mw.html.create('tr') html :tag('td') :addClass('mbox-image') :wikitext(self:renderIcon(articleHistoryObj)) :done :tag('td') :addClass('mbox-text') :wikitext(text) self._html = html return htmlend
function Row:setCategories(val) -- Set the categories from the object's config. val can be either an array -- of strings or a function returning an array of category objects. self.categories = valend
function Row:getCategories(articleHistoryObj) local ret = if type(self.categories)
'function' then local t = self.categories(articleHistoryObj, self) or for _, categoryObj in ipairs(t) do ret[#ret + 1] = categoryObj end end return retend
--------------------------------------------------------------------------------- Status class-- Status objects deal with possible current statuses of the article.-------------------------------------------------------------------------------
local Status = setmetatable(Row)Status.__index = Status
function Status.new(data) local obj = Row.new(data) setmetatable(obj, Status)
obj.id = data.id obj.statusCfg = obj.cfg.statuses[obj.id] obj.name = obj.statusCfg.name obj:setIconValues(obj.statusCfg.icon, obj.statusCfg.iconCaption or obj.name, data.iconSize ) obj:setNoticeBarIconValues(obj.statusCfg.noticeBarIcon, obj.statusCfg.noticeBarIconCaption or obj.name, obj.statusCfg.noticeBarIconSize ) obj:setText(obj.statusCfg.text) obj:setCategories(obj.statusCfg.categories)
return objend
function Status:getIconSize return self.iconSize or self.statusCfg.iconSize or self.cfg.defaultStatusIconSize or '50px'end
function Status:getText(articleHistoryObj) local text = Row.getText(self, articleHistoryObj) if text then return substituteParams(text, self.currentTitle.subjectPageTitle.prefixedText, self.currentTitle.text ) endend
--------------------------------------------------------------------------------- MultiStatus class-- For when an article can have multiple distinct statuses, e.g. former-- featured article status and good article status.-------------------------------------------------------------------------------
local MultiStatus = setmetatable(Row)MultiStatus.__index = MultiStatus
function MultiStatus.new(data) local obj = Row.new(data) setmetatable(obj, MultiStatus)
obj.id = data.id obj.statusCfg = obj.cfg.statuses[data.id] obj.name = obj.statusCfg.name
-- Set child status objects local function getChildStatusData(data, id, iconSize) local ret = for k, v in pairs(data) do ret[k] = v end ret.id = id ret.iconSize = iconSize return ret end obj.statuses = local defaultIconSize = obj.cfg.defaultMultiStatusIconSize or '30px' for _, id in ipairs(obj.statusCfg.statuses) do table.insert(obj.statuses, Status.new(getChildStatusData(data, id, obj.cfg.statuses[id].iconMultiSize or defaultIconSize ))) end
return objend
function MultiStatus:exportHtml(articleHistoryObj) local ret = mw.html.create for _, obj in ipairs(self.statuses) do ret:node(obj:exportHtml(articleHistoryObj)) end return retend
function MultiStatus:getCategories(articleHistoryObj) local ret = for _, obj in ipairs(self.statuses) do for _, categoryObj in ipairs(obj:getCategories(articleHistoryObj)) do ret[#ret + 1] = categoryObj end end return retend
function MultiStatus:exportNoticeBarIcon local ret = for _, obj in ipairs(self.statuses) do ret[#ret + 1] = obj:exportNoticeBarIcon end return table.concat(ret)end
function MultiStatus:getWarnings local ret = for _, obj in ipairs(self.statuses) do for _, msg in ipairs(obj:getWarnings) do ret[#ret + 1] = msg end end return retend
--------------------------------------------------------------------------------- Notice class-- Notice objects contain notices about an article that aren't part of its-- current status, e.g. the date an article was featured on the main page.-------------------------------------------------------------------------------
local Notice = setmetatable(Row)Notice.__index = Notice
function Notice.new(data) local obj = Row.new(data) setmetatable(obj, Notice)
obj:setIconValues(data.icon, data.iconCaption, data.iconSize ) obj:setNoticeBarIconValues(data.noticeBarIcon, data.noticeBarIconCaption, data.noticeBarIconSize ) obj:setText(data.text) obj:setCategories(data.categories)
return objend
--------------------------------------------------------------------------------- Action class-- Action objects deal with a single action in the history of the article. We-- use getter methods rather than properties for the name and result, etc., as-- their processing needs to be delayed until after the status object has been-- initialised. The status object needs to parse the action objects when it is-- initialised, and the value of some names, etc., in the action objects depend-- on the status object, so this is necessary to avoid errors/infinite loops.-------------------------------------------------------------------------------
local Action = setmetatable(Row)Action.__index = Action
function Action.new(data) local obj = Row.new(data) setmetatable(obj, Action)
obj.paramNum = data.paramNum
-- Set the ID do if not data.code then obj:raiseError(obj:message('action-error-no-code', obj:getParameter('code')), obj:message('action-error-no-code-help') ) end local code = mw.ustring.upper(data.code) obj.id = obj.cfg.actions[code] and obj.cfg.actions[code].id if not obj.id then obj:raiseError(obj:message('action-error-invalid-code', data.code, obj:getParameter('code') ), obj:message('action-error-invalid-code-help') ) end end
-- Add a shortcut for this action's config. obj.actionCfg = obj.cfg.actions[obj.id]
-- Set the link obj.link = data.link or obj.currentTitle.talkPageTitle.prefixedText
-- Set the result ID do local resultCode = data.resultCode and mw.ustring.lower(data.resultCode) or '_BLANK' if obj.actionCfg.results[resultCode] then obj.resultId = obj.actionCfg.results[resultCode].id elseif resultCode
-- Set the date if data.date then local success, date = pcall(lang.formatDate, lang, obj:message('action-date-format'), data.date ) if success and date then obj.date = date else obj:addWarning(obj:message('action-warning-invalid-date', data.date, obj:getParameter('date') ), obj:message('action-warning-invalid-date-help') ) end else obj:addWarning(obj:message('action-warning-no-date', obj.paramNum, obj:getParameter('date'), obj:getParameter('code') ), obj:message('action-warning-no-date-help') ) end obj.date = obj.date or obj:message('action-date-missing')
-- Set the oldid obj.oldid = tonumber(data.oldid) if data.oldid and (not obj.oldid or not isPositiveInteger(obj.oldid)) then obj.oldid = nil obj:addWarning(obj:message('action-warning-invalid-oldid', data.oldid, obj:getParameter('oldid') ), obj:message('action-warning-invalid-oldid-help') ) end
-- Set the notice bar icon values obj:setNoticeBarIconValues(data.noticeBarIcon, data.noticeBarIconCaption, data.noticeBarIconSize )
-- Set the categories obj:setCategories(obj.actionCfg.categories)
return objend
function Action:getParameter(key) -- Finds the original parameter name for the given key that was passed to -- Action.new. local prefix = self.cfg.actionParamPrefix local suffix for k, v in pairs(self.cfg.actionParamSuffixes) do if v
function Action:getName(articleHistoryObj) return maybeCallFunc(self.actionCfg.name, articleHistoryObj, self)end
function Action:getResult(articleHistoryObj) return maybeCallFunc(self.actionCfg.results[self.resultId].text, articleHistoryObj, self )end
function Action:exportHtml(articleHistoryObj) if self._html then return self._html end
local row = mw.html.create('tr')
-- Date cell local dateCell = row:tag('td') if self.oldid then dateCell :tag('span') :addClass('plainlinks') :wikitext(makeUrlLink(self.currentTitle.subjectPageTitle:fullUrl, self.date )) else dateCell:wikitext(self.date) end
-- Process cell row :tag('td') :wikitext(string.format("%s", self.link, self:getName(articleHistoryObj) ))
-- Result cell row :tag('td') :wikitext(self:getResult(articleHistoryObj))
self._html = row return rowend
--------------------------------------------------------------------------------- CollapsibleNotice class-- This class makes notices that go in the collapsible part of the template,-- underneath the list of actions.-------------------------------------------------------------------------------
local CollapsibleNotice = setmetatable(Row)CollapsibleNotice.__index = CollapsibleNotice
function CollapsibleNotice.new(data) local obj = Row.new(data) setmetatable(obj, CollapsibleNotice)
obj:setIconValues(data.icon, data.iconCaption, data.iconSize ) obj:setNoticeBarIconValues(data.noticeBarIcon, data.noticeBarIconCaption, data.noticeBarIconSize ) obj:setText(data.text) obj:setCollapsibleText(data.collapsibleText) obj:setCategories(data.categories)
return objend
function CollapsibleNotice:setCollapsibleText(s) self.collapsibleText = send
function CollapsibleNotice:getCollapsibleText(articleHistoryObj) return maybeCallFunc(self.collapsibleText, articleHistoryObj, self)end
function CollapsibleNotice:getIconSize return self.iconSize or self.cfg.defaultCollapsibleNoticeIconSize or '20px'end
function CollapsibleNotice:exportHtml(articleHistoryObj, isInCollapsibleTable) local cacheKey = isInCollapsibleTable and '_htmlCacheCollapsible' or '_htmlCacheDefault' return self:_cachedTry(cacheKey, '_isHtmlError', function local text = self:getText(articleHistoryObj) if not text then return nil end
local function maybeMakeCollapsibleTable(cell, text, collapsibleText) -- If collapsible text is specified, makes a collapsible table -- inside the cell with two rows, a header row with one cell and a -- collapsed row with one cell. These are filled with text and -- collapsedText, respectively. If no collapsible text is -- specified, the text is added to the cell as-is. if collapsibleText then cell :tag('div') :addClass('mw-collapsible mw-collapsed') :tag('div') :wikitext(text) :done :tag('div') :addClass('mw-collapsible-content') :css('border', '1px silver solid') :wikitext(collapsibleText) else cell:wikitext(text) end end
local html = mw.html.create('tr') local icon = self:renderIcon(articleHistoryObj) local collapsibleText = self:getCollapsibleText(articleHistoryObj) if isInCollapsibleTable then local textCell = html:tag('td') :attr('colspan', 3) :css('width', '100%') local rowText if icon then rowText = icon .. ' ' .. text else rowText = text end maybeMakeCollapsibleTable(textCell, rowText, collapsibleText) else local textCell = html :tag('td') :addClass('mbox-image') :wikitext(icon) :done :tag('td') :addClass('mbox-text') maybeMakeCollapsibleTable(textCell, text, collapsibleText) end
return html end)end
--------------------------------------------------------------------------------- ArticleHistory class-- This class represents the whole template.-------------------------------------------------------------------------------
local ArticleHistory = ArticleHistory.__index = ArticleHistoryaddMixin(ArticleHistory, Message)
function ArticleHistory.new(args, cfg, currentTitle) local obj = setmetatable(ArticleHistory)
-- Set input obj.args = args or obj.currentTitle = currentTitle or mw.title.getCurrentTitle
-- Define object structure. obj._errors = obj._allObjectsCache =
-- Format the config local function substituteAliases(t, ret) -- This function substitutes strings found in an "aliases" subtable -- as keys in the parent table. It works recursively, so "aliases" -- subtables can be placed at any level. It assumes that tables will -- not be nested recursively, which should be true in the case of our -- config file. ret = ret or for k, v in pairs(t) do if k ~= 'aliases' then if type(v)
-- do local prefixArgs = for k, v in pairs(obj.args) do if type(k)
return objend
function ArticleHistory:try(func, ...) if DEBUG_MODE then local val = func(...) return val else local success, val = pcall(func, ...) if success then return val else table.insert(self._errors, val) return nil end endend
function ArticleHistory:getActionObjects -- Gets an array of action objects for the parameters specified by the -- user. We memoise this so that the parameters only have to be processed -- once. if self.actions then return self.actions end
-- Get the action args, and exit if they don't exist. local actionArgs = self.prefixArgs[self.cfg.actionParamPrefix] if not actionArgs then self.actions = return self.actions end
-- Make the objects. local actions = local suffixes = self.cfg.actionParamSuffixes for _, t in ipairs(actionArgs) do local objArgs = for k, v in pairs(t) do local newK = suffixes[k] if newK then objArgs[newK] = v end end objArgs.paramNum = t[1] objArgs.cfg = self.cfg objArgs.currentTitle = self.currentTitle local actionObj = self:try(Action.new, objArgs) table.insert(actions, actionObj) end self.actions = actions return actionsend
function ArticleHistory:getStatusIdForCode(code) -- Gets a status ID given a status code. If no code is specified, returns -- nil, and if the code is invalid, raises an error. if not code then return nil end local statuses = self.cfg.statuses local codeUpper = mw.ustring.upper(code) if statuses[codeUpper] then return statuses[codeUpper].id else self:addWarning(self:message('articlehistory-warning-invalid-status', code), self:message('articlehistory-warning-invalid-status-help') ) return nil endend
function ArticleHistory:getStatusObj -- Get the status object for the current status. if self.statusObj
-- Check that some actions were specified, and if not add a warning. local actions = self:getActionObjects if #actions < 1 then self:addWarning(self:message('articlehistory-warning-status-no-actions'), self:message('articlehistory-warning-status-no-actions-help') ) end
-- Make a new status object. local statusObjData = local isMulti = self.cfg.statuses[statusId].isMulti local initFunc = isMulti and MultiStatus.new or Status.new local statusObj = self:try(initFunc, statusObjData) self.statusObj = statusObj or false return self.statusObj or nilend
function ArticleHistory:getStatusId local statusObj = self:getStatusObj return statusObj and statusObj.idend
function ArticleHistory:_noticeFactory(memoizeKey, configKey, class) -- This holds the logic for fetching tables of Notice and CollapsibleNotice -- objects. if self[memoizeKey] then return self[memoizeKey] end local ret = for _, t in ipairs(self.cfg[configKey] or) do if t.isActive(self) then local data = for k, v in pairs(t) do if k ~= 'isActive' then data[k] = v end end data.cfg = self.cfg data.currentTitle = self.currentTitle ret[#ret + 1] = class.new(data) end end self[memoizeKey] = ret return retend
function ArticleHistory:getNoticeObjects return self:_noticeFactory('notices', 'notices', Notice)end
function ArticleHistory:getCollapsibleNoticeObjects return self:_noticeFactory('collapsibleNotices', 'collapsibleNotices', CollapsibleNotice )end
function ArticleHistory:getAllObjects(addSelf) local cacheKey = addSelf and 'addSelf' or 'default' local ret = self._allObjectsCache[cacheKey] if not ret then ret = local statusObj = self:getStatusObj if statusObj then ret[#ret + 1] = statusObj end local objTables = for _, t in ipairs(objTables) do for _, obj in ipairs(t) do ret[#ret + 1] = obj end end if addSelf then ret[#ret + 1] = self end self._allObjectsCache[cacheKey] = ret end return retend
function ArticleHistory:getNoticeBarIcons local ret = -- Icons that aren't part of a row. if self.cfg.noticeBarIcons then for _, data in ipairs(self.cfg.noticeBarIcons) do if data.isActive(self) then ret[#ret + 1] = renderImage(data.icon, nil, data.size or self.cfg.defaultNoticeBarIconSize ) end end end -- Icons in row objects. for _, obj in ipairs(self:getAllObjects) do ret[#ret + 1] = obj:exportNoticeBarIcon(self) end return retend
function ArticleHistory:getErrorMessages -- Returns an array of error/warning strings. Error strings come first. local ret = for _, msg in ipairs(self._errors) do ret[#ret + 1] = msg end for _, obj in ipairs(self:getAllObjects(true)) do for _, msg in ipairs(obj:getWarnings) do ret[#ret + 1] = msg end end return retend
function ArticleHistory:categoriesAreActive -- Returns a boolean indicating whether categories should be output or not. local title = self.currentTitle local ns = title.namespace return title.isTalkPage and ns ~= 3 -- not user talk and ns ~= 119 -- not draft talkend
function ArticleHistory:renderCategories local ret =
if self:categoriesAreActive then -- Child object categories for _, obj in ipairs(self:getAllObjects) do local categories = self:try(obj.getCategories, obj, self) for _, categoryObj in ipairs(categories or) do ret[#ret + 1] = tostring(categoryObj) end end
-- Extra categories for _, func in ipairs(self.cfg.extraCategories or) do local cats = func(self) or for _, categoryObj in ipairs(cats) do ret[#ret + 1] = tostring(categoryObj) end end end
return table.concat(ret)end
function ArticleHistory:__tostring local root = mw.html.create
-- Table root local tableRoot = root:tag('table') tableRoot:addClass('article-history tmbox tmbox-notice')
-- Status local statusObj = self:getStatusObj if statusObj then tableRoot:node(self:try(statusObj.exportHtml, statusObj, self)) end
-- Notices local notices = self:getNoticeObjects for _, noticeObj in ipairs(notices) do tableRoot:node(self:try(noticeObj.exportHtml, noticeObj, self)) end
-- Get action objects and the collapsible notice objects, and generate the -- HTML objects for the action objects. We need the action HTML objects so -- that we can accurately calculate the number of collapsible rows, as some -- action objects may generate errors when the HTML is generated. local actions = self:getActionObjects or local collapsibleNotices = self:getCollapsibleNoticeObjects or local collapsibleNoticeHtmlObjects, actionHtmlObjects =, for _, obj in ipairs(actions) do table.insert(actionHtmlObjects, self:try(obj.exportHtml, obj, self) ) end for _, obj in ipairs(collapsibleNotices) do table.insert(collapsibleNoticeHtmlObjects, self:try(obj.exportHtml, obj, self, true) -- Render the collapsed version ) end local nActionRows = #actionHtmlObjects local nCollapsibleRows = nActionRows + #collapsibleNoticeHtmlObjects
-- Find out if we are collapsed or not. local isCollapsed = yesno(self.args.collapse) if isCollapsed
'all' then isCollapsed = false elseif nCollapsibleRows
-- If we are not collapsed, re-render the collapsible notices in the -- non-collapsed version. if not isCollapsed then collapsibleNoticeHtmlObjects = for _, obj in ipairs(collapsibleNotices) do table.insert(collapsibleNoticeHtmlObjects, self:try(obj.exportHtml, obj, self, false) ) end end
-- Collapsible table for actions and collapsible notices. Collapsible -- notices are only included in the table if it is collapsed. Action rows -- are always included. local collapsibleTable if isCollapsed or nActionRows > 0 then -- Collapsible table base collapsibleTable = tableRoot :tag('tr') :tag('td') :attr('colspan', 2) :css('width', '100%') :tag('table') :addClass('article-history-milestones') :addClass(isCollapsed and 'mw-collapsible mw-collapsed' or nil) :css('width', '100%') :css('font-size', '90%')
-- Header row local ctHeader = collapsibleTable :tag('tr') :tag('th') :attr('colspan', 3) :css('font-size', '110%')
-- Notice bar if isCollapsed then local noticeBarIcons = self:getNoticeBarIcons if #noticeBarIcons > 0 then local noticeBar = ctHeader:tag('span'):css('float', 'left') for _, icon in ipairs(noticeBarIcons) do noticeBar:wikitext(icon) end ctHeader:wikitext(' ') end end
-- Header text if mw.site.namespaces[self.currentTitle.namespace].subject.id
-- Subheadings if nActionRows > 0 then collapsibleTable :tag('tr') :css('text-align', 'left') :tag('th') :wikitext(self:message('milestones-date-header')) :done :tag('th') :wikitext(self:message('milestones-process-header')) :done :tag('th') :wikitext(self:message('milestones-result-header')) end
-- Actions for _, htmlObj in ipairs(actionHtmlObjects) do collapsibleTable:node(htmlObj) end end
-- Collapsible notices and current status -- These are only included in the collapsible table if it is collapsed. -- Otherwise, they are added afterwards, so that they align with the -- notices. do local tableNode, statusColspan if isCollapsed then tableNode = collapsibleTable statusColspan = 3 else tableNode = tableRoot statusColspan = 2 end
-- Collapsible notices for _, obj in ipairs(collapsibleNotices) do tableNode:node(self:try(obj.exportHtml, obj, self, isCollapsed)) end
-- Current status if statusObj and nActionRows > 1 then tableNode :tag('tr') :tag('td') :attr('colspan', statusColspan) :wikitext(self:message('status-blurb', statusObj.name)) end end
-- Get the categories. We have to do this before the error row, so that -- category errors display. local categories = self:renderCategories
-- Error row and error category local errors = self:getErrorMessages local errorCategory if #errors > 0 then local errorList = tableRoot :tag('tr') :tag('td') :attr('colspan', 2) :addClass('mbox-text') :tag('ul') :addClass('error') :css('font-weight', 'bold') for _, msg in ipairs(errors) do errorList:tag('li'):wikitext(msg) end if self:categoriesAreActive then errorCategory = tostring(Category.new(self:message('error-category' ))) end
-- If there are no errors and no active objects, then exit. We can't make -- this check earlier as we don't know where the errors may be until we -- have finished rendering the banner. elseif #self:getAllObjects < 1 then return end
-- Add the categories root:wikitext(categories) root:wikitext(errorCategory) local frame = mw.getCurrentFrame return frame:extensionTag .. frame:extensionTag .. tostring(root)end
--------------------------------------------------------------------------------- Exports-- These functions are called from Lua and from wikitext.-------------------------------------------------------------------------------
local p =
function p._main(args, cfg, currentTitle) local articleHistoryObj = ArticleHistory.new(args, cfg, currentTitle) return tostring(articleHistoryObj)end
function p.main(frame) local args = require('Module:Arguments').getArgs(frame,) if frame:getTitle:find('sandbox', 1, true) then CONFIG_PAGE = CONFIG_PAGE .. '/sandbox' end return p._main(args)end
function p._exportClasses return end
return p