Модуль:Message box: различия между версиями
Admin (обсуждение | вклад) м (1 версия импортирована) |
Admin (обсуждение | вклад) м (1 версия импортирована) |
||
(не показаны 2 промежуточные версии 2 участников) | |||
Строка 1: | Строка 1: | ||
-- This is a meta-module for producing message box templates, including | |||
-- {{mbox}}, {{ambox}}, {{imbox}}, {{tmbox}}, {{ombox}}, {{cmbox}} and {{fmbox}}. | |||
-- Load necessary modules. | |||
require('strict') | require('strict') | ||
local getArgs | local getArgs | ||
local categoryHandler = require('Module:Category handler')._main | |||
local yesno = require('Module:Yesno') | local yesno = require('Module:Yesno') | ||
local boxDate = require('Module:Calendar').bxDate; | |||
-- Get a language object for formatDate and ucfirst. | |||
local lang = mw.language.getContentLanguage() | local lang = mw.language.getContentLanguage() | ||
-- Define constants | |||
local CONFIG_MODULE = 'Module:Message box/configuration' | local CONFIG_MODULE = 'Module:Message box/configuration' | ||
-------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
Строка 47: | Строка 55: | ||
table.sort(nums) | table.sort(nums) | ||
return nums | return nums | ||
end | |||
-- локальная обёртка, игнорирует таблицу с номерами дня, месяца и года | |||
local function formatDate(txtDateIn, strFormat, params) | |||
local txtDateOut, date, status = boxDate(txtDateIn, strFormat, params) | |||
if status.brk then | |||
return error(status.errorText) | |||
else | |||
return txtDateOut | |||
end | |||
end | end | ||
Строка 68: | Строка 86: | ||
local ns = obj.title.namespace | local ns = obj.title.namespace | ||
-- boxType is "mbox" or invalid input | -- boxType is "mbox" or invalid input | ||
if | if ns == 0 then | ||
obj.cfg = cfg.ambox -- main namespace | obj.cfg = cfg.ambox -- main namespace | ||
elseif ns == 6 then | elseif ns == 6 then | ||
Строка 115: | Строка 120: | ||
obj.categories = {} | obj.categories = {} | ||
obj.classes = {} | obj.classes = {} | ||
return setmetatable(obj, MessageBox) | return setmetatable(obj, MessageBox) | ||
Строка 126: | Строка 129: | ||
end | end | ||
if sort then | if sort then | ||
cat = string.format('[[ | cat = string.format('[[Категория:%s|%s]]', cat, sort) | ||
else | else | ||
cat = string.format('[[ | cat = string.format('[[Категория:%s]]', cat) | ||
end | end | ||
self.categories[ns] = self.categories[ns] or {} | self.categories[ns] = self.categories[ns] or {} | ||
table.insert(self.categories[ns], cat) | table.insert(self.categories[ns], cat) | ||
Строка 169: | Строка 171: | ||
self.name = args.name | self.name = args.name | ||
if self.name then | if self.name then | ||
self:addClass(' | self:addClass('mbox-' .. string.gsub(self.name,' ','_')) | ||
end | end | ||
if yesno(args.plainlinks) ~= false then | if yesno(args.plainlinks) ~= false then | ||
Строка 185: | Строка 187: | ||
self.attrs = args.attrs | self.attrs = args.attrs | ||
self.dataLabel1 = args['data-label-1'] | |||
self.dataLabel2 = args['data-label-2'] | |||
self.dataLabel3 = args['data-label-3'] | |||
self.dataValue1 = args['data-value-1'] | |||
self.dataValue2 = args['data-value-2'] | |||
self.dataValue3 = args['data-value-3'] | |||
-- Set text style. | -- Set text style. | ||
self.textstyle = args.textstyle | self.textstyle = args.textstyle | ||
Строка 196: | Строка 205: | ||
and cfg.templateCategoryRequireName | and cfg.templateCategoryRequireName | ||
then | then | ||
self.name = args.name | |||
if self.name then | if self.name then | ||
local templateName = mw.ustring.match( | local templateName = mw.ustring.match( | ||
Строка 201: | Строка 211: | ||
'^[tT][eE][mM][pP][lL][aA][tT][eE][%s_]*:[%s_]*(.*)$' | '^[tT][eE][mM][pP][lL][aA][tT][eE][%s_]*:[%s_]*(.*)$' | ||
) or self.name | ) or self.name | ||
templateName = 'Template:' .. templateName | templateName = ('Template:' .. templateName) or ('Шаблон:' .. templateName) | ||
self.templateTitle = getTitleObject(templateName) | self.templateTitle = getTitleObject(templateName) | ||
end | end | ||
Строка 207: | Строка 217: | ||
and mw.title.equals(self.title, self.templateTitle) | and mw.title.equals(self.title, self.templateTitle) | ||
end | end | ||
-- Process data for collapsible text fields. At the moment these are only | -- Process data for collapsible text fields. At the moment these are only | ||
-- used in {{ambox}}. | -- used in {{ambox}}. | ||
Строка 215: | Строка 225: | ||
self.issue = args.smalltext | self.issue = args.smalltext | ||
else | else | ||
if args.sect and args.sect ~= '' or nil then | |||
local issue_sect = args.issue_sect | |||
issue_sect = type(issue_sect) == 'string' and issue_sect ~= '' and issue_sect or nil | |||
local text_sect = args.text_sect | |||
text_sect = type(text_sect) == 'string' and text_sect ~= '' and text_sect or nil | |||
local issues = {} | |||
table.insert(issues, issue_sect) | |||
table.insert(issues, text_sect) | |||
self.issue = table.concat(issues, ' ') | |||
else | |||
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, issue) | |||
table.insert(issues, text) | |||
self.issue = table.concat(issues, ' ') | |||
end | end | ||
end | end | ||
Строка 261: | Строка 275: | ||
end | end | ||
if talkTitle and talkTitle.exists then | if talkTitle and talkTitle.exists then | ||
local talkText = 'Соответствующую дискуссию можно найти на' | |||
if talkArgIsTalkPage then | |||
talkText = string.format( | |||
'%s [[%s|%s]].', | |||
talkText, | |||
talk, | |||
talkTitle.prefixedText | |||
) | |||
else | |||
talkText = string.format( | |||
'%s [[%s#%s|странице обсуждения]].', | |||
talkText, | |||
talkTitle.prefixedText, | |||
talk | |||
) | |||
end | |||
self.talk = talkText | self.talk = talkText | ||
end | end | ||
Строка 291: | Строка 299: | ||
local date | local date | ||
if args.date and args.date ~= '' then | if args.date and args.date ~= '' then | ||
date = args.date | local status, result = pcall(formatDate, args.date) | ||
if status then | |||
date = string.format("(<span class='date'>%s</span>)", result) | |||
else | |||
date = string.format("<span class='error'>(Строка «%s» не является верной датой, пожалуйста, укажите дату в формате <code>ГГГГ-ММ-ДД</code>)</span>", args.date) | |||
end | |||
elseif args.date == '' and self.isTemplatePage then | elseif args.date == '' and self.isTemplatePage then | ||
date = lang:formatDate(' | date = string.format("(<span class='date'>%s</span>)", formatDate( lang:formatDate('Y-m-d') ) ) -- тут возникновения ошибки, связанной с пользовательским вводом, не будет | ||
end | end | ||
if date then | if date then | ||
self.date = string.format(" <span class='date | self.date = string.format(" <span class='mbox-date'>''%s''</span>", date) | ||
end | end | ||
self.info = args.info | self.info = args.info | ||
if yesno(args.removalnotice) then | if yesno(args.removalnotice) then | ||
self.removalNotice = cfg.removalNotice | self.removalNotice = cfg.removalNotice | ||
end | |||
if args.shortFix then | |||
self.shortFix = args.shortFix | |||
end | end | ||
end | end | ||
Строка 310: | Строка 326: | ||
else | else | ||
self.text = args.text | self.text = args.text | ||
self.textsmall = args['text-small'] | |||
end | end | ||
Строка 318: | Строка 335: | ||
self.imageCellDiv = not self.isSmall and cfg.imageCellDiv | self.imageCellDiv = not self.isSmall and cfg.imageCellDiv | ||
self.imageEmptyCell = cfg.imageEmptyCell | self.imageEmptyCell = cfg.imageEmptyCell | ||
if cfg.imageEmptyCellStyle then | |||
self.imageEmptyCellStyle = 'border:none;padding:0px;width:1px' | |||
end | |||
-- Left image settings. | -- Left image settings. | ||
local imageLeft = self.isSmall and args.smallimage or args.image | local imageLeft = self.isSmall and args.smallimage or args.image | ||
if cfg.imageCheckBlank and imageLeft ~= 'blank' and imageLeft ~= 'none' | if cfg.imageCheckBlank and imageLeft ~= 'blank' and imageLeft ~= 'none' and imageLeft ~= '' | ||
or not cfg.imageCheckBlank and imageLeft ~= 'none' | or not cfg.imageCheckBlank and imageLeft ~= 'none' | ||
then | then | ||
Строка 328: | Строка 348: | ||
local imageSize = self.isSmall | local imageSize = self.isSmall | ||
and (cfg.imageSmallSize or '30x30px') | and (cfg.imageSmallSize or '30x30px') | ||
or cfg.imageSize | |||
or '40x40px' | or '40x40px' | ||
self.imageLeft = string.format('[[File:%s|%s | self.imageLeft = string.format('[[File:%s|%s|alt=]]', self.typeImage | ||
or ' | or 'Information icon4.svg', imageSize) | ||
end | end | ||
end | end | ||
Строка 339: | Строка 360: | ||
self.imageRight = imageRight | self.imageRight = imageRight | ||
end | end | ||
end | end | ||
Строка 348: | Строка 365: | ||
local args = self.args | local args = self.args | ||
local cfg = self.cfg | local cfg = self.cfg | ||
local date = nil | |||
if not cfg.allowMainspaceCategories then | if not cfg.allowMainspaceCategories then | ||
Строка 360: | Строка 378: | ||
-- The following is roughly equivalent to the old {{Ambox/category}}. | -- The following is roughly equivalent to the old {{Ambox/category}}. | ||
local | local status, result = pcall(formatDate, args.date, 'xg Y') | ||
if status then | |||
date = result | |||
end | |||
date = type(date) == 'string' and date | date = type(date) == 'string' and date | ||
local preposition = ' | local preposition = 'с' | ||
local suffix = 'года' | |||
for _, num in ipairs(nums) do | for _, num in ipairs(nums) do | ||
local mainCat = args['cat' .. tostring(num)] | local mainCat = args['cat' .. tostring(num)] | ||
Строка 370: | Строка 392: | ||
allCat = type(allCat) == 'string' and allCat | allCat = type(allCat) == 'string' and allCat | ||
if mainCat and date and date ~= '' then | if mainCat and date and date ~= '' then | ||
local catTitle = string.format('%s %s %s', mainCat, preposition, date) | local catTitle = string.format('%s %s %s %s', mainCat, preposition, date, suffix) | ||
self:addCat(0, catTitle) | self:addCat(0, catTitle) | ||
catTitle = getTitleObject(' | catTitle = getTitleObject('Категория:' .. catTitle) | ||
if not | local status, result = pcall(formatDate, args.date) | ||
self:addCat(0, ' | if not status then | ||
end | self:addCat(0, 'Википедия:Статьи с недопустимым параметром даты в шаблоне-сообщении') | ||
end | |||
elseif mainCat and (not date or date == '') then | elseif mainCat and (not date or date == '') then | ||
self:addCat(0, mainCat) | self:addCat(0, mainCat) | ||
Строка 431: | Строка 454: | ||
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', ' | self:addCat('all', 'Википедия:Необходимо исправить параметр в шаблоне-сообщении', allSort) | ||
end | end | ||
if self.isSubstituted then | if self.isSubstituted then | ||
self:addCat('all', ' | self:addCat('all', 'Википедия:Страницы с ошибочно подставленными шаблонами') | ||
end | |||
if self.isSmall then | |||
self:addCat(0, 'Википедия:Страницы с малыми шаблонами-сообщениями') | |||
end | end | ||
end | end | ||
Строка 448: | Строка 474: | ||
function MessageBox:renderCategories() | function MessageBox:renderCategories() | ||
-- Convert category tables to strings and pass them through | -- Convert category tables to strings and pass them through | ||
-- [[Module:Category handler]]. | -- [[Module:Category handler]]. | ||
return | return categoryHandler{ | ||
main = table.concat(self.categories[0] or {}), | main = table.concat(self.categories[0] or {}), | ||
template = table.concat(self.categories[10] or {}), | template = table.concat(self.categories[10] or {}), | ||
Строка 473: | Строка 493: | ||
:addClass('error') | :addClass('error') | ||
:wikitext(string.format( | :wikitext(string.format( | ||
' | 'Шаблон <code>%s[[Шаблон:%s|%s]]%s</code> был неккоректно подставлен.', | ||
mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}') | mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}') | ||
)) | )) | ||
end | end | ||
Строка 502: | Строка 507: | ||
:cssText(self.style or nil) | :cssText(self.style or nil) | ||
:attr('role', 'presentation') | :attr('role', 'presentation') | ||
if self.dataLabel1 then | |||
boxTable:attr('data-' .. self.dataLabel1, self.dataValue1) | |||
end | |||
if self.dataLabel2 then | |||
boxTable:attr('data-' .. self.dataLabel2, self.dataValue2) | |||
end | |||
if self.dataLabel3 then | |||
boxTable:attr('data-' .. self.dataLabel3, self.dataValue3) | |||
end | |||
if self.attrs then | if self.attrs then | ||
boxTable:attr(self.attrs) | boxTable:attr(self.attrs) | ||
Строка 516: | Строка 531: | ||
-- image width to 52px. If any images in a div are wider than that, | -- 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. | -- they may overlap with the text or cause other display problems. | ||
imageLeftCell = imageLeftCell:tag('div'): | imageLeftCell = imageLeftCell:tag('div'):css('width', '52px') | ||
end | end | ||
imageLeftCell:wikitext(self.imageLeft or nil) | imageLeftCell:wikitext(self.imageLeft or nil) | ||
Строка 526: | Строка 541: | ||
row:tag('td') | row:tag('td') | ||
:addClass('mbox-empty-cell') | :addClass('mbox-empty-cell') | ||
:cssText(self.imageEmptyCellStyle or nil) | |||
end | end | ||
Строка 536: | Строка 552: | ||
local textCellDiv = textCell:tag('div') | local textCellDiv = textCell:tag('div') | ||
textCellDiv | textCellDiv | ||
:addClass('mbox-text- | :addClass('mbox-text-div') | ||
:wikitext(self.issue or nil) | :wikitext(self.issue or nil) | ||
if (self.talk or self.fix) then | local textsmallCellDiv = textCell:tag('div') | ||
textsmallCellDiv | |||
:addClass('mbox-textsmall-div hide-when-compact') | |||
:cssText(self.textsmallstyle or 'font-size:85%') | |||
:wikitext(self.textsmall or nil) | |||
if (self.talk or self.fix) and not self.isSmall then | |||
textsmallCellDiv:tag('span') | |||
:addClass('hide-when-compact') | :addClass('hide-when-compact') | ||
:wikitext(self.fix and (' ' .. self.fix) or nil) | |||
:wikitext(self.talk and (' ' .. self.talk) or nil) | :wikitext(self.talk and (' ' .. self.talk) or nil) | ||
end | end | ||
textCellDiv:wikitext(self.date and (' ' .. self.date) or nil) | if self.textsmall or self.fix or self.talk then | ||
textsmallCellDiv:wikitext(self.date and (' ' .. self.date) or nil) | |||
else | |||
textCellDiv:wikitext(self.date and (' ' .. self.date) or nil) | |||
end | |||
if self.info and not self.isSmall then | if self.info and not self.isSmall then | ||
textsmallCellDiv | |||
:tag('span') | :tag('span') | ||
:addClass('hide-when-compact') | :addClass('hide-when-compact') | ||
Строка 552: | Строка 577: | ||
end | end | ||
if self.removalNotice then | if self.removalNotice then | ||
textsmallCellDiv:tag('small') | |||
:addClass('hide-when-compact') | :addClass('hide-when-compact') | ||
:tag('i') | :tag('i') | ||
:wikitext(string.format(" (%s)", self.removalNotice)) | :wikitext(string.format(" (%s)", self.removalNotice)) | ||
end | |||
if self.shortFix then | |||
textCell:tag('div') | |||
:addClass('mbox-multiply') | |||
:tag('span') | |||
:wikitext(string.format("%s", self.shortFix)) | |||
:tag('span') | |||
:wikitext(self.date and (' ' .. self.date) or nil) | |||
end | end | ||
else | else | ||
Строка 570: | Строка 603: | ||
-- If we are using a div, redefine imageRightCell so that the image | -- If we are using a div, redefine imageRightCell so that the image | ||
-- is inside it. | -- is inside it. | ||
imageRightCell = imageRightCell:tag('div'): | imageRightCell = imageRightCell:tag('div'):css('width', '52px') | ||
end | end | ||
imageRightCell | imageRightCell | ||
Строка 589: | Строка 622: | ||
if self.invalidTypeError then | if self.invalidTypeError then | ||
root:tag('div') | root:tag('div') | ||
: | :css('text-align', 'center') | ||
:wikitext(string.format( | :wikitext(string.format( | ||
' | 'Этот шаблон-сообщение использует неверный параметр "type=%s", необходимо исправить.', | ||
self.type or '' | self.type or '' | ||
)) | )) |
Текущая версия от 17:37, 23 июня 2023
Для документации этого модуля может быть создана страница Модуль:Message box/doc
-- This is a meta-module for producing message box templates, including -- {{mbox}}, {{ambox}}, {{imbox}}, {{tmbox}}, {{ombox}}, {{cmbox}} and {{fmbox}}. -- Load necessary modules. require('strict') local getArgs local categoryHandler = require('Module:Category handler')._main local yesno = require('Module:Yesno') local boxDate = require('Module:Calendar').bxDate; -- Get a language object for formatDate and ucfirst. local lang = mw.language.getContentLanguage() -- Define constants local CONFIG_MODULE = 'Module:Message box/configuration' -------------------------------------------------------------------------------- -- Helper functions -------------------------------------------------------------------------------- local 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 then return title 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 local function getArgNums(args, prefix) local nums = {} for k, v in pairs(args) do local num = mw.ustring.match(tostring(k), '^' .. prefix .. '([1-9]%d*)$') if num then table.insert(nums, tonumber(num)) end end table.sort(nums) return nums end -- локальная обёртка, игнорирует таблицу с номерами дня, месяца и года local function formatDate(txtDateIn, strFormat, params) local txtDateOut, date, status = boxDate(txtDateIn, strFormat, params) if status.brk then return error(status.errorText) else return txtDateOut end end -------------------------------------------------------------------------------- -- Box class definition -------------------------------------------------------------------------------- local MessageBox = {} MessageBox.__index = MessageBox function 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 then local ns = obj.title.namespace -- boxType is "mbox" or invalid input if ns == 0 then obj.cfg = cfg.ambox -- main namespace elseif ns == 6 then obj.cfg = cfg.imbox -- file namespace elseif ns == 14 then obj.cfg = cfg.cmbox -- category namespace else local nsTable = mw.site.namespaces[ns] if nsTable and nsTable.isTalk then obj.cfg = cfg.tmbox -- any talk namespace else obj.cfg = cfg.ombox -- other namespaces or invalid input end end end -- Set the arguments, and remove all blank arguments except for the ones -- listed in cfg.allowBlankParams. do local newArgs = {} for k, v in pairs(args) do if v ~= '' then newArgs[k] = v end end for i, param in ipairs(obj.cfg.allowBlankParams or {}) do newArgs[param] = args[param] end obj.args = newArgs end -- Define internal data structure. obj.categories = {} obj.classes = {} return setmetatable(obj, MessageBox) end function MessageBox:addCat(ns, cat, sort) if not cat then return nil end if sort then cat = string.format('[[Категория:%s|%s]]', cat, sort) else cat = string.format('[[Категория:%s]]', cat) end self.categories[ns] = self.categories[ns] or {} table.insert(self.categories[ns], cat) end function MessageBox:addClass(class) if not class then return nil end table.insert(self.classes, class) end function MessageBox:setParameters() local args = self.args local cfg = self.cfg -- Get type data. self.type = args.type local typeData = cfg.types[self.type] self.invalidTypeError = cfg.showInvalidTypeError and self.type and not typeData typeData = typeData or cfg.types[cfg.default] self.typeClass = typeData.class self.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.smallParam or not cfg.smallParam and yesno(args.small) ) -- Add attributes, classes and styles. self.id = args.id self.name = args.name if self.name then self:addClass('mbox-' .. string.gsub(self.name,' ','_')) end if yesno(args.plainlinks) ~= false then self:addClass('plainlinks') end for _, class in ipairs(cfg.classes or {}) do self:addClass(class) end if self.isSmall then self:addClass(cfg.smallClass or 'mbox-small') end self:addClass(self.typeClass) self:addClass(args.class) self.style = args.style self.attrs = args.attrs self.dataLabel1 = args['data-label-1'] self.dataLabel2 = args['data-label-2'] self.dataLabel3 = args['data-label-3'] self.dataValue1 = args['data-value-1'] self.dataValue2 = args['data-value-2'] self.dataValue3 = args['data-value-3'] -- 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.useCollapsibleTextFields if self.useCollapsibleTextFields or cfg.templateCategory and cfg.templateCategoryRequireName then self.name = args.name 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) or ('Шаблон:' .. 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 -- used in {{ambox}}. if self.useCollapsibleTextFields then -- Get the self.issue value. if self.isSmall and args.smalltext then self.issue = args.smalltext else if args.sect and args.sect ~= '' or nil then local issue_sect = args.issue_sect issue_sect = type(issue_sect) == 'string' and issue_sect ~= '' and issue_sect or nil local text_sect = args.text_sect text_sect = type(text_sect) == 'string' and text_sect ~= '' and text_sect or nil local issues = {} table.insert(issues, issue_sect) table.insert(issues, text_sect) self.issue = table.concat(issues, ' ') else 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, issue) table.insert(issues, text) self.issue = table.concat(issues, ' ') end 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 talkArgIsTalkPage then talkText = string.format( '%s [[%s|%s]].', talkText, talk, talkTitle.prefixedText ) else talkText = string.format( '%s [[%s#%s|странице обсуждения]].', talkText, talkTitle.prefixedText, talk ) 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 local status, result = pcall(formatDate, args.date) if status then date = string.format("(<span class='date'>%s</span>)", result) else date = string.format("<span class='error'>(Строка «%s» не является верной датой, пожалуйста, укажите дату в формате <code>ГГГГ-ММ-ДД</code>)</span>", args.date) end elseif args.date == '' and self.isTemplatePage then date = string.format("(<span class='date'>%s</span>)", formatDate( lang:formatDate('Y-m-d') ) ) -- тут возникновения ошибки, связанной с пользовательским вводом, не будет end if date then self.date = string.format(" <span class='mbox-date'>''%s''</span>", date) end self.info = args.info if yesno(args.removalnotice) then self.removalNotice = cfg.removalNotice end if args.shortFix then self.shortFix = args.shortFix end end -- 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 then self.text = args.smalltext or args.text else self.text = args.text self.textsmall = args['text-small'] end -- Set the below row. self.below = cfg.below and args.below -- General image settings. self.imageCellDiv = not self.isSmall and cfg.imageCellDiv self.imageEmptyCell = cfg.imageEmptyCell if cfg.imageEmptyCellStyle then self.imageEmptyCellStyle = 'border:none;padding:0px;width:1px' end -- Left image settings. local imageLeft = self.isSmall and args.smallimage or args.image if cfg.imageCheckBlank and imageLeft ~= 'blank' and imageLeft ~= 'none' and imageLeft ~= '' or not cfg.imageCheckBlank and imageLeft ~= 'none' then self.imageLeft = imageLeft if not imageLeft then local imageSize = self.isSmall and (cfg.imageSmallSize or '30x30px') or cfg.imageSize or '40x40px' self.imageLeft = string.format('[[File:%s|%s|alt=]]', self.typeImage or 'Information icon4.svg', imageSize) end end -- Right image settings. local imageRight = self.isSmall and args.smallimageright or args.imageright if not (cfg.imageRightNone and imageRight == 'none') then self.imageRight = imageRight end end function MessageBox:setMainspaceCategories() local args = self.args local cfg = self.cfg local date = nil 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 status, result = pcall(formatDate, args.date, 'xg Y') if status then date = result end date = type(date) == 'string' and date local preposition = 'с' local suffix = 'года' 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 %s', mainCat, preposition, date, suffix) self:addCat(0, catTitle) catTitle = getTitleObject('Категория:' .. catTitle) local status, result = pcall(formatDate, args.date) if not status then self:addCat(0, 'Википедия:Статьи с недопустимым параметром даты в шаблоне-сообщении') end elseif mainCat and (not date or date == '') then self:addCat(0, mainCat) end if allCat then self:addCat(0, allCat) end end end function MessageBox:setTemplateCategories() local args = self.args local cfg = self.cfg -- Add template categories. if cfg.templateCategory then if cfg.templateCategoryRequireName then if self.isTemplatePage then self:addCat(10, cfg.templateCategory) end elseif not self.title.isSubpage then self:addCat(10, cfg.templateCategory) 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 function MessageBox:setAllNamespaceCategories() -- Set categories for all namespaces. if self.invalidTypeError then local allSort = (self.title.namespace == 0 and 'Main:' or '') .. self.title.prefixedText self:addCat('all', 'Википедия:Необходимо исправить параметр в шаблоне-сообщении', allSort) end if self.isSubstituted then self:addCat('all', 'Википедия:Страницы с ошибочно подставленными шаблонами') end if self.isSmall then self:addCat(0, 'Википедия:Страницы с малыми шаблонами-сообщениями') end end function MessageBox:setCategories() if self.title.namespace == 0 then self:setMainspaceCategories() elseif self.title.namespace == 10 then self:setTemplateCategories() end self:setAllNamespaceCategories() end function MessageBox:renderCategories() -- Convert category tables to strings and pass them through -- [[Module:Category handler]]. return categoryHandler{ 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 } end function MessageBox:export() 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( 'Шаблон <code>%s[[Шаблон:%s|%s]]%s</code> был неккоректно подставлен.', 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 {}) do boxTable:addClass(class or nil) end boxTable :cssText(self.style or nil) :attr('role', 'presentation') if self.dataLabel1 then boxTable:attr('data-' .. self.dataLabel1, self.dataValue1) end if self.dataLabel2 then boxTable:attr('data-' .. self.dataLabel2, self.dataValue2) end if self.dataLabel3 then boxTable:attr('data-' .. self.dataLabel3, self.dataValue3) end if self.attrs then boxTable:attr(self.attrs) end -- Add the left-hand image. local row = boxTable:tag('tr') if self.imageLeft then local 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') end imageLeftCell: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-div') :wikitext(self.issue or nil) local textsmallCellDiv = textCell:tag('div') textsmallCellDiv :addClass('mbox-textsmall-div hide-when-compact') :cssText(self.textsmallstyle or 'font-size:85%') :wikitext(self.textsmall or nil) if (self.talk or self.fix) and not self.isSmall then textsmallCellDiv:tag('span') :addClass('hide-when-compact') :wikitext(self.fix and (' ' .. self.fix) or nil) :wikitext(self.talk and (' ' .. self.talk) or nil) end if self.textsmall or self.fix or self.talk then textsmallCellDiv:wikitext(self.date and (' ' .. self.date) or nil) else textCellDiv:wikitext(self.date and (' ' .. self.date) or nil) end if self.info and not self.isSmall then textsmallCellDiv :tag('span') :addClass('hide-when-compact') :wikitext(self.info and (' ' .. self.info) or nil) end if self.removalNotice then textsmallCellDiv:tag('small') :addClass('hide-when-compact') :tag('i') :wikitext(string.format(" (%s)", self.removalNotice)) end if self.shortFix then textCell:tag('div') :addClass('mbox-multiply') :tag('span') :wikitext(string.format("%s", self.shortFix)) :tag('span') :wikitext(self.date and (' ' .. self.date) or nil) 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 = 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') end imageRightCell :wikitext(self.imageRight or nil) end -- Add the below row. if self.below then boxTable: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 then root:tag('div') :css('text-align', 'center') :wikitext(string.format( 'Этот шаблон-сообщение использует неверный параметр "type=%s", необходимо исправить.', 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 } end function p.main(boxType, args, cfgTables) local box = MessageBox.new(boxType, args, cfgTables or mw.loadData(CONFIG_MODULE)) box:setParameters() box:setCategories() return box:export() end function mt.__index(t, k) return function (frame) if not getArgs then getArgs = require('Module:Arguments').getArgs end return t.main(k, getArgs(frame, {trim = false, removeBlanks = false})) end end return setmetatable(p, mt)