Mô đun:Message box

Tài liệu mô đun

This is a meta-module that implements the message box templates {{mbox}}, {{ambox}}, {{cmbox}}, {{fmbox}}, {{imbox}}, {{ombox}}, and {{tmbox}}. It is intended to be used from Lua modules, and should not be used directly from wiki pages. If you want to use this module's functionality from a wiki page, please use the individual message box templates instead.

Usage

To use this module from another Lua module, first you need to load it.

local messageBox = require('Module:Message box')

To create a message box, use the main function. It takes two parameters: the first is the box type (as a string), and the second is a table containing the message box parameters.

local box = messageBox.main( boxType, {    param1 = param1,    param2 = param2,    -- More parameters...})

There are seven available box types:

Box typeTemplatePurpose
mbox{{mbox}}For message boxes to be used in multiple namespaces
ambox{{ambox}}For article message boxes
cmbox{{cmbox}}For category message boxes
fmbox{{fmbox}}For interface message boxes
imbox{{imbox}}For file namespace message boxes
tmbox{{tmbox}}For talk page message boxes
ombox{{ombox}}For message boxes in other namespaces

See the template page of each box type for the available parameters.

Usage from #invoke

As well as the main function, this module has separate functions for each box type. They are accessed using the code {{#invoke:Message box|mbox|...}}, {{#invoke:Message box|ambox|...}}, etc. These will work when called from other modules, but they access code used to process arguments passed from #invoke, and so calling them will be less efficient than calling main.

Technical details

The module uses the same basic code for each of the templates listed above; the differences between each of them are configured using the data at Module:Message box/configuration. Here are the various configuration options and what they mean:

  • types - a table containing data used by the type parameter of the message box. The table keys are the values that can be passed to the type parameter, and the table values are tables containing the class and the image used by that type.
  • default - the type to use if no value was passed to the type parameter, or if an invalid value was specified.
  • showInvalidTypeError - whether to show an error if the value passed to the type parameter was invalid.
  • allowBlankParams - usually blank values are stripped from parameters passed to the module. However, whitespace is preserved for the parameters included in the allowBlankParams table.
  • allowSmall - whether a small version of the message box can be produced with "small=yes".
  • smallParam - a custom name for the small parameter. For example, if set to "left" you can produce a small message box using "small=left".
  • smallClass - the class to use for small message boxes.
  • substCheck - whether to perform a subst check or not.
  • classes - an array of classes to use with the message box.
  • imageEmptyCell - whether to use an empty <td>...</td> cell if there is no image set. This is used to preserve spacing for message boxes with a width of less than 100% of the screen.
  • imageEmptyCellStyle - whether empty image cells should be styled.
  • imageCheckBlank - whether "image=blank" results in no image being displayed.
  • imageSmallSize - usually, images used in small message boxes are set to 30x30px. This sets a custom size.
  • imageCellDiv - whether to enclose the image in a div enforcing a maximum image size.
  • useCollapsibleTextFields - whether to use text fields that can be collapsed, i.e. "issue", "fix", "talk", etc. Currently only used in ambox.
  • imageRightNone - whether imageright=none results in no image being displayed on the right-hand side of the message box.
  • sectionDefault - the default name for the "section" parameter. Depends on useCollapsibleTextFields.
  • allowMainspaceCategories - allow categorisation in the main namespace.
  • templateCategory - the name of a category to be placed on the template page.
  • templateCategoryRequireName - whether the name parameter is required to display the template category.
  • templateErrorCategory - the name of the error category to be used on the template page.
  • templateErrorParamsToCheck - an array of parameter names to check. If any are absent, the templateErrorCategory is applied to the template page.

-- This is a meta-module for producing message box templates, including-- {{mbox}}, {{ambox}}, {{imbox}}, {{tmbox}}, {{ombox}}, {{cmbox}} and {{fmbox}}.-- Load necessary modules.require('Mô đun:No globals')local getArgslocal yesno = require('Mô đun:Yesno')-- Get a language object for formatDate and ucfirst.local lang = mw.language.getContentLanguage()-- Define constantslocal CONFIG_MODULE = 'Mô đun:Message box/configuration'local DEMOSPACES = {talk = 'tmbox', image = 'imbox', file = 'imbox', category = 'cmbox', article = 'ambox', main = 'ambox'}local viArgs = {["kiểu"] = "style",["kiểu chữ"] = "textstyle",["loại"] = "type",["hình"] = "image",["chữ"] = "text",["hình phải"] = "imageright",["tên"] = "name",["thế"] = "subst",["lớp"] = "class",["vấn đề"] = "issue",["giải quyết"] = "fix",["khắc phục"] = "fix",["dưới"] = "below",}---------------------------------------------------------------------------------- Helper functions--------------------------------------------------------------------------------local function viMapArgs(args)local newArgs = {}for k, v in pairs(args) donewArgs[viArgs[k] or k] = vendreturn newArgsendlocal function getTitleObject(...)-- Get the title object, passing the function through pcall-- in case we are over the expensive function count limit.local success, title = pcall(mw.title.new, ...)if success thenreturn titleendendlocal function union(t1, t2)-- Returns the union of two arrays.local vals = {}for i, v in ipairs(t1) dovals[v] = trueendfor i, v in ipairs(t2) dovals[v] = trueendlocal ret = {}for k in pairs(vals) dotable.insert(ret, k)endtable.sort(ret)return retendlocal function getArgNums(args, prefix)local nums = {}for k, v in pairs(args) dolocal num = mw.ustring.match(tostring(k), '^' .. prefix .. '([1-9]%d*)$')if num thentable.insert(nums, tonumber(num))endendtable.sort(nums)return numsend---------------------------------------------------------------------------------- Box class definition--------------------------------------------------------------------------------local MessageBox = {}MessageBox.__index = MessageBoxfunction MessageBox.new(boxType, args, cfg)args = args or {}local obj = {}-- Set the title object and the namespace.obj.title = getTitleObject(args.page) or mw.title.getCurrentTitle()-- Set the config for our box type.obj.cfg = cfg[boxType]if not obj.cfg thenlocal ns = obj.title.namespace-- boxType is "mbox" or invalid inputif args.demospace and args.demospace ~= '' then-- implement demospace parameter of mboxlocal demospace = string.lower(args.demospace)if DEMOSPACES[demospace] then-- use template from DEMOSPACESobj.cfg = cfg[DEMOSPACES[demospace]]elseif string.find( demospace, 'talk' ) then-- demo as a talk pageobj.cfg = cfg.tmboxelse-- default to omboxobj.cfg = cfg.omboxendelseif ns == 0 thenobj.cfg = cfg.ambox -- main namespaceelseif ns == 6 thenobj.cfg = cfg.imbox -- file namespaceelseif ns == 14 thenobj.cfg = cfg.cmbox -- category namespaceelselocal nsTable = mw.site.namespaces[ns]if nsTable and nsTable.isTalk thenobj.cfg = cfg.tmbox -- any talk namespaceelseobj.cfg = cfg.ombox -- other namespaces or invalid inputendendend-- Set the arguments, and remove all blank arguments except for the ones-- listed in cfg.allowBlankParams.dolocal newArgs = {}for k, v in pairs(args) doif v ~= '' thennewArgs[k] = vendendfor i, param in ipairs(obj.cfg.allowBlankParams or {}) donewArgs[param] = args[param]endobj.args = newArgsend-- Define internal data structure.obj.categories = {}obj.classes = {}-- For lazy loading of [[Module:Category handler]].obj.hasCategories = falsereturn setmetatable(obj, MessageBox)endfunction MessageBox:addCat(ns, cat, sort)if not cat thenreturn nilendif sort thencat = string.format('[[Thể loại:%s|%s]]', cat, sort)elsecat = string.format('[[Thể loại:%s]]', cat)endself.hasCategories = trueself.categories[ns] = self.categories[ns] or {}table.insert(self.categories[ns], cat)endfunction MessageBox:addClass(class)if not class thenreturn nilendtable.insert(self.classes, class)endfunction MessageBox:setParameters()local args = self.argslocal cfg = self.cfg-- Get type data.self.type = args.typelocal typeData = cfg.types[self.type]self.invalidTypeError = cfg.showInvalidTypeErrorand self.typeand not typeDatatypeData = typeData or cfg.types[cfg.default]self.typeClass = typeData.classself.typeImage = typeData.image-- Find if the box has been wrongly substituted.self.isSubstituted = cfg.substCheck and args.subst == 'SUBST'-- Find whether we are using a small message box.self.isSmall = cfg.allowSmall and (cfg.smallParam and args.small == cfg.smallParamor not cfg.smallParam and yesno(args.small))-- Add attributes, classes and styles.self.id = args.idself.name = args.nameif self.name thenself:addClass('box-' .. string.gsub(self.name,' ','_'))endif yesno(args.plainlinks) ~= false thenself:addClass('plainlinks')endfor _, class in ipairs(cfg.classes or {}) doself:addClass(class)endif self.isSmall thenself:addClass(cfg.smallClass or 'mbox-small')endself:addClass(self.typeClass)self:addClass(args.class)self.style = args.styleself.attrs = args.attrs-- Set text style.self.textstyle = args.textstyle-- Find if we are on the template page or not. This functionality is only-- used if useCollapsibleTextFields is set, or if both cfg.templateCategory-- and cfg.templateCategoryRequireName are set.self.useCollapsibleTextFields = cfg.useCollapsibleTextFieldsif self.useCollapsibleTextFieldsor cfg.templateCategoryand cfg.templateCategoryRequireNamethenif self.name thenlocal templateName = mw.ustring.match(self.name,'^[bB][aẢ][nN][%s_]+[mM][ẫẪ][uU][%s_]*:[%s_]*(.*)$') or self.nametemplateName = 'Bản mẫu:' .. templateNameself.templateTitle = getTitleObject(templateName)endself.isTemplatePage = self.templateTitleand mw.title.equals(self.title, self.templateTitle)end-- Process data for collapsible text fields. At the moment these are only-- used in {{ambox}}.if self.useCollapsibleTextFields then-- Get the self.issue value.if self.isSmall and args.smalltext thenself.issue = args.smalltextelselocal sectif args.sect == '' thensect = (lang:ucfirst(cfg.sectionDefault) or 'Trang') .. ' này'elseif type(args.sect) == 'string' thensect = lang:ucfirst(args.sect) .. ' này'endlocal issue = args.issueissue = type(issue) == 'string' and issue ~= '' and issue or nillocal text = args.texttext = type(text) == 'string' and text or nillocal issues = {}table.insert(issues, sect)table.insert(issues, issue)table.insert(issues, text)self.issue = table.concat(issues, ' ')end-- Get the self.talk value.local talk = args.talk-- Show talk links on the template page or template subpages if the talk-- parameter is blank.if talk == ''and self.templateTitleand (mw.title.equals(self.templateTitle, self.title)or self.title:isSubpageOf(self.templateTitle))thentalk = '#'elseif talk == '' thentalk = nilendif talk then-- If the talk value is a talk page, make a link to that page. Else-- assume that it's a section heading, and make a link to the talk-- page of the current page with that section heading.local talkTitle = getTitleObject(talk)local talkArgIsTalkPage = trueif not talkTitle or not talkTitle.isTalkPage thentalkArgIsTalkPage = falsetalkTitle = getTitleObject(self.title.text,mw.site.namespaces[self.title.namespace].talk.id)endif talkTitle and talkTitle.exists thenlocal talkText = 'Có thể có thảo luận liên quan tại'if talkArgIsTalkPage thentalkText = string.format('%s [[%s|%s]].',talkText,talk,talkTitle.prefixedText)elsetalkText = string.format('%s [[%s#%s|trang thảo luận]].',talkText,talkTitle.prefixedText,talk)endself.talk = talkTextendend-- Get other values.self.fix = args.fix ~= '' and args.fix or nillocal dateif args.date and args.date ~= '' thendate = args.dateelseif args.date == '' and self.isTemplatePage thendate = lang:formatDate('"Tháng" n "năm" Y')endif date thenself.date = string.format(" <small class='date-container'>''(<span class='date'>%s</span>)''</small>", date)endself.info = args.infoend-- Set the non-collapsible text field. At the moment this is used by all box-- types other than ambox, and also by ambox when small=yes.if self.isSmall thenself.text = args.smalltext or args.textelseself.text = args.textend-- Set the below row.self.below = cfg.below and args.below-- General image settings.self.imageCellDiv = not self.isSmall and cfg.imageCellDivself.imageEmptyCell = cfg.imageEmptyCellif cfg.imageEmptyCellStyle thenself.imageEmptyCellStyle = 'border:none;padding:0px;width:1px'end-- Left image settings.local imageLeft = self.isSmall and args.smallimage or args.imageif cfg.imageCheckBlank and imageLeft ~= 'blank' and imageLeft ~= 'none' and imageLeft ~= 'không'or not cfg.imageCheckBlank and (imageLeft ~= 'none' and imageLeft ~= 'không')thenself.imageLeft = imageLeftif not imageLeft thenlocal imageSize = self.isSmalland (cfg.imageSmallSize or '30x30px')or '40x40px'self.imageLeft = string.format('[[Tập tin:%s|%s|link=|alt=]]', self.typeImageor 'Imbox notice.png', imageSize)endend-- Right image settings.local imageRight = self.isSmall and args.smallimageright or args.imagerightif not (cfg.imageRightNone and (imageRight == 'none' or imageRight == 'không')) thenself.imageRight = imageRightendendfunction MessageBox:setMainspaceCategories()local args = self.argslocal cfg = self.cfgif not cfg.allowMainspaceCategories thenreturn nilendlocal nums = {}for _, prefix in ipairs{'cat', 'category', 'all'} doargs[prefix .. '1'] = args[prefix]nums = union(nums, getArgNums(args, prefix))end-- The following is roughly equivalent to the old {{Ambox/category}}.local date = args.datedate = type(date) == 'string' and datelocal preposition = 'từ'for _, num in ipairs(nums) dolocal mainCat = args['cat' .. tostring(num)]or args['category' .. tostring(num)]local allCat = args['all' .. tostring(num)]mainCat = type(mainCat) == 'string' and mainCatallCat = type(allCat) == 'string' and allCatif mainCat and date and date ~= '' thenlocal catTitle = string.format('%s %s %s', mainCat, preposition, date)self:addCat(0, catTitle)catTitle = getTitleObject('Thể loại:' .. catTitle)if not catTitle or not catTitle.exists thenself:addCat(0, 'Tham số bản mẫu ngày tháng không hợp lệ')endelseif mainCat and (not date or date == '') thenself:addCat(0, mainCat)endif allCat thenself:addCat(0, allCat)endendendfunction MessageBox:setTemplateCategories()local args = self.argslocal cfg = self.cfg-- Add template categories.if cfg.templateCategory thenif cfg.templateCategoryRequireName thenif self.isTemplatePage thenself:addCat(10, cfg.templateCategory)endelseif not self.title.isSubpage thenself:addCat(10, cfg.templateCategory)endend-- Add template error categories.if cfg.templateErrorCategory thenlocal templateErrorCategory = cfg.templateErrorCategorylocal templateCat, templateSortif not self.name and not self.title.isSubpage thentemplateCat = templateErrorCategoryelseif self.isTemplatePage thenlocal paramsToCheck = cfg.templateErrorParamsToCheck or {}local count = 0for i, param in ipairs(paramsToCheck) doif not args[param] thencount = count + 1endendif count > 0 thentemplateCat = templateErrorCategorytemplateSort = tostring(count)endif self.categoryNums and #self.categoryNums > 0 thentemplateCat = templateErrorCategorytemplateSort = 'C'endendself:addCat(10, templateCat, templateSort)endendfunction MessageBox:setAllNamespaceCategories()-- Set categories for all namespaces.if self.invalidTypeError thenlocal allSort = (self.title.namespace == 0 and 'Main:' or '') .. self.title.prefixedTextself:addCat('all', 'Tham số hộp thông báo Wikipedia cần sửa chữa', allSort)endif self.isSubstituted thenself:addCat('all', 'Trang nhúng bản mẫu một cách không chính xác')endendfunction MessageBox:setCategories()if self.title.namespace == 0 thenself:setMainspaceCategories()elseif self.title.namespace == 10 thenself:setTemplateCategories()endself:setAllNamespaceCategories()endfunction MessageBox:renderCategories()if not self.hasCategories then-- No categories added, no need to pass them to Category handler so,-- if it was invoked, it would return the empty string.-- So we shortcut and return the empty string.return ""end-- Convert category tables to strings and pass them through-- [[Module:Category handler]].return require('Mô đun:Category handler')._main{main = table.concat(self.categories[0] or {}),template = table.concat(self.categories[10] or {}),all = table.concat(self.categories.all or {}),nocat = self.args.nocat,page = self.args.page}endfunction MessageBox:export()local root = mw.html.create()-- Add the subst check error.if self.isSubstituted and self.name thenroot:tag('b'):addClass('error'):wikitext(string.format('Bản mẫu <code>%s[[Bản mẫu:%s|%s]]%s</code> đã được thế sai quy cách.',mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}')))end-- Create the box table.local boxTable = root:tag('table')boxTable:attr('id', self.id or nil)for i, class in ipairs(self.classes or {}) doboxTable:addClass(class or nil)endboxTable:cssText(self.style or nil):attr('role', 'presentation')if self.attrs thenboxTable:attr(self.attrs)end-- Add the left-hand image.local row = boxTable:tag('tr')if self.imageLeft thenlocal imageLeftCell = row:tag('td'):addClass('mbox-image')if self.imageCellDiv then-- If we are using a div, redefine imageLeftCell so that the image-- is inside it. Divs use style="width: 52px;", which limits the-- image width to 52px. If any images in a div are wider than that,-- they may overlap with the text or cause other display problems.imageLeftCell = imageLeftCell:tag('div'):css('width', '52px')endimageLeftCell:wikitext(self.imageLeft or nil)elseif self.imageEmptyCell then-- Some message boxes define an empty cell if no image is specified, and-- some don't. The old template code in templates where empty cells are-- specified gives the following hint: "No image. Cell with some width-- or padding necessary for text cell to have 100% width."row:tag('td'):addClass('mbox-empty-cell'):cssText(self.imageEmptyCellStyle or nil)end-- Add the text.local textCell = row:tag('td'):addClass('mbox-text')if self.useCollapsibleTextFields then-- The message box uses advanced text parameters that allow things to be-- collapsible. At the moment, only ambox uses this.textCell:cssText(self.textstyle or nil)local textCellDiv = textCell:tag('div')textCellDiv:addClass('mbox-text-span'):wikitext(self.issue or nil)if (self.talk or self.fix) and not self.isSmall thentextCellDiv:tag('span'):addClass('hide-when-compact'):wikitext(self.talk and (' ' .. self.talk) or nil):wikitext(self.fix and (' ' .. self.fix) or nil)endtextCellDiv:wikitext(self.date and (' ' .. self.date) or nil)if self.info and not self.isSmall thentextCellDiv:tag('span'):addClass('hide-when-compact'):wikitext(self.info and (' ' .. self.info) or nil)endelse-- Default text formatting - anything goes.textCell:cssText(self.textstyle or nil):wikitext(self.text or nil)end-- Add the right-hand image.if self.imageRight thenlocal imageRightCell = row:tag('td'):addClass('mbox-imageright')if self.imageCellDiv then-- If we are using a div, redefine imageRightCell so that the image-- is inside it.imageRightCell = imageRightCell:tag('div'):css('width', '52px')endimageRightCell:wikitext(self.imageRight or nil)end-- Add the below row.if self.below thenboxTable:tag('tr'):tag('td'):attr('colspan', self.imageRight and '3' or '2'):addClass('mbox-text'):cssText(self.textstyle or nil):wikitext(self.below or nil)end-- Add error message for invalid type parameters.if self.invalidTypeError thenroot:tag('div'):css('text-align', 'center'):wikitext(string.format('Hộp thông báo này đang sử dụng một tham số "loại=%s" sai quy cách và cần được sửa chữa.',self.type or ''))end-- Add categories.root:wikitext(self:renderCategories() or nil)return tostring(root)end---------------------------------------------------------------------------------- Exports--------------------------------------------------------------------------------local p, mt = {}, {}function p._exportClasses()-- For testing.return {MessageBox = MessageBox}endfunction p.main(boxType, args, cfgTables)args = viMapArgs(args)local box = MessageBox.new(boxType, args, cfgTables or mw.loadData(CONFIG_MODULE))box:setParameters()box:setCategories()return box:export()endfunction mt.__index(t, k)return function (frame)if not getArgs thengetArgs = require('Mô đun:Arguments').getArgsendreturn t.main(k, getArgs(frame, {trim = false, removeBlanks = false}))endendreturn setmetatable(p, mt)