Jump to content

Module:WikiProject banner/sandbox

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by MSGJ (talk | contribs) at 15:17, 10 June 2023 (try something). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)
require('strict')
local p = {}
local cfg = mw.loadData('Module:WikiProject banner/config')
local pagetype = require('Module:Pagetype')._main({})
local mbox = require('Module:Message box').main
local yesno = require('Module:Yesno')
local frame = mw.getCurrentFrame()

local function wikilink(link, display)
	if display then
		return '[[' .. link .. '|' .. display .. ']]'
	else
		return '[[' .. link .. ']]'
	end
end

local function image(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 function warn_on_subst(subst, project, banner_name, suppress_categories)
	if subst ~= 'SUBST' then
		return ''
	end
	local target_name
	if banner_name then
		target_name = mw.title.new(banner_name).text
	else
		target_name = 'WikiProject ' .. project
	end
	local warning = mbox( 'ombox', {
		image = cfg.subst_warning.image,
		type = cfg.subst_warning.type_,
		text = '<p>' .. string.format(cfg.subst_warning.text, project, target_name) .. '</p>'
	})
	local categories
	if suppress_categories then
		categories = ''
	else
		-- Is the intent of <includeonly> only to add it to the banner invocation
		-- and not elsewhere? TODO: Check docs.
		categories = cfg.subst_warning.categories
	end
	return warning .. categories
end

local function warn_on_namespace(category, project, banner_name, suppress_categories)
	local current_title = mw.title.getCurrentTitle()
	local current_namespace = current_title.nsText
	local current_subjectspace = current_title.subjectNsText
	if current_namespace ~= current_subjectspace or -- if talk page or
		current_namespace == 'User' then -- if user page
			-- then don't warn
			-- why user pages?
		return ''
	end
	if category == 'no' then return '' end -- if no cat then don't warn?
	--if is_banner_page(banner_name, project) then return '' end -- if a banner (sub)page don't warn
	local para_category = frame:expandTemplate{
		title = 'para',
		args = {'category', 'no'}
	}
	local para_banner_name = frame:expandTemplate{
		title = 'para',
		args = {'BANNER_NAME'}
	}
	local on_template_page
	if current_namespace == 'Template' then
		on_template_page = string.format(
			cfg.namespace_warning.on_template_page,
			para_banner_name,
			current_title.nsText .. ':' .. current_title.rootText
		)
	else
		on_template_page = ''
	end
	local warning = mbox( 'ombox', {
		image = cfg.namespace_warning.image,
		type = cfg.namespace_warning.type_,
		text = '<p>' .. string.format(
			cfg.namespace_warning.text,
			pagetype,
			current_title.talkPageTitle.fullText,
			para_category,
			on_template_page
		) .. '</p>'
	})
	local category
	if suppress_categories then
		category = ''
	else
		local sortkey
		if current_namespace == 'Template' then
			sortkey = "PARAMETER"
		else
			sortkey = "LOCATION"
		end
		category = string.format(
			cfg.namespace_warning.categories,
			sortkey
		)
	end
	return warning .. category
	
end

local function status_class(project_status)
	local status_classes = {
		['inactive-wikiproject'] = { 'inactive' },
		['semi-active-wikiproject'] = { 'semi-active', 'semiactive' },
		['defunct-wikiproject'] = { 'defunct' },
		['active-wikiproject'] = { 'active' }
	}
	for v, k_table in ipairs(status_classes) do
		for _, k in ipairs(k_table) do
			if v == k then 
				return v
			end
		end
	end
end

local function if_exists(target, fallback)
	if mw.title.new(target).exists then
		return wikilink(target)
	else
		return fallback or target
	end
end

local isarticle = function(class)
	local nonarticleclasses = {'Template', 'File', 'Category', 'Disambig', 'Redirect', 'Portal', 'Project', 'Draft', 'Book', 'FM'} -- these classes will not be identified as conflicting with NA-class
	local article = true
	for _,v in ipairs(nonarticleclasses) 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 mw.title.getCurrentTitle().prefixedText
	local get_parameter_value = require('Module:Template parameter value').getValue
	local WPBSredirects = {'WikiProject banner shell','WikiProject banner shell/sandbox','Bannershell','Multiple wikiprojects','Project shell','Scope shell','WPB','WPBS','WPBannerShell','WP Banner Shell','WP banner shell','WikiProjectBannerShell','WikiProjectBanner Shell','WikiProjectBanners','WikiProject BannerShell','WikiProject Banner Shell','WikiProject Banners','WikiProject Banners Shell','WikiProject Shell','WikiProject banner','WikiProject banner shell/redirect','WikiProject shell','WikiprojectBannerShell','Wikiproject banner holder','Wikiproject banner shell','Wikiprojectbanners','Wikiprojectbannershell','Wpb','Wpbannershell','Wpbsgclass'}
	local success, result = get_parameter_value(page, WPBSredirects, '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
		if mw.title.new(banner_name..'/importance').exists then -- pass to custom importance mask
			importance = frame:expandTemplate{title=banner_name..'/importance', args={importance=raw_importance or '¬', class=class}}
		end
	else
		importance = frame:expandTemplate{title='Template:Importance mask', args={raw_importance or '¬', class=class}}
	end
	return importance
end

function p._main(args)
---------------------------
-- Initialise parameters --
---------------------------
local project = args.PROJECT
local arg_or_default = function(arg, default) -- make it cleaner to initialize 'trivial' variables
	if args[arg] and args[arg] ~= '' then
		return args[arg]
	else
		return default or nil
	end
end
local banner_name = arg_or_default('BANNER_NAME', 'Template:WikiProject' .. project)
local subst = arg_or_default('substcheck', '¬')
-- TODO: When done, remove suppress_categories (or convert).
local suppress_categories = arg_or_default('suppress')
local subst_warning = warn_on_subst(subst, project, banner_name, suppress_categories)
local category = arg_or_default('category', '¬') -- TODO: replace ¬ to nil
--	if category ~= 'no' and not is_banner_page(banner_name, project) then
--		local banner = banner(args) or '' -- TODO
--	else
--		local template_page_banner = template_page_banner(args) or '' -- TODO
--	end
local project_status = arg_or_default('PROJECT_STATUS', 'active')
local project_link = mw.title.new(arg_or_default('PROJECT_LINK', 'Wikipedia:WikiProject ' .. project))
local rows, collapsed, nested_ratings, task_forces, notes, categories = {}, {}, {}, {}, {}, {}
local add_category = function(category, key)
	table.insert(categories, wikilink(':Category:' .. category, key))
	return
end
local project_talk = wikilink(project_link.talkPageTitle.prefixedText,'the discussion')
local assessment_cat = arg_or_default('ASSESSMENT_CAT', project .. ' articles')
-- Provides the list of task forces (for now)
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
local title = args.page and mw.title.new(args.page) or mw.title.getCurrentTitle()
---------------------------
-- Primary image/text -----
---------------------------
local function primary_image(image_name, image_size)
	local cell = mw.html.create('td')
	local image_size = image_size or '80px'
	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 = arg_or_default('MAIN_ARTICLE')
local topic
if mainarticle then
	topic = if_exists(mainarticle)
else
	topic = if_exists(project, project .. ' articles')
end
local main_text = arg_or_default('MAIN_TEXT') or string.format(cfg.maintext, pagetype, wikilink(project_link.prefixedText, 'WikiProject ' .. project), topic, project_talk)
local image_left_size = arg_or_default('IMAGE_LEFT_SIZE') or arg_or_default('IMAGE_LEFT_LARGE')
local primary_row = mw.html.create('tr')
primary_row
	:node(primary_image(arg_or_default('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)
			:done()
			:tag('span')
				:addClass('wpb-assessment_cat')
				:wikitext(assessment_cat)
			:done()
		:done()
	:done()	
	:node(primary_image(
		arg_or_default('IMAGE_RIGHT'),
		arg_or_default('IMAGE_RIGHT_SIZE') or arg_or_default('IMAGE_RIGHT_LARGE')
	))
:done()
table.insert(rows, {node=primary_row})
---------------------------
-- Quality assessment -----
---------------------------
local class_mask = require('Module:Class mask')._main
local class = '¬'
if args.QUALITY_SCALE == 'inline' then
	class = args.class
elseif args.QUALITY_SCALE == 'subpage' then
	if mw.title.new(args.banner_name .. '/class').exists then
		class = frame:expandTemplate{title = args.banner_name .. '/class', args = args}
	end
else
	args.FQS = (args.QUALITY_SCALE == 'extended') and 'yes' or 'no'
	args[1] = args.class
	class = class_mask(args, title)
end
local show = true
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},title.prefixedText)
		article_class = article_class and class_mask({article_class}, 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},title.prefixedText)
					if classparam == '' then -- no class parameters defined, display as globally unassessed
						show = 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 = 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 = false
			else -- article class exists and differs from local class
				add_category('Articles with conflicting quality ratings')
			end
		end
	end
	if show then -- quality rating shown in banner
		local class_module = require('Module:Class')._class
		local rating = cfg.This .. ' '
		if pagetype=='article' then
			rating = rating .. ' ' .. cfg.article .. ' ' .. (class=='' and cfg.notyet or (cfg.rated .. ' ' .. class .. '-' .. cfg.class))
		else
			rating = rating .. pagetype .. ' ' .. cfg.notrequired
		end
		rating = rating .. ' ' .. cfg.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(rating)
		:allDone()
		table.insert(rows, {node=class_row})
		table.insert(nested_ratings, class..'-class')
	end
	add_category((class=='' and 'Unassessed' or class..'-Class') .. ' ' .. assessment_cat)
end
---------------------------
-- Importance assessment --
---------------------------
local importance = importance_mask(args.importance,class,args.IMPORTANCE_SCALE,banner_name)
local importance_name = args.IMPN or 'importance'
if importance~='¬' and importance~='NA' then
	local rating = cfg.This .. ' ' .. pagetype .. ' has '
		.. (importance == 'Unknown' and 'not yet received a rating' or 'been rated as ' .. importance .. '-' .. importance_name)
		.. ' on the [[Wikipedia:Version 1.0 Editorial Team/Release Version Criteria#Importance of topic|' .. importance_name .. ' 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(rating)
	:allDone()
	table.insert(rows, {node=importance_row})
	if importance~='Unknown' then -- importance is not NA or Unknown
		table.insert(nested_ratings,importance .. '-' .. importance_name)
	end
	add_category((importance=='' and 'Unknown' or importance..'-importance') .. ' ' .. assessment_cat)
end
---------------------------
-- Task forces ------------
---------------------------
local nested_tf = {}
local render_taskforce = function(args)
	local tf_assessment_cat = arg_or_default(tf_prefix .. 'ASSESSMENT_CAT', args[tf_prefix .. 'NAME'] .. 'articles')
	local tf_importance = args['tf '..k..' importance'] and importance_mask(args['tf '..k..' importance'],class,args.IMPORTANCE_SCALE,banner_name)
	if args[tf_prefix .. 'TEXT']~='none' then
		local text = ''
		if arg_or_default(tf_prefix .. 'PORTAL') then
			text = frame:expandTemplate{title='Portal', args={args[tf_prefix .. 'PORTAL'], height='15', margin='0'}}
		end
		if arg_or_default(tf_prefix .. 'TEXT') then
			text = 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~='¬' 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 = arg_or_default(tf_prefix .. 'SIZE',tf_default_size)
		local tf_image = ''
		if arg_or_default(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, {node=taskforce})
	end
	if arg_or_default(tf_prefix .. 'HOOK') then
		table.insert(rows,{text=args[tf_prefix .. 'HOOK']})
	end
	if yesno(args[tf_prefix .. 'QUALITY']) 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 arg_or_default(tf_prefix .. 'MAIN_CAT') then
		add_category(args[tf_prefix .. 'MAIN_CAT'])
	end
	if arg_or_default(tf_prefix .. 'NESTED') then
		table.insert(nested_tf, wikilink(args[tf_prefix .. 'LINK'],args[tf_prefix .. 'NESTED']))
	end
end
local tf_default_size = arg_or_default('TF_SIZE',arg_or_default('TF_1_SIZE','x25px'))
for _, k in ipairs(task_forces) do
	local tf_prefix = 'TF_' .. k .. '_'
	local tf_importance = args['tf '..k..' importance']--normalise?
	local tf_args = {
		category = category,
		PROJECT = project,
		LINK = args[tf_prefix..'LINK'],
		NAME = args[tf_prefix..'NAME'],
		IMAGE = args[tf_prefix..'IMAGE'],
		SIZE = args[tf_prefix..'SIZE'],
		TEXT = args[tf_prefix..'TEXT'],
		QUALITY = args[tf_prefix..'QUALITY'],
		class = class,
		['tf importance'] = tf_importance,
		BANNER_NAME = banner_name,
		IMPORTANCE_SCALE = args.IMPORTANCE_SCALE,
		['inherit importance'] = tf_importance and nil or args['inherit importance'],
		IMPN = args.IMPN,
		ASSESSMENT_CAT = args[tf_prefix..'ASSESSMENT_CAT'],
		MAIN_CAT = args[tf_prefix..'MAIN_CAT'],
		PORTAL = args[tf_prefix..'PORTAL']
	} 
end
if arg_or_default('HOOK_TF') then
	table.insert(rows, {text=args.HOOK_TF})
end
---------------------------
-- Notes ------------------
---------------------------
local note_default_size = arg_or_default('NOTE_SIZE',arg_or_default('NOTE_1_SIZE','x25px'))
local render_note = function(note_args)--text, image_name, size, category, sort_prefix
	if note_args.category then
		local sort = note_args.sort_prefix and note_args.sort_prefix .. title.text
		add_category(note_args.category,sort)
	end
	local note_image = ''
	if note_args.image_name then
		note_image = image{
			image = note_args.image_name,
			size = note_args.size or note_default_size,
			location = 'center',
			alt = 'Note icon',
			link = ''
		}
	end
	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 = arg_or_default('auto')
if auto_arg then
	auto_arg = string.lower(auto_arg)
end
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 = arg_or_default('AUTO_ASSESS_CAT', 'Automatically assessed ' .. project .. ' articles')
	if auto_cat=='none' then
		auto_cat = nil
	end
	local auto_text = 'This ' .. pagetype .. ' has been [[Wikipedia:Types of bots#WikiProject tagging and auto-assessment bots|automatically rated]] by a [[Wikipedia:Bots|bot]] or other tool'
	if auto=='stub' then
		auto_text = auto_text .. ' as <b>Stub-class</b> because it uses a [[Wikipedia:Stub|stub template]]'
	elseif auto=='length' then
		auto_text = auto_text .. ' based on the length of the article'
	elseif auto=='inherit' then
		auto_text = auto_text .. ' because one or more other projects use this class'
	end
	auto_text = auto_text .. '. Please ensure the assessment is correct before removing the ' .. frame:expandTemplate{title='Template:Para', args={'auto'}} .. ' parameter.'
	local sort_prefix
	if auto=='stub' then
		sort_prefix = 'S'
	elseif auto=='length' then
		sort_prefix = 'L'
	elseif auto=='inherit' then
		local sort_codes = {A=0, FA=1, FL=2, GA=3, B=4, C=5, Start=6, Stub=7, List=8}
		if sort_codes[class] then
			sort_prefix = tostring(sort_codes[class])
		else
			sort_prefix = '9' -- default prefix for any other class
		end		
	end
	local auto_note = render_note{
		text = auto_text,
		image_name = 'Robot icôn.svg',
		category = auto_cat,
		sort_prefix = sort_prefix
	}
	table.insert(collapsed, {node=auto_note})
end
if yesno(args.attention) then
	local attention_cat = arg_or_default('ATTENTION_CAT', project .. ' articles needing attention')
	if attention_cat=='none' then
		attention_cat = nil
	end
	local attention_note = render_note{
		text = 'This ' .. pagetype .. ' has been marked as needing immediate attention.',
		image_name = 'Diamond-caution.svg',
		category = attention_cat
	}
	table.insert(collapsed, {node=attention_note})
end
if yesno(args.infobox) then
	local infobox_cat = arg_or_default('INFOBOX_CAT', project .. ' articles needing infoboxes')
	if infobox_cat=='none' then
		infobox_cat=nil
	end
	local infobox_note = render_note{
		text = 'This ' .. pagetype .. ' has been marked as needing an [[Wikipedia:Manual of Style/Infoboxes|infobox]]',
		image_name = 'Gnome-mime-text-x-credits.svg',
		category = infobox_cat
	}
	table.insert(collapsed, {node=infobox_note})
end
for _, k in ipairs(notes) do
	local note_prefix = 'NOTE_' .. k .. '_'
	local note = render_note{
		text = arg_or_default(note_prefix..'TEXT'),
		image_name = arg_or_default(note_prefix..'IMAGE'),
		size = arg_or_default(note_prefix..'SIZE'),
		category = arg_or_default(note_prefix..'CAT'),
	}
	table.insert(collapsed, {node=note})
end
---------------------------
-- Collapsed secton -------
---------------------------
if #collapsed+arg_or_default('HOOK_COLLAPSED',0)>tonumber(arg_or_default('COLLAPSED','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(arg_or_default('COLLAPSED_HEAD') or 'More information:')
				:done()
			:done()--end conditional?
			:tag('tr')
				:tag('td')
					:addClass('mbox-image wpb-gutter')
					:css('min-width',image_left_size)
					:tag('span')
						:addClass('wpb-iefix')
						:wikitext('\xc2\xa0') --TO FIX IE
					:done()
				:done()
				:tag('td'):done()
				:tag('td'):done()
			:done()
			for _, row in ipairs(collapsed) do
				collapsed_rows:node(row.node)
			end
	collapsed_rows:allDone()
	table.insert(rows, {node=collapsed_section})
else
	for _, row in ipairs(collapsed) do
		table.insert(rows, row)
	end
end
---------------------------
-- Bottom text ------------
---------------------------
if arg_or_default('BOTTOM_TEXT') then
	local bottom_text = mw.html.create('tr')
		bottom_text
			:tag('td')
			:attr('colspan','3')
			:wikitext(args.BOTTOM_TEXT)
	:allDone()
	table.insert(rows, {node=bottom_text})
end
---------------------------
-- Make banner ------------
---------------------------
local nested_ratings_str = table.concat(nested_ratings, ', ')
if nested_ratings_str~='' then
	nested_ratings_str = '(Rated ' .. nested_ratings_str .. ')'
end
local nested_tf_str = table.concat(nested_tf, ' / ')
if nested_tf_str~='' then
	nested_tf_str = ' / ' .. nested_tf_str
end
local banner = mw.html.create('table')
local banner_rows = banner
	:addClass('tmbox tmbox-notice mw-collapsible innercollapse wpb')
	:addClass(status_class(project_status))
	:css('height', '0')
	:tag('tr')
		:addClass('wpb-header')
		:tag('td')
			:addClass('wpb-header-name')
			:wikitext(wikilink(project_link.prefixedText, 'WikiProject ' .. project) .. 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
					if row.node then
						banner_rows:node(row.node)
					elseif row.text then
						banner_rows:wikitext(row.text)
					end
				end
banner_rows:allDone()
if arg_or_default('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 tstyle .. subst_warning .. tostring(banner) .. 'Categories: ' .. table.concat(categories,', ')
end

function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame, {removeBlanks = false})
	if not args.PROJECT or args.PROJECT=='' then
		error('PROJECT must be defined')
	end
	local target_name
	if args.BANNER_NAME and args.BANNER_NAME~='' then
		target_name = args.BANNER_NAME
	else
		target_name = 'Template:WikiProject ' .. args.PROJECT
	end
	local current_title = mw.title.getCurrentTitle()
	local root_title = current_title.nsText .. ':' .. current_title.rootText
	if target_name==mw.title.getCurrentTitle().prefixedText then -- on template page
		local templatepage = require('Module:WikiProject banner/templatepage').templatepage
		return templatepage(args)
	else
		local namespace_warning = warn_on_namespace(false, args.PROJECT, target_name, false)
		return namespace_warning .. p._main(args)
	end
end

return p