Module:WikiProject banner
Kaonekelo
Documentation for this module may be created at Module:WikiProject banner/doc
require('strict')
local p = {}
local sandbox --= '/sandbox'
local cfg = mw.loadData('Module:WikiProject banner/config' .. (sandbox or ''))
local mbox = require('Module:Message box').main
local yesno = require('Module:Yesno')
local frame = mw.getCurrentFrame()
local lang = mw.getLanguage(cfg.language)
local current_title = mw.title.getCurrentTitle()
local parameter_format = function(parameter, value)
return frame:expandTemplate{title='para', args={parameter, value or ''}}
end
local wikilink = function(link, display)
if link then
return display and '[['..link..'|'..display..']]' or '[['..link..']]'
else
return display or ''
end
end
local image = function(args)
return string.format(
'[[File:%s%s%s%s%s]]',
args.image,
(args.size and args.size ~= '') and '|' .. args.size or '',
(args.location and args.location ~= '') and '|' .. args.location or '',
(args.alt and args.alt ~= '') and '|alt=' .. args.alt or '',
(args.link and args.link ~= '') and '|link=' .. args.link or ''
)
end
local if_exists = function(target, fallback) -- function to add wikilink if target exists
local title = mw.title.new(target)
if title and title.exists then
return wikilink(target)
else
return fallback or target
end
end
local isarticle = function(class)
local article = true
for _,v in ipairs(cfg.quality.non_article_classes) do
if class==v then -- class matches one of the non-article classes
article = false
break
end
end
return article
end
p.readarticleclass = function(options, page) -- used by _main and also Module:Banner shell
page = page or current_title.prefixedText
local get_parameter_value = require('Module:Template parameter value').getValue
local success, result = get_parameter_value(page, cfg.WPBS_redirects, 'class', options)
return success and result
-- returns FALSE if banner shell template does not exist on page
-- returns BLANK if class parameter is not defined or is defined blank
-- otherwise returns class parameter
end
local importance_mask = function(raw_importance, class, scale, banner_name)
local importance
if scale=='inline' then -- pass importance without change
importance = raw_importance
elseif scale=='subpage' then
local custom_mask = banner_name:subPageTitle('importance')
if custom_mask.exists and #custom_mask:getContent()>1 then -- pass to custom importance mask
importance = frame:expandTemplate{
title=custom_mask.prefixedText,
args={importance=raw_importance or '¬', class=class}
}
end
else
importance = frame:expandTemplate{title='Template:Importance mask', args={raw_importance or '¬', class=class}}
end
if importance=='¬' then
importance = nil
end
return importance
end
local page_assessment = function(project, class, importance) -- add PageAssessments parser function
local assessment = table.concat({project, class or '', importance or ''},'|')
frame:preprocess('{{#assessment:' .. assessment .. '}}')
end
function p._main(args, raw_args, demo, banner_name)
---------------------------
-- Initialise parameters --
---------------------------
local project = args.PROJECT
local project_name = args.PROJECT_NAME or 'WikiProject ' .. project
local project_link = mw.title.new(args.PROJECT_LINK or 'Wikipedia:' .. project_name)
local pagetype = demo and 'article' or require('Module:Pagetype')._main({})
local rows, collapsed, nested_ratings, task_forces, notes, categories = {}, {}, {}, {}, {}, {}
local add_category = function(category, key)
if not demo then
local cat_link = wikilink('Category:' .. category, key)
table.insert(categories, cat_link)
end
end
local parse_text = function(text)
return text and string.gsub(text, '_PAGETYPE_', pagetype)
end
for arg_name, arg_value in pairs(args) do
local tf_match = mw.ustring.match(arg_name,'^tf (%d+)$')
if tf_match and yesno(arg_value) then
table.insert(task_forces, tf_match)
else
local note_match = mw.ustring.match(arg_name,'^note (%d+)$')
if note_match and yesno(arg_value) then
table.insert(notes, note_match)
end
end
end
table.sort(task_forces, function (x, y) return tonumber(x) < tonumber(y) end)
table.sort(notes, function (x, y) return tonumber(x) < tonumber(y) end)
local warning = ''
---------------------------
-- Location warning -------
---------------------------
local show_namespace_warning = not (current_title.isTalkPage or demo)
if show_namespace_warning then
local text = string.format(
cfg.namespace_warning.text .. '<br><span style="font-size:95%%">' .. cfg.namespace_warning.small .. '</span>',
pagetype,
current_title.talkPageTitle.fullText,
parameter_format('category', 'no')
)
local sortkey = current_title.namespace==10 and cfg.namespace_warning.sortkey_on_template_page or cfg.namespace_warning.sortkey
if current_title.namespace==10 then -- on the Template namespace
text = text .. string.format(
cfg.namespace_warning.on_template_page,
parameter_format('BANNER_NAME'),
current_title.prefixedText
)
end
warning = mbox('ombox', {
image = '[[File:' .. cfg.namespace_warning.image .. '|40px]]',
type = cfg.namespace_warning.type_,
text = text
})
if not current_title.subjectPageTitle:inNamespace(2) then
add_category(cfg.namespace_warning.categories, sortkey)
end
end
---------------------------
-- Substitution warning ---
---------------------------
if args.substcheck=='SUBST' then
local text = string.format(
cfg.subst_warning.text,
project_name,
'<code>{{'..banner_name.prefixedText..'}}</code>'
)
warning = warning .. mbox('ombox', {
image = '[[File:' .. cfg.subst_warning.image .. '|40px]]',
type = cfg.subst_warning.type_,
text = text,
}) .. cfg.subst_warning.categories
end
---------------------------
-- Primary image/text -----
---------------------------
local project_talk = wikilink(project_link.talkPageTitle.prefixedText,'the discussion')
local assessment_cat = args.ASSESSMENT_CAT or project .. ' articles'
local primary_image = function(image_name, image_size)
local cell = mw.html.create('td')
if image_name and image_name ~= '' then
cell:addClass('mbox-image wpb-image')
:wikitext(image({
image = image_name,
size = image_size,
alt = 'WikiProject icon'
}))
else
cell:addClass('mbox-empty-cell')
end
cell:done()
return cell
end
local mainarticle = args.MAIN_ARTICLE
local topic = mainarticle and if_exists(mainarticle) or if_exists(project, project .. ' articles')
local portal = args.PORTAL
local portal_box = portal and frame:expandTemplate{title='Portal', args={portal}} or ''
local main_text = portal_box .. (parse_text(args.MAIN_TEXT) or string.format(
cfg.maintext,
pagetype,
wikilink(project_link.prefixedText, project_name),
topic,
project_talk
))
local image_left_size = args.IMAGE_LEFT_SIZE or args.IMAGE_LEFT_LARGE or '80px'
local primary_row = mw.html.create('tr')
primary_row
:node(primary_image(args.IMAGE_LEFT, image_left_size))
:tag('td')
:addClass('mbox-text')
:wikitext(main_text)
:tag('span')
:addClass('metadata wpb-metadata')
:tag('span')
:addClass('wpb-project')
:wikitext(project)
:done()
:tag('span')
:addClass('wpb-project_link')
:wikitext(project_link.prefixedText)
:done()
:tag('span')
:addClass('wpb-banner_name')
:wikitext(banner_name.prefixedText)
:done()
:tag('span')
:addClass('wpb-assessment_cat')
:wikitext(assessment_cat)
:done()
:done()
:done()
:node(primary_image(
args.IMAGE_RIGHT,
args.IMAGE_RIGHT_SIZE or args.IMAGE_RIGHT_LARGE or '80px'
))
:done()
table.insert(rows, primary_row)
---------------------------
-- Quality assessment -----
---------------------------
local assessment_link = args.ASSESSMENT_LINK
if not assessment_link then
local fallback = mw.title.new(project_link.prefixedText .. '/Assessment')
assessment_link = fallback.exists and fallback.prefixedText
elseif assessment_link=='no' then
assessment_link = nil
end
local class_mask = require('Module:Class mask')._main
local class
if args.QUALITY_SCALE=='inline' then
class = raw_args.class
elseif args.QUALITY_SCALE=='subpage' then
local custom_mask = banner_name:subPageTitle('class')
if custom_mask.exists then
class = frame:expandTemplate{
title=custom_mask.prefixedText,
args=raw_args
}
end
else
args.FQS = args.QUALITY_SCALE=='extended' and 'yes' or 'no'
args[1] = raw_args.class
class = class_mask(args, current_title)
end
local show_quality = true
if class=='¬' then
class = nil
end
if class then -- banner gives quality ratings
if args.QUALITY_CRITERIA~='custom' then -- project uses standard scale and will inherit article class if needed
local article_class = p.readarticleclass({ignore_subtemplates=true}, current_title.prefixedText)
article_class = article_class and class_mask({article_class}, current_title)
if article_class then -- banner shell exists
if article_class=='' then -- no article class defined
if class=='' then -- local class also does not exist, check whether any other class parameters are defined inside the shell
local classparam = p.readarticleclass({ignore_blank=true, only_subtemplates=true}, current_title.prefixedText)
if classparam=='' then -- no class parameters defined, display as globally unassessed
show_quality = false -- hide quality class in project banner
end
end
elseif class=='' or class==article_class then -- local class matches article class or is blank
show_quality = false -- hide quality class in project banner
class = article_class
elseif (article_class=='NA') and not isarticle(class) then -- article class and local class are both non-article classes
show_quality = false
else -- article class exists and differs from local class
add_category('Articles with conflicting quality ratings')
end
end
end
if show_quality then -- quality rating shown in banner
local class_module = require('Module:Class')._class
local rating
if pagetype=='article' then
rating = class=='' and cfg.quality.not_yet or string.format(cfg.quality.rated, class)
else
rating = cfg.quality.not_required
end
local scale = args.QUALITY_CRITERIA=='custom' and assessment_link and string.format(
cfg.quality.project_scale,
wikilink(assessment_link..'#'..lang:ucfirst(cfg.quality.name), cfg.quality.name)
) or cfg.quality.default_scale
local quality_rating = string.format(
cfg.quality.rating,
pagetype,
rating,
scale
)
local class_row = mw.html.create('tr')
class_row
:node(class_module{class, category = assessment_cat})
:tag('td')
:addClass('mbox-text')
:attr('colspan', '2')
:wikitext(quality_rating)
:allDone()
table.insert(rows, class_row)
table.insert(nested_ratings, class..'-class')
end
add_category((class=='' and 'Unassessed' or class..'-Class') .. ' ' .. assessment_cat)
end
if args.HOOK_ASSESS then
table.insert(rows, args.HOOK_ASSESS)
end
---------------------------
-- Importance assessment --
---------------------------
local importance = importance_mask(raw_args.importance or raw_args.priority, class, args.IMPORTANCE_SCALE, banner_name)
local importance_name = args.IMPN or (raw_args.priority and 'priority' or cfg.importance.default_name)
if importance and importance~='NA' then -- display importance rating
local rating = importance=='Unknown' and cfg.importance.not_yet or string.format(cfg.importance.rated, importance, importance_name)
local scale_name = string.format(cfg.importance.scale, importance_name)
local scale = assessment_link and string.format(
cfg.importance.project_scale,
wikilink(assessment_link..'#'..lang:ucfirst(scale_name), scale_name)
) or cfg.importance.default_scale
local importance_rating = string.format(
cfg.importance.rating,
pagetype,
rating,
scale
)
local importance_row = mw.html.create('tr')
importance_row
:node(frame:expandTemplate{title='Importance', args={importance, category=assessment_cat, impn=importance_name}})
:tag('td')
:addClass('mbox-text')
:attr('colspan', '2')
:wikitext(importance_rating)
:allDone()
table.insert(rows, importance_row)
if importance~='Unknown' then -- importance is not NA or Unknown
table.insert(nested_ratings,importance .. '-' .. importance_name)
end
end
if importance then --add importance category
add_category((importance=='' and 'Unknown' or importance..'-' .. importance_name) .. ' ' .. assessment_cat)
page_assessment(project, class, importance)
end
if args.HOOK_IMPORTANCE then
table.insert(rows, args.HOOK_IMPORTANCE)
end
---------------------------
-- Task forces ------------
---------------------------
local nested_tf = {}
local tf_default_size = args.TF_SIZE or args.TF_1_SIZE or 'x25px'
for _, k in ipairs(task_forces) do
local tf_prefix = 'TF_' .. k .. '_'
local tf_assessment_cat = args[tf_prefix..'ASSESSMENT_CAT'] or (args[tf_prefix..'NAME'] or '')..' articles'
local tf_importance = raw_args['tf '..k..' importance'] and importance_mask(raw_args['tf '..k..' importance'], class, args.IMPORTANCE_SCALE, banner_name)
if args[tf_prefix .. 'TEXT']~='none' then
local text = ''
if args[tf_prefix..'PORTAL'] then
text = frame:expandTemplate{title='Portal', args={args[tf_prefix .. 'PORTAL'], height='15', margin='0'}}
end
if args[tf_prefix..'TEXT'] then
text = text .. parse_text(args[tf_prefix..'TEXT'])
else
text = text .. 'This ' .. pagetype .. ' is supported by <b>' .. wikilink(args[tf_prefix .. 'LINK'],args[tf_prefix .. 'NAME']) .. '</b>'
if tf_importance and tf_importance~='NA' and tf_importance~='Unknown' then
text = text .. ' (marked as ' .. wikilink(':Category:' .. tf_importance .. '-' .. importance_name .. ' ' .. tf_assessment_cat, tf_importance .. '-' .. importance_name) .. ')'
end
text = text .. '.'
end
local tf_size = args[tf_prefix..'SIZE'] or tf_default_size
local tf_image = ''
if args[tf_prefix..'IMAGE'] then
tf_image = image{
image = args[tf_prefix..'IMAGE'],
size = tf_size,
location = 'center',
alt = 'Taskforce icon',
}
end
local taskforce = mw.html.create('tr')
taskforce
:tag('td')
:wikitext(tf_image)
:done()
:tag('td')
:addClass('mbox-text')
:attr('colspan','2')
:wikitext(text)
:allDone()
table.insert(rows, taskforce)
end
if args[tf_prefix..'HOOK'] then
table.insert(rows, args[tf_prefix..'HOOK'])
end
if yesno(args[tf_prefix..'QUALITY']) and class then
add_category((class=='' and 'Unassessed' or class..'-Class') .. ' ' .. tf_assessment_cat)
end
if tf_importance then
add_category(tf_importance .. '-' .. importance_name .. ' ' .. tf_assessment_cat)
end
if args[tf_prefix..'NAME'] then
page_assessment(project..'/'..args[tf_prefix..'NAME'], class, tf_importance)
end
if args[tf_prefix..'MAIN_CAT'] then
add_category(args[tf_prefix..'MAIN_CAT'])
end
if args[tf_prefix..'NESTED'] then
table.insert(nested_tf, wikilink(args[tf_prefix..'LINK'], args[tf_prefix..'NESTED']))
end
end
if args.HOOK_TF then
table.insert(rows, args.HOOK_TF)
end
---------------------------
-- Notes ------------------
---------------------------
local note_default_size = args.NOTE_SIZE or args.NOTE_1_SIZE or 'x25px'
local render_note = function(note_args)--text, image_name, size, category, sort_prefix
if note_args.category and note_args.category~='none' then
local sort = note_args.sort_prefix and note_args.sort_prefix .. current_title.text
add_category(note_args.category, sort)
end
local note_image = note_args.image_name and image{
image = note_args.image_name,
size = note_args.size or note_default_size,
location = 'center',
alt = 'Note icon',
link = ''
} or ''
if note_args.text then
local ret = mw.html.create('tr')
ret:tag('td'):wikitext(note_image):done()
:tag('td'):addClass('mbox-text'):attr('colspan', '2'):wikitext(note_args.text):allDone()
return ret
end
end
local auto = false
local auto_arg = args.auto and string.lower(args.auto)
if (auto_arg=='yes' or auto_arg=='stub') and class=='Stub' then
auto = 'stub'
elseif (auto_arg=='inherit' or auto_arg=='length') and class and class~='' then
auto = auto_arg
end
if auto then
local auto_cat = args.AUTO_ASSESS_CAT or string.format(cfg.auto.default_cat, project)
local auto_text = string.format(
cfg.auto.assessed,
pagetype,
cfg.auto[auto], -- method of automatic assessment
parameter_format('auto')
)
local sort_prefix
if auto=='stub' then
sort_prefix = 'S'
elseif auto=='length' then
sort_prefix = 'L'
elseif auto=='inherit' then
local sort_codes = cfg.auto.sort_codes
sort_prefix = sort_codes[class] or cfg.auto.default_sort_code
end
local auto_note = render_note{
text = auto_text,
image_name = cfg.auto.icon,
category = auto_cat,
sort_prefix = sort_prefix
}
table.insert(collapsed, auto_note)
end
if yesno(args.attention) then
local attention_cat = args.ATTENTION_CAT or string.format(cfg.attention.default_cat, project)
local attention_note = render_note{
text = string.format(cfg.attention.text, pagetype),
image_name = cfg.attention.icon,
category = attention_cat
}
table.insert(collapsed, attention_note)
end
if yesno(args.infobox) then
local infobox_cat = args.INFOBOX_CAT or string.format(cfg.infobox.default_cat, project)
local infobox_note = render_note{
text = string.format(cfg.infobox.text, pagetype),
image_name = cfg.infobox.icon,
category = infobox_cat
}
table.insert(collapsed, infobox_note)
end
for _, k in ipairs(notes) do
local note_prefix = 'NOTE_' .. k .. '_'
local note = render_note{
text = parse_text(args[note_prefix..'TEXT']),
image_name = args[note_prefix..'IMAGE'],
size = args[note_prefix..'SIZE'],
category = args[note_prefix..'CAT']
}
table.insert(collapsed, note)
end
local note_count = #collapsed
if args.HOOK_NOTE then
table.insert(collapsed, args.HOOK_NOTE)
local hook_collapsed
if args.HOOK_COLLAPSED then
local success, result = pcall(mw.ext.ParserFunctions.expr, args.HOOK_COLLAPSED)
hook_collapsed = success and tonumber(result) or 0
if args.HOOK_COLLAPSED=='auto' then
hook_collapsed = 1
end
end
note_count = note_count + hook_collapsed
end
---------------------------
-- Collapsed secton -------
---------------------------
if note_count > (tonumber(args.COLLAPSED) or 2) then -- collapse note section
local collapsed_section = mw.html.create('tr')
local collapsed_rows = collapsed_section:tag('td')
:attr('colspan','3')
:addClass('wpb-collapsed-notes')
:tag('table')
:addClass('mw-collapsible mw-collapsed')
:tag('tr')
:tag('th')
:attr('colspan','3')
:addClass('wpb-collapsed-head')
:wikitext(args.COLLAPSED_HEAD or 'More information:')
:done()
:done()
:tag('tr')
:tag('td')
:addClass('mbox-image wpb-gutter')
:css('min-width',image_left_size)
:tag('span')
:addClass('wpb-iefix')
:wikitext('/ ') --TO FIX IE
:done()
:done()
:tag('td'):done()
:tag('td'):done()
:done()
for _, row in ipairs(collapsed) do
collapsed_rows:node(row)
end
collapsed_rows:allDone()
table.insert(rows, collapsed_section)
else
for _, row in ipairs(collapsed) do
table.insert(rows, row)
end
end
---------------------------
-- Bottom text ------------
---------------------------
if args.HOOK_BOTTOM then
table.insert(rows, args.HOOK_BOTTOM)
end
if args.BOTTOM_TEXT then
local bottom_text = mw.html.create('tr')
bottom_text
:tag('td')
:attr('colspan','3')
:wikitext(parse_text(args.BOTTOM_TEXT))
:allDone()
table.insert(rows, bottom_text)
end
if args.MAIN_CAT then
add_category(args.MAIN_CAT)
end
---------------------------
-- Make banner ------------
---------------------------
local nested_tf_str = #nested_tf>0 and ' / ' .. table.concat(nested_tf, ' / ') or ''
if args.HOOK_NESTED then
nested_tf_str = nested_tf_str .. ' ' .. args.HOOK_NESTED
end
local nested_ratings_str = #nested_ratings>0 and '(Rated ' .. table.concat(nested_ratings, ', ') .. ')' or ''
if args.HOOK_NESTED_ASSESS then
nested_ratings_str = nested_ratings_str .. args.HOOK_NESTED_ASSESS
end
local status_class = (cfg.status[args.PROJECT_STATUS] or cfg.status.default) .. '-wikiproject'
local banner = mw.html.create('table')
local banner_rows = banner
:addClass('tmbox tmbox-notice mw-collapsible innercollapse wpb')
:addClass(status_class)
:tag('tr')
:addClass('wpb-header')
:tag('td')
:addClass('wpb-header-name')
:wikitext(wikilink(project_link.prefixedText, project_name) .. nested_tf_str)
:done()
:tag('th')
:addClass('wpb-header-assessment')
:wikitext(nested_ratings_str)
:done()
:done()
:tag('tr')
:tag('td')
:addClass('mbox-text wpb-main')
:attr('colspan','2')
:tag('table')
for _, row in ipairs(rows) do
banner_rows:node(row)
end
banner_rows:allDone()
if args.listas then
frame:preprocess('{{DEFAULTSORT:' .. args.listas .. '}}')
end
local tstyle = frame:extensionTag ('templatestyles', '', {src='Module:Message box/tmbox.css'}) ..
frame:extensionTag ('templatestyles', '', {src='WPBannerMeta/styles.css'})
return warning .. tstyle .. tostring(banner) .. table.concat(categories)
end
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame)
local raw_args = frame:getParent().args
local banner_name = mw.title.new(args.BANNER_NAME or 'Template:WikiProject ' .. (args.PROJECT or 'PROJECT'))
local demo = not yesno(args.category or true, true)
local on_template_page = not demo and (
current_title.rootPageTitle==banner_name.rootPageTitle
or (current_title.rootPageTitle and current_title.rootPageTitle.prefixedText=='Template:WPBannerMeta')
)
if on_template_page then
local templatepage = require('Module:WikiProject banner/templatepage' .. (sandbox or '')).templatepage
return templatepage(args, raw_args)
else
return p._main(args, raw_args, demo, banner_name)
end
end
return p