Module:WikiProject banner/templatepage

Module:WikiProject banner/related

This submodule of Module:WikiProject banner produces the output which will be displayed on WikiProject banner template pages. It will be displayed automatically on the following pages:

  • Pages of the form Template:WikiProject PROJECT
  • Pages specified by the BANNER_NAME parameter (if used)

The module produces the following main components:

  • An example of the output of the template it is displayed on. This includes a demonstration of all assessments, notes, task forces that the template is set up for.
  • Notices about the configuration of the template.
  • Warnings about any missing categories needed by the template.
  • Relevant template categories.
  • Automatically produced documentation (if selected).

See also

mali niŋ

require('strict')
local p = {}
p.templatepage = function(args, raw_args, invoke)
---------------------------
-- Initialise variables ---
---------------------------
local sandbox-- = '/sandbox'
local config = mw.loadData('Module:WikiProject banner/config' .. (sandbox or ''))
local cfg = config.template_page -- convenient shortcut for template_page configuration settings
local lang = mw.language.getContentLanguage()
local current_page = mw.title.getCurrentTitle()
local on_sandbox = lang:lc(current_page.subpageText)=='sandbox' -- on sandbox subpage
	or current_page.namespace==2 -- in User namespace
	or current_page.rootText=='WPBannerMeta' -- subpage of Template:WPBannerMeta
local frame = mw.getCurrentFrame()
local parameter_format = function(parameter, value)
	local code = mw.html.create('code')
		:addClass('tpl-para')
		:css('word-break', 'break-word')
		:wikitext('|' .. parameter .. '=' .. (value or ''))
		:done()
	return tostring(code)
end
local yesno = require('Module:Yesno')
local messageBox = require('Module:Message box').main
local project = args.PROJECT or ''
local banner_name = mw.title.new(args.BANNER_NAME or 'Template:WikiProject ' .. project)
local notices, tracking_cats = {}, {}
local add_tracking = function(tracking, show_on_sandbox)
	local category = tracking.category or cfg.default_tracking -- uses [[Category:WikiProject banners with errors]] by default
	local key = tracking.sort_key or (project~='' and project or '*') -- sort key defaults to name of project
	if not on_sandbox or show_on_sandbox then
		table.insert(tracking_cats, '[[Category:' .. category .. '|' .. key .. ']]')
	end
end
local project_link = args.PROJECT_LINK or 'Wikipedia:WikiProject ' .. project
---------------------------
-- Demonstrative banner ---
---------------------------
args.PROJECT = args.PROJECT or ''
args.class = raw_args.class and 'C'
args.importance = raw_args.importance and 'High'
args.priority = raw_args.priority and 'High'
args.auto = raw_args.auto and 'inherit'
args.attention = raw_args.attention and 'yes'
args.infobox = raw_args.infobox and 'yes'
args.b1 = raw_args.b1 and 'yes'
args.b2 = raw_args.b2 and 'no'
args.b3 = raw_args.b3 and 'foo'
args.b4 = raw_args.b4 and 'yes'
args.b5 = raw_args.b5 and 'na'
args.b6 = raw_args.b6
local note_count = 0
local task_forces, notes = {}, {}
for arg_name, _ in pairs(raw_args) do
	local tf_match = mw.ustring.match(arg_name,'^tf (%d+)$')
	if tf_match then
		table.insert(task_forces, tf_match)
		args[arg_name] = 'yes'
		if raw_args['tf '..tf_match..' importance'] then
			args['tf '..tf_match..' importance'] = 'Top'
		end
	end
	local note_match = mw.ustring.match(arg_name,'^note (%d+)$')
	if note_match then
		table.insert(notes, note_match)
		local text_arg = args['NOTE_'..note_match..'_TEXT']
		if text_arg and text_arg~='' then
			args[arg_name] = 'yes'
			note_count = note_count + 1
		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 demo_banner = require('Module:WikiProject banner'  .. (sandbox or ''))._main(args, args, true, banner_name)
---------------------------
-- Custom class mask ------
---------------------------
local custom_mask = banner_name:subPageTitle('class')
if args.QUALITY_SCALE=='subpage' then
	if custom_mask.exists and #custom_mask:getContent()>1 then
		table.insert(notices, string.format(
			cfg.custom_quality_mask.in_use,
			custom_mask.prefixedText
		))
	else
		table.insert(notices, string.format(
			cfg.custom_quality_mask.missing,
			parameter_format('QUALITY_SCALE'),
			custom_mask.prefixedText
		))
	end
