| Latest revision |
Your text |
| Line 2: |
Line 2: |
| local getArgs | | local getArgs |
| local yesno = require('Module:Yesno') | | local yesno = require('Module:Yesno') |
| | local err = require('Module:Error') |
| | local TNT = require('Module:TNT') |
| local lang = mw.language.getContentLanguage() | | local lang = mw.language.getContentLanguage() |
| | local int_lang = mw.getCurrentFrame():preprocess('{{int:lang}}') |
|
| |
|
| local CONFIG_MODULE = 'Module:Message box/configuration' | | local CONFIG_MODULE = 'Module:Message box/configuration' |
| local DEMOSPACES = {talk = 'tmbox', image = 'imbox', file = 'imbox', category = 'cmbox', article = 'ambox', main = 'ambox'} | | local DEMOSPACES = {talk = 'tmbox', image = 'mbox', file = 'mbox', category = 'mbox', article = 'mbox', main = 'mbox'} |
|
| |
|
| -------------------------------------------------------------------------------- | | -------------------------------------------------------------------------------- |
| -- Helper functions | | -- Helper functions |
| -------------------------------------------------------------------------------- | | -------------------------------------------------------------------------------- |
| | |
| | local function errormsg(text) |
| | -- create warning text |
| | return err.error({tag="span", message=text}) |
| | end |
|
| |
|
| local function getTitleObject(...) | | local function getTitleObject(...) |
| Line 18: |
Line 26: |
| return title | | return title |
| end | | end |
| end
| |
|
| |
| local function union(t1, t2)
| |
| -- Returns the union of two arrays.
| |
| local vals = {}
| |
| for i, v in ipairs(t1) do
| |
| vals[v] = true
| |
| end
| |
| for i, v in ipairs(t2) do
| |
| vals[v] = true
| |
| end
| |
| local ret = {}
| |
| for k in pairs(vals) do
| |
| table.insert(ret, k)
| |
| end
| |
| table.sort(ret)
| |
| return ret
| |
| end | | end |
|
| |
|
| Line 78: |
Line 69: |
| obj.cfg = cfg.tmbox | | obj.cfg = cfg.tmbox |
| else | | else |
| -- default to ombox | | -- default to ambox |
| obj.cfg = cfg.ombox | | obj.cfg = cfg.mbox |
| end | | end |
| elseif ns == 0 then | | elseif ns == 0 then |
| obj.cfg = cfg.ambox -- main namespace | | obj.cfg = cfg.mbox -- main namespace |
| elseif ns == 6 then | | elseif ns == 6 then |
| obj.cfg = cfg.imbox -- file namespace | | obj.cfg = cfg.mbox -- file namespace |
| elseif ns == 14 then | | elseif ns == 14 then |
| obj.cfg = cfg.cmbox -- category namespace | | obj.cfg = cfg.mbox -- category namespace |
| else | | else |
| local nsTable = mw.site.namespaces[ns] | | local nsTable = mw.site.namespaces[ns] |
| Line 92: |
Line 83: |
| obj.cfg = cfg.tmbox -- any talk namespace | | obj.cfg = cfg.tmbox -- any talk namespace |
| else | | else |
| obj.cfg = cfg.ombox -- other namespaces or invalid input | | obj.cfg = cfg.mbox -- other namespaces or invalid input |
| end | | end |
| end | | end |
| Line 156: |
Line 147: |
| self.typeImage = typeData.image | | self.typeImage = typeData.image |
| self.typeImageNeedsLink = typeData.imageNeedsLink | | self.typeImageNeedsLink = typeData.imageNeedsLink |
|
| |
| -- 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. | | -- Find whether we are using a small message box. |
| Line 166: |
Line 154: |
| ) | | ) |
|
| |
|
| -- Set the below row.
| |
| self.below = cfg.below and args.below
| |
|
| |
| -- Add attributes, classes and styles. | | -- Add attributes, classes and styles. |
| self.id = args.id | | self.id = args.id |
| Line 177: |
Line 162: |
| if yesno(args.plainlinks) ~= false then | | if yesno(args.plainlinks) ~= false then |
| self:addClass('plainlinks') | | self:addClass('plainlinks') |
| end
| |
| if self.below then
| |
| self:addClass('mbox-with-below')
| |
| end | | end |
| for _, class in ipairs(cfg.classes or {}) do | | for _, class in ipairs(cfg.classes or {}) do |
| Line 194: |
Line 176: |
| -- Set text style. | | -- Set text style. |
| self.textstyle = args.textstyle | | self.textstyle = args.textstyle |
|
| |
| -- Set image classes.
| |
| self.imageRightClass = args.imagerightclass or args.imageclass
| |
| self.imageLeftClass = args.imageleftclass or args.imageclass
| |
|
| |
| -- 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.useCollapsibleTextFields
| |
| if self.useCollapsibleTextFields
| |
| or cfg.templateCategory
| |
| and cfg.templateCategoryRequireName
| |
| then
| |
| if self.name then
| |
| local templateName = mw.ustring.match(
| |
| self.name,
| |
| '^[tT][eE][mM][pP][lL][aA][tT][eE][%s_]*:[%s_]*(.*)$'
| |
| ) or self.name
| |
| templateName = 'Template:' .. templateName
| |
| self.templateTitle = getTitleObject(templateName)
| |
| end
| |
| self.isTemplatePage = self.templateTitle
| |
| and mw.title.equals(self.title, self.templateTitle)
| |
| end
| |
| | | |
| -- Process data for collapsible text fields. At the moment these are only | | -- Set width |
| -- used in {{ambox}}. | | self.width = args.width |
| if self.useCollapsibleTextFields then
| | |
| -- Get the self.issue value.
| | -- Set margin |
| if self.isSmall and args.smalltext then
| | self.margin = args.margin |
| self.issue = args.smalltext
| | |
| else
| |
| local sect
| |
| if args.sect == '' then
| |
| sect = 'This ' .. (cfg.sectionDefault or 'page')
| |
| elseif type(args.sect) == 'string' then
| |
| sect = 'This ' .. args.sect
| |
| end
| |
| local issue = args.issue
| |
| issue = type(issue) == 'string' and issue ~= '' and issue or nil
| |
| local text = args.text
| |
| text = type(text) == 'string' and text or nil
| |
| local 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.templateTitle
| |
| and (
| |
| mw.title.equals(self.templateTitle, self.title)
| |
| or self.title:isSubpageOf(self.templateTitle)
| |
| )
| |
| then
| |
| talk = '#'
| |
| elseif talk == '' then
| |
| talk = nil
| |
| end
| |
| if 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 = true
| |
| if not talkTitle or not talkTitle.isTalkPage then
| |
| talkArgIsTalkPage = false
| |
| talkTitle = getTitleObject(
| |
| self.title.text,
| |
| mw.site.namespaces[self.title.namespace].talk.id
| |
| )
| |
| end
| |
| if talkTitle and talkTitle.exists then
| |
| local talkText
| |
| if self.isSmall then
| |
| local talkLink = talkArgIsTalkPage and talk or (talkTitle.prefixedText .. (talk == '#' and '' or '#') .. talk)
| |
| talkText = string.format('([[%s|talk]])', talkLink)
| |
| else
| |
| talkText = 'Relevant discussion may be found on'
| |
| if talkArgIsTalkPage then
| |
| talkText = string.format(
| |
| '%s [[%s|%s]].',
| |
| talkText,
| |
| talk,
| |
| talkTitle.prefixedText
| |
| )
| |
| else
| |
| talkText = string.format(
| |
| '%s the [[%s' .. (talk == '#' and '' or '#') .. '%s|talk page]].',
| |
| talkText,
| |
| talkTitle.prefixedText,
| |
| talk
| |
| )
| |
| end
| |
| end
| |
| self.talk = talkText
| |
| end
| |
| end
| |
| | |
| -- Get other values.
| |
| self.fix = args.fix ~= '' and args.fix or nil
| |
| local date
| |
| if args.date and args.date ~= '' then
| |
| date = args.date
| |
| elseif args.date == '' and self.isTemplatePage then
| |
| date = lang:formatDate('F Y')
| |
| end
| |
| if date then
| |
| self.date = string.format(" <span class='date-container'><i>(<span class='date'>%s</span>)</i></span>", date)
| |
| end
| |
| self.info = args.info
| |
| if yesno(args.removalnotice) then
| |
| self.removalNotice = cfg.removalNotice
| |
| end
| |
| end | |
| | |
| -- Set the non-collapsible text field. At the moment this is used by all box | | -- 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. | | -- types other than ambox, and also by ambox when small=yes. |
| Line 322: |
Line 190: |
| self.text = args.text | | self.text = args.text |
| end | | end |
| | |
| | -- Set the below row. |
| | self.below = cfg.below and args.below |
|
| |
|
| -- General image settings. | | -- General image settings. |
| Line 336: |
Line 207: |
| local imageSize = self.isSmall | | local imageSize = self.isSmall |
| and (cfg.imageSmallSize or '30x30px') | | and (cfg.imageSmallSize or '30x30px') |
| or '40x40px' | | or '45x45px' |
| self.imageLeft = string.format('[[File:%s|%s%s|alt=]]', self.typeImage | | self.imageLeft = string.format('[[File:%s|%s%s|alt=]]', self.typeImage |
| or 'Information icon4.svg', imageSize, self.typeImageNeedsLink and "" or "|link=" ) | | or 'Information icon4.svg', imageSize, self.typeImageNeedsLink and "" or "|link=" ) |
| Line 351: |
Line 222: |
| self.base_templatestyles = cfg.templatestyles | | self.base_templatestyles = cfg.templatestyles |
| self.templatestyles = args.templatestyles | | self.templatestyles = args.templatestyles |
| end
| |
|
| |
| function MessageBox:setMainspaceCategories()
| |
| local args = self.args
| |
| local cfg = self.cfg
| |
|
| |
| if not cfg.allowMainspaceCategories then
| |
| return nil
| |
| end
| |
|
| |
| local nums = {}
| |
| for _, prefix in ipairs{'cat', 'category', 'all'} do
| |
| args[prefix .. '1'] = args[prefix]
| |
| nums = union(nums, getArgNums(args, prefix))
| |
| end
| |
|
| |
| -- The following is roughly equivalent to the old {{Ambox/category}}.
| |
| local date = args.date
| |
| date = type(date) == 'string' and date
| |
| local preposition = 'from'
| |
| for _, num in ipairs(nums) do
| |
| local mainCat = args['cat' .. tostring(num)]
| |
| or args['category' .. tostring(num)]
| |
| local allCat = args['all' .. tostring(num)]
| |
| mainCat = type(mainCat) == 'string' and mainCat
| |
| allCat = type(allCat) == 'string' and allCat
| |
| if mainCat and date and date ~= '' then
| |
| local catTitle = string.format('%s %s %s', mainCat, preposition, date)
| |
| self:addCat(0, catTitle)
| |
| catTitle = getTitleObject('Category:' .. catTitle)
| |
| if not catTitle or not catTitle.exists then
| |
| self:addCat(0, 'Articles with invalid date parameter in template')
| |
| end
| |
| elseif mainCat and (not date or date == '') then
| |
| self:addCat(0, mainCat)
| |
| end
| |
| if allCat then
| |
| self:addCat(0, allCat)
| |
| end
| |
| end
| |
| end | | end |
|
| |
|
| Line 406: |
Line 237: |
| self:addCat(10, cfg.templateCategory) | | self:addCat(10, cfg.templateCategory) |
| end | | end |
| end
| |
|
| |
| -- Add template error categories.
| |
| if cfg.templateErrorCategory then
| |
| local templateErrorCategory = cfg.templateErrorCategory
| |
| local templateCat, templateSort
| |
| if not self.name and not self.title.isSubpage then
| |
| templateCat = templateErrorCategory
| |
| elseif self.isTemplatePage then
| |
| local paramsToCheck = cfg.templateErrorParamsToCheck or {}
| |
| local count = 0
| |
| for i, param in ipairs(paramsToCheck) do
| |
| if not args[param] then
| |
| count = count + 1
| |
| end
| |
| end
| |
| if count > 0 then
| |
| templateCat = templateErrorCategory
| |
| templateSort = tostring(count)
| |
| end
| |
| if self.categoryNums and #self.categoryNums > 0 then
| |
| templateCat = templateErrorCategory
| |
| templateSort = 'C'
| |
| end
| |
| end
| |
| self:addCat(10, templateCat, templateSort)
| |
| end | | end |
| end | | end |
|
| |
|
| function MessageBox:setAllNamespaceCategories() | | function MessageBox:setAllNamespaceCategories() |
| | local cfg = self.cfg |
| | |
| -- Set categories for all namespaces. | | -- Set categories for all namespaces. |
| if self.invalidTypeError then | | if self.invalidTypeError then |
| local allSort = (self.title.namespace == 0 and 'Main:' or '') .. self.title.prefixedText | | local allSort = (self.title.namespace == 0 and 'Main:' or '') .. self.title.prefixedText |
| self:addCat('all', 'Wikipedia message box parameter needs fixing', allSort) | | self:addCat('all', 'Message box parameter needs fixing', allSort) |
| end | | end |
| if self.isSubstituted then | | -- Deprecate license type |
| self:addCat('all', 'Pages with incorrectly substituted templates') | | if self.type == 'license' and cfg.deprecateLicense then |
| | self:addCat('all', 'Licensing templates based on Mbox') |
| end | | end |
| end | | end |
|
| |
|
| function MessageBox:setCategories() | | function MessageBox:setCategories() |
| if self.title.namespace == 0 then | | if self.title.namespace == 10 then |
| self:setMainspaceCategories()
| |
| elseif self.title.namespace == 10 then
| |
| self:setTemplateCategories() | | self:setTemplateCategories() |
| end | | end |
| Line 471: |
Line 277: |
| page = self.args.page | | page = self.args.page |
| } | | } |
| end
| |
|
| |
| function MessageBox:exportDiv()
| |
| local root = mw.html.create()
| |
|
| |
| -- Add the subst check error.
| |
| if self.isSubstituted and self.name then
| |
| root:tag('b')
| |
| :addClass('error')
| |
| :wikitext(string.format(
| |
| 'Template <code>%s[[Template:%s|%s]]%s</code> has been incorrectly substituted.',
| |
| mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}')
| |
| ))
| |
| end
| |
|
| |
| local frame = mw.getCurrentFrame()
| |
| root:wikitext(frame:extensionTag{
| |
| name = 'templatestyles',
| |
| args = { src = self.base_templatestyles },
| |
| })
| |
| -- Add support for a single custom templatestyles sheet. Undocumented as
| |
| -- need should be limited and many templates using mbox are substed; we
| |
| -- don't want to spread templatestyles sheets around to arbitrary places
| |
| if self.templatestyles then
| |
| root:wikitext(frame:extensionTag{
| |
| name = 'templatestyles',
| |
| args = { src = self.templatestyles },
| |
| })
| |
| end
| |
|
| |
| -- Create the box.
| |
| local mbox = root:tag('div')
| |
| mbox:attr('id', self.id or nil)
| |
| for i, class in ipairs(self.classes or {}) do
| |
| mbox:addClass(class or nil)
| |
| end
| |
| mbox
| |
| :cssText(self.style or nil)
| |
|
| |
| if self.attrs then
| |
| mbox:attr(self.attrs)
| |
| end
| |
|
| |
| local flex_container
| |
| if self.below then
| |
| -- we need to wrap the flex components (`image(right)` and `text`) in their
| |
| -- own container div to support the `below` parameter
| |
| flex_container = mw.html.create('div')
| |
| flex_container:addClass('mbox-flex')
| |
| else
| |
| -- the mbox itself is the parent, so we need no HTML flex_container
| |
| flex_container = mw.html.create()
| |
| end
| |
|
| |
| -- Add the left-hand image.
| |
| if self.imageLeft then
| |
| local imageLeftCell = flex_container:tag('div'):addClass('mbox-image')
| |
| imageLeftCell
| |
| :addClass(self.imageLeftClass)
| |
| :wikitext(self.imageLeft or nil)
| |
| end
| |
|
| |
| -- Add the text.
| |
| local textCell = flex_container:tag('div'):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) then
| |
| textCellDiv:tag('span')
| |
| :addClass('hide-when-compact')
| |
| :wikitext(self.talk and (' ' .. self.talk) or nil)
| |
| :wikitext(self.fix and (' ' .. self.fix) or nil)
| |
| end
| |
| textCellDiv:wikitext(self.date and (' ' .. self.date) or nil)
| |
| if self.info and not self.isSmall then
| |
| textCellDiv
| |
| :tag('span')
| |
| :addClass('hide-when-compact')
| |
| :wikitext(self.info and (' ' .. self.info) or nil)
| |
| end
| |
| if self.removalNotice then
| |
| textCellDiv:tag('span')
| |
| :addClass('hide-when-compact')
| |
| :tag('i')
| |
| :wikitext(string.format(" (%s)", self.removalNotice))
| |
| end
| |
| else
| |
| -- 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 then
| |
| local imageRightCell = flex_container:tag('div'):addClass('mbox-imageright')
| |
| imageRightCell
| |
| :addClass(self.imageRightClass)
| |
| :wikitext(self.imageRight or nil)
| |
| end
| |
|
| |
| mbox:node(flex_container)
| |
|
| |
| -- Add the below row.
| |
| if self.below then
| |
| mbox:tag('div')
| |
| :addClass('mbox-text mbox-below')
| |
| :cssText(self.textstyle or nil)
| |
| :wikitext(self.below or nil)
| |
| end
| |
|
| |
| -- Add error message for invalid type parameters.
| |
| if self.invalidTypeError then
| |
| root:tag('div')
| |
| :addClass('mbox-invalid-type')
| |
| :wikitext(string.format(
| |
| 'This message box is using an invalid "type=%s" parameter and needs fixing.',
| |
| self.type or ''
| |
| ))
| |
| end
| |
|
| |
| -- Add categories.
| |
| root:wikitext(self:renderCategories() or nil)
| |
|
| |
| return tostring(root)
| |
| end | | end |
|
| |
|
| function MessageBox:export() | | function MessageBox:export() |
|
| |
| local root = mw.html.create() | | local root = mw.html.create() |
|
| |
| -- Add the subst check error.
| |
| if self.isSubstituted and self.name then
| |
| root:tag('b')
| |
| :addClass('error')
| |
| :wikitext(string.format(
| |
| 'Template <code>%s[[Template:%s|%s]]%s</code> has been incorrectly substituted.',
| |
| mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}')
| |
| ))
| |
| end
| |
|
| |
| local frame = mw.getCurrentFrame() | | local frame = mw.getCurrentFrame() |
| root:wikitext(frame:extensionTag{ | | root:wikitext(frame:extensionTag{ |
| Line 641: |
Line 305: |
| :cssText(self.style or nil) | | :cssText(self.style or nil) |
| :attr('role', 'presentation') | | :attr('role', 'presentation') |
| | | if self.width then |
| | boxTable:cssText('width: ' .. self.width) |
| | end |
| | if self.margin then |
| | boxTable:cssText('margin: 2px ' .. self.margin) |
| | end |
| if self.attrs then | | if self.attrs then |
| boxTable:attr(self.attrs) | | boxTable:attr(self.attrs) |
| Line 657: |
Line 326: |
| imageLeftCell = imageLeftCell:tag('div'):addClass('mbox-image-div') | | imageLeftCell = imageLeftCell:tag('div'):addClass('mbox-image-div') |
| end | | end |
| imageLeftCell | | imageLeftCell:wikitext(self.imageLeft or nil) |
| :addClass(self.imageLeftClass)
| |
| :wikitext(self.imageLeft or nil)
| |
| elseif self.imageEmptyCell then | | elseif self.imageEmptyCell then |
| -- Some message boxes define an empty cell if no image is specified, and | | -- Some message boxes define an empty cell if no image is specified, and |
| Line 714: |
Line 381: |
| end | | end |
| imageRightCell | | imageRightCell |
| :addClass(self.imageRightClass)
| |
| :wikitext(self.imageRight or nil) | | :wikitext(self.imageRight or nil) |
| end | | end |
| Line 732: |
Line 398: |
| root:tag('div') | | root:tag('div') |
| :addClass('mbox-invalid-type') | | :addClass('mbox-invalid-type') |
| :wikitext(string.format( | | :wikitext(errormsg(TNT.formatInLanguage(int_lang, 'I18n/Message box', 'err-type', self.type or ''))) |
| 'This message box is using an invalid "type=%s" parameter and needs fixing.',
| |
| self.type or ''
| |
| ))
| |
| end | | end |
|
| |
|
| Line 761: |
Line 424: |
| box:setParameters() | | box:setParameters() |
| box:setCategories() | | box:setCategories() |
| -- DIV MIGRATION CONDITIONAL
| |
| if box.cfg.div_structure then
| |
| return box:exportDiv()
| |
| end
| |
| -- END DIV MIGRATION CONDITIONAL
| |
| return box:export() | | return box:export() |
| end | | end |