Module:WikiProject banner

require('strict')

local p = {}

local sandbox-- = '/sandbox' -- BE SURE TO COMMENT OUT this definition when deploying to live

local cfg = mw.loadData('Module:WikiProject banner/config' .. (sandbox or ''))

local auxiliary = cfg.auxiliary_module .. (sandbox or '')

local args_module = require('Module:Arguments')

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 ''..display..'' or ''..link..''

else

return display or ''

end

end

local display_error = function(text)

local span = mw.html.create('div')

:addClass('error')

:wikitext(text)

return tostring(span)

end

local image = function(image_name, size, alt, position)

return image_name and '[[File:'

.. image_name

.. (size and '|' .. size or '')

.. (position and '|' .. position or '')

.. (alt and '|alt=' .. alt 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 importance_mask = function(raw_importance, scale, banner_name, pagetype, class)

---------------------------

-- Importance mask --------

---------------------------

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 = mw.text.trim(frame:expandTemplate{

title = custom_mask.prefixedText,

args = {

importance = raw_importance or '¬',

class = class,

pagetype = pagetype

}

})

end

elseif raw_importance then-- standard importance scale

importance = cfg.importance.na

if pagetype=='article' or pagetype=='set index article' or pagetype=='redirect' or pagetype=='draft' then

local mask = cfg.importance.mask

if mask[raw_importance:lower()] then -- valid importance specified

importance = mask[raw_importance:lower()]

elseif pagetype=='article' or pagetype=='set index article' then -- unspecified or invalid importance, use "Unknown" for articles

importance = cfg.importance.unknown

end

end

end

return importance

end

---------------------------

-- Quality class mask -----

---------------------------

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').getParameter

local success, result = get_parameter_value(page, cfg.banner_shell.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

p.class_mask = function(class, title, FQS, pagetype, article)

local resolveFQSgrade = function(class)

return FQS and lang:ucfirst(class) or 'NA'

end

local out

title = title or mw.title.getCurrentTitle()

local ns = title.namespace

class = class:match('^%s*(.-)%s*$'):lower()

if pagetype=='redirect' or pagetype=='soft redirect' then

out = resolveFQSgrade('redirect')

elseif pagetype=='disambiguation page' then

out = resolveFQSgrade('disambig')

elseif article or pagetype=='article' or pagetype=='set index article' then

if pagetype=='set index article' then

out = 'List'

elseif class=='start' or class=='stub' then -- Ucfirst

out = lang:ucfirst(class)

elseif class=='b' or class=='c' or class=='fa' or class=='fl' or class=='a' or class=='ga' then -- Upper-case

out = class:upper()

elseif class=='list' or class=='sia' or class=='si' or class=='sl' then-- List

out = 'List'

else

out = '' -- unassessed

end

elseif ns==7 or ns==711 then -- File talk

if class=='fm' then

out = 'FM'

else

out = resolveFQSgrade('file')

end

else

local grade = cfg.quality.ns_to_class[ns] or 'NA'

out = resolveFQSgrade(grade)

end

return out

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

local bubble = function(text, conflict, style)

local out = mw.html.create('span')

:addClass('wpb-header-bubbles')

:addClass(style)

:addClass(conflict and 'conflict' or nil)

:wikitext(text)

return tostring(out)

end

p._main = function(args, raw_args, demo_page, banner_name, inactive)

---------------------------

-- Initialise parameters --

---------------------------

local project = args.PROJECT or '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_page==true and 'article' or require('Module:Pagetype')._main({

page = demo_page,

dab = 'disambiguation page',

sia = 'set index article',

draft = 'draft'

})

local article = pagetype=='article' or pagetype=='set index article'

local rows, nested_ratings, task_forces, notes, categories, taskforce_categories = {}, {}, {}, {}, {}, {}

local add_category = function(category, key)

if category and category~='none' then

table.insert(categories, {category = category, key = key})

end