elseif custom_mask.exists and #custom_mask:getContent()>1 and args.QUALITY_CRITERIA=='custom' then
	table.insert(notices, string.format(
		cfg.custom_quality_mask.unused,
		custom_mask.prefixedText,
		parameter_format('QUALITY_SCALE','subpage')
	))
end
---------------------------
-- Quality criteria -------
---------------------------
if args.QUALITY_CRITERIA=='custom' then
	table.insert(notices, cfg.quality_criteria.custom)
	add_tracking(cfg.quality_criteria.custom_tracking)
elseif args.QUALITY_SCALE=='inline' or args.QUALITY_SCALE=='subpage' then
	table.insert(notices, string.format(
		cfg.quality_criteria.standard,
		parameter_format('QUALITY_SCALE')
	))
	add_tracking(cfg.quality_criteria.standard_tracking)
end
---------------------------
-- Custom importance mask -
---------------------------
local custom_mask = banner_name:subPageTitle('importance')
if args.IMPORTANCE_SCALE=='subpage' then
	if custom_mask.exists and #custom_mask:getContent()>1 then
		table.insert(notices, string.format(
			cfg.custom_importance_mask.in_use,
			custom_mask.prefixedText
		))
	else
		table.insert(notices, string.format(
			cfg.custom_importance_mask.missing,
			parameter_format('IMPORTANCE_SCALE'),
			custom_mask.prefixedText
		))
	end
elseif custom_mask.exists and #custom_mask:getContent()>1 then
	table.insert(notices, string.format(
		cfg.custom_importance_mask.unused,
		custom_mask.prefixedText,
		parameter_format('IMPORTANCE_SCALE','subpage')
	))
end
---------------------------
-- Collapsed section ------
---------------------------
local collapse_threshold = tonumber(args.COLLAPSED) or 2
local hook_note = 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 nil
	if args.HOOK_COLLAPSED=='auto' then
		hook_collapsed = hook_note and 1 or 0 -- default assumption is that HOOK_NOTE produces max one note
	end
end
note_count = note_count
	+ (raw_args.auto and 1 or 0)
	+ (raw_args.attention and 1 or 0)
	+ (raw_args.infobox and 1 or 0)
	+ (hook_collapsed or 0)
local more_than = collapse_threshold>0 and string.format(cfg.collapsing_notes.more_than, collapse_threshold) or cfg.collapsing_notes.any
if note_count > collapse_threshold then
	table.insert(notices, string.format(
		cfg.collapsing_notes.text,
		note_count,
		more_than,
		parameter_format('COLLAPSED')
	))
end
if hook_note then
	if not hook_collapsed then
		table.insert(notices, string.format(
			cfg.collapsing_notes.note_counter,
			parameter_format('HOOK_NOTE'),
			parameter_format('HOOK_COLLAPSED')
		))
		add_tracking(cfg.collapsing_notes.tracking)
	end
end
---------------------------
-- Assessment link --------
---------------------------
if raw_args.class or raw_args.importance then
	local url = mw.uri.fullUrl(banner_name.prefixedText,{
		action = 'edit',
		summary = cfg.assessment_link.edit_summary
	})
	if args.ASSESSMENT_LINK then
		if not mw.title.new(args.ASSESSMENT_LINK).exists then
			table.insert(notices, string.format(
				cfg.assessment_link.missing,
				'[[' .. args.ASSESSMENT_LINK .. ']]',
				tostring(url),
				parameter_format('ASSESSMENT_LINK')
			))
		end
	else
		local default = mw.title.new(project_link .. '/Assessment')
		if default.exists then
			table.insert(notices, string.format(
				cfg.assessment_link.default,
				'[[' .. default.prefixedText .. ']]',
				tostring(url),
				parameter_format('ASSESSMENT_LINK')
			))
		end
	end
end
---------------------------
-- Project parameter ------
---------------------------
if args.PROJECT=='' then
	table.insert(notices, cfg.project.text)
	add_tracking(cfg.project.tracking)
end
---------------------------
-- Subst check ------------
---------------------------
if not raw_args.substcheck then
	table.insert(notices, string.format(
		cfg.substcheck.text,
		parameter_format('substcheck')
	))
	add_tracking(cfg.substcheck.tracking)
end
---------------------------
-- Portal link ------------
---------------------------
if args.PORTAL then
	local portal_image = require('Module:Portal')._image(args.PORTAL)
	local portal_link = mw.title.new('Portal:' .. args.PORTAL)
	local explain
	if portal_link and portal_link.exists then
		if portal_image=='Portal-puzzle.svg' then
			explain = string.format(cfg.portal.no_image, cfg.portal.instructions)
		else
			explain = string.format(
				cfg.portal.image_link,
				'[[:File:' .. portal_image .. ']]',
				cfg.portal.instructions
			)
		end
	else
		explain = string.format(cfg.portal.missing, parameter_format('PORTAL'))
		add_tracking(cfg.portal.tracking)
	end
	local text = string.format(
		cfg.portal.text,
		portal_link.prefixedText,
		explain
	)
	table.insert(notices, text)
end
---------------------------
-- Task forces ------------
---------------------------
for _, k in ipairs(task_forces) do
	local name_parameter = 'TF_'..k..'_NAME'
	if raw_args['tf '..k] and not args['TF_'..k..'_NAME'] then
		table.insert(notices, string.format(
			cfg.task_force.text,
			parameter_format('TF_'..k..'_NAME')
		))
		add_tracking(cfg.task_force.tracking)
	end
	if args['TF_'..k..'_QUALITY'] and not yesno(args['TF_'..k..'_QUALITY']) then
		table.insert(notices, string.format(
			cfg.task_force.quality_tracking,
			parameter_format('TF_'..k..'_QUALITY')
		))
		add_tracking(cfg.task_force.quality_cat)
	end