end

local parse_pt = function(text) -- function to replace _PAGETYPE_ with the actual page type

local ptype = article and 'article' or pagetype -- display "article" for articles otherwise page type

return text and text:gsub('_PAGETYPE_', ptype)

end

for arg_name, arg_value in pairs(args) do

local tf_match = mw.ustring.match(arg_name,'^tf (%d+)$')

local note_match = mw.ustring.match(arg_name,'^note (%d+)$')

if tf_match and yesno(arg_value, true) then

table.insert(task_forces, tf_match)

elseif note_match and yesno(arg_value, true) then

table.insert(notes, note_match)

else

local tf, cat = mw.ustring.match(arg_name,'^tf (%d+) cat (%d+)$')

if tf and yesno(arg_value, true) then

if not taskforce_categories[tf] then -- initialise table

taskforce_categories[tf] = {}

end

table.insert(taskforce_categories[tf], cat)

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 assessment_category = function(cat, name)

if cat then

return cat:gsub(' articles', '') -- remove "articles" from category

else

return name or ''

end

end

local assessment_cat = assessment_category(args.ASSESSMENT_CAT, project)

---------------------------

-- Location warning -------

---------------------------

local warning = ''

if not current_title.isTalkPage and not demo_page then

local text = cfg.namespace_warning.text:format(

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 .. ' ' .. cfg.namespace_warning.on_template_page:format(

parameter_format('BANNER_NAME'),

current_title.prefixedText

)

end

warning = mbox('ombox', {

image = '40px',

type = cfg.namespace_warning.type_,

text = parse_pt(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 = cfg.subst_warning.text:format(

project_name,

'{{'..banner_name.prefixedText..'}}'

)

warning = warning .. mbox('ombox', {

image = '40px',

type = cfg.subst_warning.type_,

text = text,

}) .. cfg.subst_warning.categories

end

---------------------------

-- Primary image/text -----

---------------------------

local primary_image = function(image_name, size)

local cell = mw.html.create('td')

if image_name and image_name~='' then

cell:addClass('mbox-image wpb-image')

:wikitext(image(image_name, size, cfg.image.alt))

else

cell:addClass('mbox-empty-cell')

end

return cell

end

local portal = args.PORTAL

local portal_box = portal and frame:expandTemplate{title='Portal', args={portal}} or ''

local main_text = portal_box .. parse_pt(args.MAIN_TEXT or cfg.main_text:format(

project_link.prefixedText,

project_name,

args.MAIN_ARTICLE and if_exists(args.MAIN_ARTICLE) or if_exists(project, project .. ' articles'),

project_link.talkPageTitle.prefixedText

))

local image_left_size = args.IMAGE_LEFT_SIZE or cfg.image.default_size

local metadata = function(class, data)

return mw.html.create('span')

:addClass(class)

:wikitext(data)

end

local text_cell = mw.html.create('td')

:addClass('mbox-text')

:wikitext(main_text)

:tag('span')

:addClass('metadata wpb-metadata')

:node(metadata('wpb-project', project))

:node(metadata('wpb-project_link', project_link.prefixedText))

:node(metadata('wpb-banner_name', banner_name.prefixedText))

:node(metadata('wpb-assessment_cat', assessment_cat))

:done()

local primary_row = mw.html.create('tr')

:node(primary_image(args.IMAGE_LEFT, image_left_size))

:node(text_cell)

:node(primary_image(args.IMAGE_RIGHT, args.IMAGE_RIGHT_SIZE or cfg.image.default_size))

table.insert(rows, primary_row)

---------------------------

-- Banner shell checks ----

---------------------------

local title = demo_page and demo_page~=true and mw.title.new(demo_page) or current_title

local article_class = p.readarticleclass({ignore_subtemplates=true}, title.prefixedText)

if article_class then -- banner shell exists

local special_chars = '([%%%(%)%.%+%-%*%?%[%]%^%$])'

local banner_name_escaped = banner_name.text

local page_content = require('Module:Wikitext Parsing').PrepareText(title:getContent()) -- get content of current page

local content_without_shell

for capture in mw.ustring.gmatch(page_content, '%b{}') do -- look for possible templates on page

for _, redirect in ipairs(cfg.banner_shell.redirects) do

if mw.ustring.find(capture, '^{{%s*' .. redirect .. '%s*[|}].*}}$') then -- found a banner shell

banner_name_escaped = banner_name_escaped:gsub(special_chars, '%%%1') -- escape each special character

capture = capture:gsub(special_chars, '%%%1')

content_without_shell = mw.ustring.gsub(page_content, capture, '') -- remove banner shell content from page content

end

if content_without_shell then break end

end

if content_without_shell then break end

end

local template_outside_shell

if content_without_shell and mw.ustring.find(content_without_shell, '{{%s*' .. banner_name_escaped .. '%s*[|}]') then -- found banner template outside of the shell

add_category(cfg.banner_shell.category.outside_shell)

end

else -- no banner shell on page

if article then

add_category(cfg.banner_shell.category.no_banner_shell_articles)

elseif title.namespace==3 then --User talk namespace

for _, user in ipairs(cfg.banner_shell.valid_users) do

if string.find(title.rootText, user) then

add_category(cfg.banner_shell.category.no_banner_shell)

end

end

elseif title.namespace~=2 then --User namespace

add_category(cfg.banner_shell.category.no_banner_shell)

end

end

---------------------------

-- 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 check_fallbacks = function(class, category)

if article then -- no fallbacks for articles

return class

else -- check fallbacks for non-article classes

local new_class = class

local category_exists = function(class)

local cat = mw.title.new(cfg.quality.assessment_category:format(class, category .. ' ' .. (article and 'articles' or 'pages')))

return cat.exists and #cat:getContent()>0 -- check if category exists and is not blank

end

if class=='FM' and not category_exists('FM') then

new_class = 'File' -- fall back to File-class if FM category does not exist

end

if not category_exists(new_class) then

new_class = 'NA' -- use NA for non-article pages if category does not exist

end

return new_class

end

end

local class = raw_args.class

if class then -- banner gives quality ratings

article_class = article_class and p.class_mask(article_class, title, false, pagetype, article)

if args.QUALITY_CRITERIA=='custom' then -- project has opted out of standard assessment scale and uses a custom mask

local custom_mask = banner_name:subPageTitle('class')

if custom_mask.exists and #custom_mask:getContent()>1 then

raw_args.demo_page = demo_page -- send demo_page to custom mask

class = mw.text.trim(frame:expandTemplate{

title = custom_mask.prefixedText,

args = raw_args

})

if class== and article_class and article_class~= then -- if unassessed and article class exists, check if it can be inherited

local new_arg_table = {}

for arg, val in pairs(raw_args) do -- construct new argument table to send to custom mask

new_arg_table[arg] = val

end

new_arg_table.class = article_class -- replace class with inherited class

local article_class_normalised = mw.text.trim(frame:expandTemplate{

title = custom_mask.prefixedText,

args = new_arg_table

})

if article_class_normalised and article_class_normalised~='' then

class = article_class_normalised -- inherit class from article_class normalised by custom mask

else

article_class = nil -- effectively no article_class for this banner

end

end

end

else

class = p.class_mask(class, title, true, pagetype, article)

end

local check_redundant = function()

if raw_args.class~='' and args.QUALITY_CRITERIA~='custom' then -- banner has a non-blank class value which is ignored

add_category(cfg.banner_shell.category.redundant_class)

end

end

class = check_fallbacks(class, assessment_cat)

local show = false -- hide quality class in project banner by default

if article_class then -- banner shell exists

if class=='' then -- local class is blank

class = check_fallbacks(article_class, assessment_cat) -- check fallbacks again now that class may have changed

check_redundant()

elseif class==article_class then -- local class matches article class

check_redundant()

elseif article_class=='' then -- local class defined and no article class defined

show = true

add_category(cfg.banner_shell.category.no_quality_rating)

if args.QUALITY_CRITERIA~='custom' then

warning = warning .. display_error(cfg.banner_shell.piqa_warning)

end

elseif article_class=='FM' and args.QUALITY_CRITERIA~='custom' then

class = check_fallbacks('FM', assessment_cat)

check_redundant()

elseif not article and class~='FM' then -- article class and local class are both non-article classes

check_redundant()

elseif args.QUALITY_CRITERIA=='custom' then -- project uses custom criteria and class differs

show = true -- show quality class in project banner

else -- article class exists and differs from local class

show = 'conflict'

add_category(class .. cfg.banner_shell.conflict.category)

end

else -- banner shell does not exist

show = true

end

local category = (class=='' and 'Unassessed' or class..'-Class') .. ' ' .. assessment_cat .. ' ' .. (article and 'articles' or 'pages')

if show then -- quality rating shown in banner

local rating

if article then

rating = class=='' and cfg.quality.not_yet or cfg.quality.rated:format(class)

else

rating = cfg.quality.not_required

end

local scale = args.QUALITY_CRITERIA=='custom'

and assessment_link

and cfg.quality.project_scale:format(wikilink(assessment_link..'#'..lang:ucfirst(cfg.quality.name), cfg.quality.name))

or cfg.quality.default_scale

local quality_rating = show=='conflict'

and cfg.banner_shell.conflict.text

or cfg.quality.rating:format(rating, scale)

local cssClass = 'class-' .. (class=='' and 'unassessed' or class:lower())

local class_row = mw.html.create('tr')

:tag('td')

:addClass('assess')

:addClass(cssClass)

:addClass(show=='conflict' and 'conflict' or nil)

:wikitext(wikilink(':Category:' .. category, class=='' and '???' or class))

:done()

:tag('td')

:addClass('mbox-text')

:attr('colspan', '2')

:wikitext(parse_pt(quality_rating))

:done()

table.insert(rows, class_row)

table.insert(

nested_ratings,

1,

bubble(class=='' and 'Unassessed' or (class..'‑class'), show=='conflict', cssClass)

)

end

add_category(category)

end

if args.HOOK_ASSESS then

table.insert(rows, args.HOOK_ASSESS)

end

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

local b_checklist = require(auxiliary).b_checklist(args, raw_args, class, demo_page, assessment_link)

table.insert(rows, b_checklist)

end

---------------------------

-- Importance assessment --

---------------------------

local importance = importance_mask(raw_args.importance or raw_args.priority, args.IMPORTANCE_SCALE, banner_name, pagetype, class)

local importance_name = args.IMPN or (raw_args.priority and 'priority' or cfg.importance.default_name)

if importance then -- banner gives importance ratings

local category = importance .. '-' .. importance_name .. ' ' .. assessment_cat .. ' ' .. (importance=='NA' and 'pages' or 'articles')

if importance~='NA' then -- display importance rating

local rating = importance=='Unknown' and cfg.importance.not_yet or cfg.importance.rated:format(importance, importance_name)

local scale_name = cfg.importance.scale:format(importance_name)

local scale = assessment_link

and cfg.importance.project_scale:format(assessment_link..'#'..lang:ucfirst(scale_name), scale_name)

or cfg.importance.default_scale

local importance_rating = parse_pt(cfg.importance.rating:format(rating, scale))

local cssClass = 'import-' .. importance:lower()

local importance_row = mw.html.create('tr')

:tag('td')

:addClass('assess')

:addClass(cssClass)

:wikitext(wikilink(':Category:' .. category, importance=='Unknown' and '???' or importance))

:done()

:tag('td')

:addClass('mbox-text')

:attr('colspan', '2')

:wikitext(importance_rating)

:done()

table.insert(rows, importance_row)

if importance~='Unknown' then -- importance is not NA or Unknown

table.insert(

nested_ratings,

bubble(importance .. '‑' .. importance_name, false, cssClass)

)

end

end

add_category(category)

end

page_assessment(project, class, importance)

if args.HOOK_IMPORTANCE then

table.insert(rows, args.HOOK_IMPORTANCE)

end

if args.QII_FORMAT then

add_category(require(auxiliary).quality_importance_insection(args, class, importance, importance_name, assessment_cat, article))

end

---------------------------

-- Collapsing sections ----

---------------------------

local collapse_section = function(collapse, new_rows, header)

if collapse then

local header_row = mw.html.create('tr')

:tag('th')

:attr('colspan','3')

:addClass('wpb-collapsed-head')

:wikitext(header)

:done()

local blank_row = mw.html.create('tr')

:tag('td')

:addClass('mbox-image wpb-gutter')

:css('min-width', image_left_size)

:tag('span')

:addClass('wpb-iefix')

:wikitext('/ ')

:done() --TO FIX IE

:done()

:tag('td'):done()

:tag('td'):done()

local collapsed_rows = mw.html.create('table')

:addClass('mw-collapsible mw-collapsed')

:node(header_row)

:node(blank_row)

for _, row in ipairs(new_rows) do

collapsed_rows:node(row)

end

local collapsed_section = mw.html.create('tr')

:tag('td')

:attr('colspan','3')

:addClass('wpb-collapsed-notes')

:node(collapsed_rows)

:done()

table.insert(rows, collapsed_section)

else

for _, row in ipairs(new_rows) do

table.insert(rows, row)

end

end

end

---------------------------

-- Task forces ------------

---------------------------

local nested_tf, taskforce_output = {}, {}

local tf_default_size = args.TF_SIZE or cfg.task_force.default_size

for _, k in ipairs(task_forces) do

local tf_prefix = 'TF_' .. k .. '_'

local tf_assessment_cat = assessment_category(

args[tf_prefix..'ASSESSMENT_CAT'],

args[tf_prefix..'NAME']

)

if yesno(args[tf_prefix..'QUALITY']) and class then

local tf_class = check_fallbacks(class, tf_assessment_cat)

add_category((tf_class=='' and 'Unassessed' or tf_class..'-Class') .. ' ' .. tf_assessment_cat .. ' ' .. (article and 'articles' or 'pages'))

end

local tf_importance, tf_importance_category

if raw_args['tf '..k..' importance'] then

tf_importance = importance_mask(raw_args['tf '..k..' importance'], args.IMPORTANCE_SCALE, banner_name, pagetype, class)

if tf_importance=='Unknown' and yesno(args.INHERIT_IMPORTANCE) then

tf_importance = importance

end

tf_importance_category = tf_importance .. '-' .. importance_name .. ' ' .. tf_assessment_cat .. ' ' .. (tf_importance=='NA' and 'pages' or 'articles')

add_category(tf_importance_category)

end

if args[tf_prefix .. 'TEXT']~='none' then

local portal = args[tf_prefix..'PORTAL'] and frame:expandTemplate{

title='Portal',

args={args[tf_prefix .. 'PORTAL'], height='15', margin='0'}

} or ''

local text = ''

local tf_text = args[tf_prefix..'TEXT'] or args.TF_TEXT

if tf_text then

text = portal .. tf_text

:gsub('_NAME_', args[tf_prefix .. 'NAME'] or '')

:gsub('_LINK_', args[tf_prefix .. 'LINK'] or '')

:gsub('_IMPORTANCE_', tf_importance or '')

else

local tf_importance_text = tf_importance

and tf_importance~='NA'

and tf_importance~='Unknown'

and ' ' .. cfg.task_force.importance:format(

wikilink(

':Category:' .. tf_importance_category,

tf_importance .. '-' .. importance_name

)

) or ''

text = portal .. cfg.task_force.text:format(

wikilink(args[tf_prefix .. 'LINK'], args[tf_prefix .. 'NAME']),

tf_importance_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(args[tf_prefix .. 'IMAGE'], tf_size, cfg.task_force.icon_alt, 'center')

end

local taskforce = mw.html.create('tr')

:tag('td')

:wikitext(tf_image)

:done()

:tag('td')

:addClass('mbox-text')

:attr('colspan','2')

:wikitext(parse_pt(text))

:done()

table.insert(taskforce_output, taskforce)

end

if args[tf_prefix..'HOOK'] then

table.insert(taskforce_output, args[tf_prefix..'HOOK'])

end

if args[tf_prefix..'QII_FORMAT'] then

add_category(require(auxiliary).quality_importance_insection(args, class, tf_importance, importance_name, tf_assessment_cat, article, tf_prefix))

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

for _, c in ipairs(taskforce_categories[k] or {}) do-- add additional taskforce categories

add_category(args[tf_prefix..'CAT_'..c])

end

end

if args.HOOK_TF then

table.insert(taskforce_output, args.HOOK_TF)

end

local threshold = tonumber(args.TF_COLLAPSE) or (args.TF_HEADER and cfg.task_force.lower_threshold) or cfg.task_force.upper_threshold

collapse_section(

#taskforce_output > threshold,

taskforce_output,

args.TF_HEADER or cfg.task_force.header

)

---------------------------

-- Notes ------------------

---------------------------

local note_output = {}

local note_default_size = args.NOTE_SIZE or args.NOTE_1_SIZE or cfg.note.default_size

local render_note = function(note_args)--text, image_name, size, category, sort_prefix

local sort = note_args.sort_prefix and note_args.sort_prefix .. current_title.text

add_category(note_args.category, sort)

add_category(note_args.category2, sort)

if note_args.text then

local note_image = image(

note_args.image_name,

note_args.size or note_default_size,

cfg.note.icon_alt,

'center'

)

local new_note = mw.html.create('tr')

:tag('td')

:css('background', note_args.background)

:wikitext(note_image)

:done()

:tag('td')

:addClass('mbox-text')

:attr('colspan', '2')

:wikitext(parse_pt(note_args.text))

:done()

table.insert(note_output, new_note)

if note_image then

local icon = mw.html.create('span')

:addClass('wpb-header-bubbles')

:wikitext('File:' .. note_args.image_name .. '')

table.insert(nested_ratings, tostring(icon))

end

end

end

local auto = false

local auto_arg = args.auto and args.auto:lower()

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 cfg.auto.default_cat:format(project)

local auto_text = cfg.auto.assessed:format(

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

render_note{

text = auto_text,

image_name = cfg.auto.icon,

category = auto_cat,

sort_prefix = sort_prefix

}

end

if yesno(args.attention, true) then

local attention_cat = args.ATTENTION_CAT or cfg.attention.default_cat:format(project)

render_note{

text = cfg.attention.text,

image_name = cfg.attention.icon,

category = attention_cat

}

end

if yesno(args.infobox, true) then

local infobox_cat = args.INFOBOX_CAT or cfg.infobox.default_cat:format(project)

render_note{

text = cfg.infobox.text,

image_name = cfg.infobox.icon,

category = infobox_cat

}

end

for _, k in ipairs(notes) do

local note_prefix = 'NOTE_' .. k .. '_'

render_note{

text = parse_pt(args[note_prefix..'TEXT']),

image_name = args[note_prefix..'IMAGE'],

size = args[note_prefix..'SIZE'],

category = args[note_prefix..'CAT']

}

end

if yesno(args['image-needed'], true) then

local image_needed_args = require(auxiliary).image_needed(args)

render_note(image_needed_args)

end

if yesno(args['collaboration-candidate'], true) or yesno(args['collaboration-current'], true) or yesno(args['collaboration-past'], true) then

local collaboration_args = require(auxiliary).collaboration(args, current_title)

render_note(collaboration_args.candidate)

render_note(collaboration_args.current)

render_note(collaboration_args.past)

end

if yesno(args['a class'], true) then

local a_class_args = require(auxiliary).a_class(args, lang)

render_note(a_class_args)

end

if yesno(args['peer review'], true) or yesno(args['old peer review'], true) then

local peer_review_args = require(auxiliary).peer_review(args, current_title)

render_note(peer_review_args.current)

render_note(peer_review_args.past)

end

local note_count = #note_output

if args.HOOK_NOTE then

table.insert(note_output, args.HOOK_NOTE)

local hook_collapsed = 0

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

collapse_section(

note_count > (tonumber(args.COLLAPSED) or cfg.note.threshold),

note_output,

args.COLLAPSED_HEAD or cfg.note.header

)

---------------------------

-- Bottom text ------------

---------------------------

if args.HOOK_BOTTOM then

table.insert(rows, args.HOOK_BOTTOM)

end

if args.TODO_LINK or args.TODO_TEXT then

local todolist = require(auxiliary).todo_list(args, frame)

table.insert(rows, todolist)

end

if args.BOTTOM_TEXT then

local bottom_text = mw.html.create('tr')

:tag('td')

:attr('colspan','3')

:wikitext(parse_pt(args.BOTTOM_TEXT))

:done()

table.insert(rows, bottom_text)

end

if args.MAIN_CAT then

add_category(args.MAIN_CAT)

end

---------------------------

-- Nested display ---------

---------------------------

if args.HOOK_NESTED then

local hook_nested = args.HOOK_NESTED:gsub('^ / ', '') -- remove initial slash, will be added later

table.insert(nested_tf, hook_nested)

end

local nested_tf_str = ''

if #nested_tf>0 then

nested_tf_str = tostring(mw.html.create('span')

:addClass('wpb-nested-task-force')

:wikitext(': ' .. table.concat(nested_tf, ' / '))

)

end

local nested_ratings_str = #nested_ratings>0 and table.concat(nested_ratings, ' ') or ''

if args.HOOK_NESTED_ASSESS then

nested_ratings_str = nested_ratings_str .. tostring(mw.html.create('span')

:addClass('wpb-header-bubbles')

:wikitext(args.HOOK_NESTED_ASSESS)

)

end

local header_row = mw.html.create('tr')

:addClass('wpb-header')

:tag('td')

:addClass('wpb-header-icon')

:wikitext(image(args.IMAGE_LEFT, cfg.image.header_size, cfg.image.alt))

:done()

:tag('td')

:addClass('wpb-header-combined')

:wikitext(wikilink(project_link.prefixedText, project) .. nested_tf_str .. ' ' .. nested_ratings_str)

:done()

---------------------------

-- Prepare categories -----

---------------------------

local categories_formatted = ''

if demo_page and demo_page~=true then -- for testing purposes

local category_list = mw.html.create('ul')

for _, cat in ipairs(categories) do

local item = mw.html.create('li')

:wikitext(wikilink(':Category:' .. cat.category, cat.category))

category_list:node(item)

end

local category_box = mw.html.create('div')

:addClass('wpb-category-box')

:wikitext('Categories:')

:node(category_list)

categories_formatted = tostring(category_box)

elseif not demo_page then

local categories_linked = {}

for _, cat in ipairs(categories) do

local cat_link = wikilink('Category:' .. cat.category, cat.key)

table.insert(categories_linked, cat_link)

end

categories_formatted = table.concat(categories_linked)

end

---------------------------

-- Make banner ------------

---------------------------

local banner_rows = mw.html.create('table')

for _, row in ipairs(rows) do

banner_rows:node(row)

end

local banner = mw.html.create('table')

:addClass('tmbox tmbox-notice mw-collapsible innercollapse wpb wpb-table')

:addClass(inactive and cfg.inactive.class or nil)

:node(header_row)

:tag('tr')

:tag('td')

:addClass('mbox-text wpb-main')

:attr('colspan','2')

:node(banner_rows)

:allDone()

local tstyle = frame:extensionTag('templatestyles', '', {src='Module:Message box/tmbox.css'}) ..

frame:extensionTag ('templatestyles', , {src = 'Module:WikiProject banner' .. (sandbox or ) .. '/styles.css'})

return warning .. tstyle .. tostring(banner) .. categories_formatted, note_count, #taskforce_output, assessment_link

end

local initialise = function(args, raw_args, inactive_status)

---------------------------

-- Initialise arguments ---

---------------------------

local parent_args = args_module.getArgs(frame, {parentOnly = true})

local category = parent_args.category or args.category or true

local demo_page = parent_args.demo_page

local on_template_page = false

local banner_name = mw.title.new(args.BANNER_NAME or 'Template:WikiProject ' .. (args.PROJECT or 'PROJECT'))

if not demo_page then

if yesno(category, true) then

on_template_page = current_title.rootPageTitle==banner_name.rootPageTitle

else

demo_page = true

end

end

local project_name = args.PROJECT_NAME or 'WikiProject ' .. (args.PROJECT or 'PROJECT')

local unknown_parameters = ''

if banner_name.exists and not demo_page then -- check for unknown parameters

local parameters = {}

for parameter in banner_name:getContent():gmatch('{{{([^|}]+)') do

table.insert(parameters, parameter)

end

parameters.showblankpositional = "1"

local check_for_unknown = require('Module:Check for unknown parameters')._check

local unknowns = check_for_unknown(parameters, parent_args)

if unknowns and unknowns~='' then -- there are some unknown parameters

parameters.preview = cfg.unknown_parameters.preview:format(wikilink(banner_name.fullText))

local unknown_category = cfg.unknown_parameters.tracking:format(project_name)

if not mw.title.new(unknown_category).exists then

unknown_category = cfg.unknown_parameters.default

end

parameters.unknown = unknown_category and '_VALUE_' or ''

unknown_parameters = check_for_unknown(parameters, parent_args)

end

end

if on_template_page then

local templatepage = require('Module:WikiProject banner/templatepage' .. (sandbox or '')).templatepage

return templatepage(args, raw_args, inactive_status)

else

return unknown_parameters

.. p._main(args, raw_args, demo_page, banner_name, inactive_status and true or false), nil -- nil to disregard subsequent returned values

end

end

p.main = function(frame)

local args = args_module.getArgs(frame, {frameOnly = true})

local raw_args = args_module.getArgs(frame, {frameOnly = true, removeBlanks = false})

return initialise(args, raw_args)

end

---------------------------

-- Inactive projects ------

---------------------------

p.inactive = function(frame)

local args = args_module.getArgs(frame, {frameOnly = true})

local project_name = args.PROJECT_NAME or 'WikiProject ' .. (args.PROJECT or 'PROJECT')

local project_link = mw.title.new(args.PROJECT_LINK or 'Wikipedia:' .. project_name)

local _status = cfg.inactive.status[args.PROJECT_STATUS] or cfg.inactive.default

local main_text = cfg.inactive.text:format(

project_link.prefixedText,

project_name,

_status

)

return initialise(

{

PROJECT = args.PROJECT,

BANNER_NAME = args.BANNER_NAME,

IMAGE_LEFT = cfg.inactive.image,

IMAGE_LEFT_SIZE = cfg.inactive.image_size,

MAIN_TEXT = main_text,

HOOK_NESTED_ASSESS = ' ' .. cfg.inactive.nested:format(_status),

substcheck = args.substcheck,

category = args.category

}, {

substcheck = '' -- to prevent warning on templatepage

}, _status

)

end

return p