end
---------------------------
-- Parameter checking -----
---------------------------
if invoke then-- template is calling {{#invoke:WikiProject banner|main|...}}
	local category = mw.title.new(string.format(
		config.unknown_parameters.tracking,
		args.PROJECT_NAME or 'WikiProject ' .. project
	))
	local text = string.format(
		category.exists and cfg.parameters.tracking or cfg.parameters.create,
		'[[:' .. category.fullText .. ']]'
	)
	table.insert(notices, text)
end
---------------------------
-- Assessment categories --
---------------------------
local importance_name = args.IMPN or (raw_args.priority and 'priority' or config.importance.default_name)
local check_categories = function(project, assessment_cat, quality_scale, importance_scale, prefix)
	assessment_cat = assessment_cat or ((project or '') .. ' articles')
	local missing_cats = {}
	local check_cat = function(cat_name, preload)
		local category = mw.title.new('Category:' .. cat_name)
		if not category.exists then
			local url = mw.uri.fullUrl('Category:' .. cat_name, {
				action = 'edit',
				preload = preload,
				editintro = cfg.check_assessment.editintro,
				preview = 'no',
				summary = cfg.check_assessment.create_summary,
				['preloadparams[1]'] = project,
				['preloadparams[2]'] = mw.ustring.gsub(assessment_cat, ' articles', '')
			})
			local pages_in_category = mw.site.stats.pagesInCategory(cat_name, 'pages')
			local label = pages_in_category>0 and '<b>create</b>' or 'create'
			local create_link = '[[:' .. category.prefixedText .. ']] &ndash; ([' .. tostring(url) .. ' ' .. label .. '])'
			table.insert(missing_cats, create_link)
		end
	end
	if quality_scale and quality_scale~='inline' and quality_scale~='subpage' then
		check_cat(lang:ucfirst(assessment_cat) .. ' by quality', cfg.check_assessment.meta_preload)
		for _, class in ipairs(cfg.check_assessment.classes) do
			local cat_name = (class=='Unassessed' and 'Unassessed' or class..'-Class') .. ' ' .. assessment_cat
			check_cat(cat_name, cfg.check_assessment.quality_preload)
		end
	end
	if importance_scale and importance_scale~='inline' and importance_scale~='subpage' then
		check_cat(lang:ucfirst(assessment_cat) .. ' by ' .. importance_name, cfg.check_assessment.meta_preload)
		for _, importance in ipairs(cfg.check_assessment.importances) do
			local cat_name = importance .. '-' .. importance_name .. ' ' .. assessment_cat
			check_cat(cat_name, cfg.check_assessment.importance_preload)
		end
	end
	if #missing_cats>0 then
		local intro = string.format(
			cfg.check_assessment.text,
			project or '',
			parameter_format((prefix or '') .. cfg.check_assessment.parameter_suffix)
		)
		local list = mw.html.create('ul')
		for _, missing in ipairs(missing_cats) do
			list:tag('li'):wikitext(missing):done()
		end
		list:done()
		add_tracking(cfg.check_assessment.tracking)
		return messageBox('ombox', {
			type = 'content',
			image = '[[File:' .. cfg.check_assessment.icon .. '|50px]]',
			text = intro .. tostring(list)
		})
	end
	return ''
end
local warnings = {}
table.insert(warnings, check_categories(
	project,
	args.ASSESSMENT_CAT,
	raw_args.class and (args.QUALITY_SCALE or ''),
	(raw_args.importance or raw_args.priority) and (args.IMPORTANCE_SCALE or '')
))
local ntf = #task_forces
local check_task_forces = {}
if ntf>10 then -- too many task forces to check all, so check a selection instead
	local used = {}
	math.randomseed(os.time())
	for i = 1, 10 do
		local new
		repeat
			new = math.random(ntf)
		until not used[new]
		table.insert(check_task_forces, task_forces[new])
		used[new] = true
	end
	table.sort(check_task_forces, function (x, y) return tonumber(x) < tonumber(y) end)
	table.insert(notices, string.format(cfg.check_assessment.too_many, table.concat(check_task_forces,', ')))
else
	check_task_forces = task_forces
end
for _, k in ipairs(check_task_forces) do
	local tf_prefix = 'TF_' .. k .. '_'
	table.insert(warnings, check_categories(
		args[tf_prefix..'NAME'],
		args[tf_prefix..'ASSESSMENT_CAT'],
		raw_args.class and yesno(args[tf_prefix..'QUALITY']) and (args.QUALITY_SCALE or ''),
		raw_args['tf '..k..' importance'] and (args.IMPORTANCE_SCALE or ''),
		tf_prefix
	))
end
---------------------------
-- Other categories -------
---------------------------
local missing_cats = {}
local check_cat = function(cat_name)
	if cat_name and cat_name~='none' then
		local category = mw.title.new('Category:' .. (cat_name or ''))
		if category and not category.exists then
			table.insert(missing_cats, '[[:' .. category.prefixedText .. ']]')
		end
	end
end
if raw_args.attention then
	local attention_cat = args.ATTENTION_CAT or string.format(config.attention.default_cat, project)
	check_cat(attention_cat)
end
if raw_args.infobox then
	local infobox_cat = args.INFOBOX_CAT or string.format(config.infobox.default_cat, project)
	check_cat(infobox_cat)
end
if raw_args.auto then
	local auto_cat = args.AUTO_ASSESS_CAT or string.format(config.auto.default_cat, project)
	check_cat(auto_cat)
end
check_cat(args.MAIN_CAT)
for _, k in ipairs(notes) do
	check_cat(args['NOTE_'..k..'_CAT'])
end
for _, k in ipairs(task_forces) do
	check_cat(args['TF_'..k..'_MAIN_CAT'])
end
local b_checklist = false
if (raw_args.b1 or raw_args.b2 or raw_args.b3 or raw_args.b4 or raw_args.b5 or raw_args.b6) then
	b_checklist = true
	for _, p in ipairs(cfg.parameters.b_checklist.categories) do
		check_cat(args[p])
	end
end
if #missing_cats>0 then
	local list = mw.html.create('ul')
	for _, missing in ipairs(missing_cats) do
		list:tag('li'):wikitext(missing):done()
	end
	list:done()
	table.insert(warnings, messageBox('ombox', {
		type = 'content',
		image = '[[File:' .. cfg.check_other.icon .. '|50px]]',
		text = cfg.check_other.text .. tostring(list)
	}))
	add_tracking(cfg.check_other.tracking)
end
---------------------------
-- Template categories ----
---------------------------
if on_sandbox then
	add_tracking(cfg.template_categories.sandbox, true)
else
	if raw_args.class then
		add_tracking(cfg.template_categories.with_quality)
	else
		add_tracking(cfg.template_categories.without_quality)
	end
	if (args.PROJECT_NAME=='WikiProject '..project or not args.PROJECT_NAME) and current_page.rootPageTitle.prefixedText~='Template:WikiProject ' .. project then
		add_tracking(cfg.template_categories.non_standard)
	end
end
---------------------------
-- Unknown parameters -----
---------------------------
local append_table = function(source, destination)
	local out = destination or {}
	for _, v in ipairs(source) do
		table.insert(out, v)
	end
	return out
end
local parameters = append_table(cfg.parameters.config)
parameters = append_table(cfg.parameters.passthrough, parameters)
for _, k in ipairs(task_forces) do
	table.insert(parameters, 'tf '..k)
	table.insert(parameters, 'tf '..k..' importance')
	for _, p in ipairs(cfg.parameters.taskforce.suffix) do
		table.insert(
			parameters,
			string.format(cfg.parameters.taskforce.prefix, k) .. p
		)
	end
end
for _, k in ipairs(notes) do
	table.insert(parameters, 'note '..k)
	for _, p in ipairs(cfg.parameters.note.suffix) do
		table.insert(
			parameters,
			string.format(cfg.parameters.note.prefix, k) .. p
		)
	end
end
if b_checklist then
	parameters = append_table(cfg.parameters.b_checklist.categories, parameters)
	parameters = append_table(cfg.parameters.b_checklist.other, parameters)
end
parameters.preview = cfg.parameters.preview
parameters.unknown = ''
local parameter_check = require('Module:Check for unknown parameters')._check(parameters, raw_args)
---------------------------
-- Produce notices --------
---------------------------
local notice_html, notice_list
if #notices>=1 then
	notice_html = mw.html.create('ul')
	for _, notice in ipairs(notices) do
		notice_html:tag('li'):wikitext(notice):done()
	end
	notice_list = ' ' .. cfg.notice_text .. tostring(notice_html)
end
local info =  messageBox('ombox', {
	type = 'notice',
	image = '[[File:Icon tools.svg|50px]]',
	text = string.format(cfg.info, frame:expandTemplate{title='tlx', args={'WPBannerMeta'}}) .. (notice_list or '')
})
---------------------------
-- Documentation ----------
---------------------------
local auto_doc = {}
local template_code = banner_name:getContent()
local parameter_name = {}
for _, parameter in ipairs(parameters) do
	parameter_name[parameter] = string.match(template_code, parameter..'%s*=%s*{%{%{([%a%d_%s-]*)')
end
local show_param = function(parameter, map)
	local p = parameter_name[parameter]
	if p then
		table.insert(auto_doc, '|' .. p .. '= ')
	end
end
local add_title = function(title, level)
	local hl = 'h' .. tostring(level or 3)
	table.insert(auto_doc, tostring(mw.html.create(hl):wikitext(title)) .. '\n')
end
table.insert(auto_doc,'This is the WikiProject banner template used by [[' .. project_link .. '|' .. (args.PROJECT_NAME or 'WikiProject ' .. project) .. ']] to keep track of articles within its scope.')
add_title('Basic usage')
table.insert(auto_doc, 'Place this on the talk page of relevant articles:')
table.insert(auto_doc, '<br><code>{{' .. banner_name.text .. ' ')
show_param('class')
if raw_args.importance then
	show_param('importance')
else
	show_param('priority')
end
table.insert(auto_doc, '}}</code>')
add_title('Full usage')
table.insert(auto_doc, 'It is usual to remove any unused parameters from the template call.')
table.insert(auto_doc, '<br><code>{{' .. banner_name.text .. ' ')
show_param('class')
if raw_args.importance then
	show_param('importance')
else
	show_param('priority')
end
show_param('listas')
show_param('attention', true)
show_param('infobox', true)
for _, k in ipairs(notes) do
	show_param('note ' .. k)
end
for _, k in ipairs(task_forces) do
	show_param('tf ' .. k)
	show_param('tf ' .. k .. ' importance')
end
table.insert(auto_doc, '}}</code>')
add_title('Parameters', 2)
if raw_args.class then
	table.insert(auto_doc, frame:expandTemplate{title = 'WPBannerDoc', args = {
		[1] = 'class',
		ASSESSMENT_LINK = args.ASSESSMENT_LINK,
		BANNER_NAME = banner_name.prefixedText,
		PROJECT = project,
		PROJECT_LINK = project_link,
		QUALITY_SCALE = args.QUALITY_SCALE,
		class_values = '',
	}} .. '\n')
end
if raw_args.importance then
	table.insert(auto_doc, frame:expandTemplate{title = 'WPBannerDoc', args = {
		[1] = 'importance',
		ASSESSMENT_LINK = args.ASSESSMENT_LINK,
		BANNER_NAME = banner_name.prefixedText,
		PROJECT = project,
		PROJECT_LINK = project_link,
		IMPORTANCE_SCALE = args.IMPORTANCE_SCALE,
	}} .. '\n')
end

local doc = ''
if args.DOC=='auto' or args.DOC=='auto+custom' then
	doc = require('Module:Documentation').main{content = table.concat(auto_doc)}
end
if args.DOC=='custom' or args.DOC=='auto+custom' then
	doc = doc .. require('Module:Documentation').main()
end

return parameter_check .. demo_banner .. info .. table.concat(warnings) .. table.concat(tracking_cats) .. doc
end

return p