Introducing: Representatives. swedishwin.com, chud.win, and soyzellig.party positions are vacant.


The new Community missions system is now live! Where anyone can propose a task, discuss nominations, or join the current mission. Community missions give editors a place to turn requests from the Janny noticeboard into focused projects. No mission can be too small

Module:News/contents

From Soyjak Wiki, the free ensoyclopedia
Jump to navigationJump to search

This module contains self-reading tools for the newspaper issues. Both entry points will read the current page's wikitext live and in preview.

Entry points

  • {{#invoke:News/contents|contents|mode=|level=}} - the "In this issue" list automatically generated by this module by counting (default 2, == == ) heading levels in page order. The counts are automatically linked to its section, under the contents label from config. Links, bold, italic marks are removed from heading text. mode sets the class, by default the stylesheet is expecting it to be topbar.
  • {{#invoke:News/contents|check}} - a "hidden" sanity checker, called by the issue nameplate. It silently counts to check for errors. If any are found, it will report them in red under the folio:
    • unknown parameters on any News/... template on the page.
    • Unbalanced News/row and News/box pairs, when the number of openers and enders differ.

Any problem also adds the error category from the config.


local p = {}

local News = require('Module:News')
local parser = require('Module:WikitextParser')

local openers = { ['News/row'] = true, ['News/box'] = true }
local closers = { ['News/row end'] = 'News/row', ['News/box end'] = 'News/box' }

local function clean(text)
	text = text:gsub('%[%[[^%]|]*|([^%]]*)%]%]', '%1')
	text = text:gsub('%[%[([^%]]*)%]%]', '%1')
	text = text:gsub("'''", ''):gsub("''", '')
	return mw.text.trim(text)
end

function p.contents(frame)
	local marks = string.rep('=', tonumber(frame.args.level) or 2)
	local text = mw.title.getCurrentTitle():getContent() or ''
	local state = News.newState()
	local items = {}
	for line in (text .. '\n'):gmatch('([^\n]*)\n') do
		local inner = line:match('^' .. marks .. '([^=].-)' .. marks .. '%s*$')
		if inner then
			local heading = clean(inner)
			table.insert(items, '* [[#' .. heading .. '|' .. heading .. ']]')
		end
	end
	if #items == 0 then return '' end
	return News.finish(
		state,
		'<div class="sn-news-contents sn-news-contents-'
			.. frame.args.mode
			.. '"><div class="sn-news-contents-heading">'
			.. News.text(state, 'contents')
			.. '</div>\n'
			.. table.concat(items, '\n')
			.. '\n</div>'
	)
end
-- part 1: Check for unknown parameters
local function unknown(state, name, args)
	local title = mw.title.new(name, 10)
	local source = title and title:getContent()
	if not source then return {} end
	local known = {}
	for param in source:gmatch('{{{%s*([^|}]-)%s*[|}]') do
		known[param] = true
	end
	if next(known) == nil then return {} end
	local names = {}
	for arg in pairs(args) do
		arg = mw.text.trim(tostring(arg))
		if not known[arg] then table.insert(names, arg) end
	end
	table.sort(names)
	local out = {}
	for _, arg in ipairs(names) do
		table.insert(out, News.errorText(state, 'unknownParam', { name = arg, template = name }))
	end
	return out
end

local function headings(text)
	local list = {}
	for pos, line in (text .. '\n'):gmatch('()([^\n]*)\n') do
		local inner = line:match('^==+([^=].-)==+%s*$')
		if inner then table.insert(list, { pos = pos, text = clean(inner) }) end
	end
	return list
end

local function place(under)
	if under then return News.fill(News.need('messages').placeUnder, { heading = under }) end
	return News.need('messages').placeTop
end
-- part2: check for opening / closing misbalance
function p.check(frame)
	local text = mw.title.getCurrentTitle():getContent() or ''
	local lang = mw.language.getContentLanguage()
	local state = News.newState()
	local heads = headings(text)
	local h = 0
	local stack, out = {}, {}
	for pos, template in text:gmatch('()({%b{}})') do
		if template:sub(1, 3) ~= '{{#' then
			while heads[h + 1] and heads[h + 1].pos < pos do
				h = h + 1
			end
			local under = heads[h] and heads[h].text
			local name = lang:ucfirst((mw.text.trim(parser.getTemplateName(template) or ''):gsub('_', ' ')))
			if openers[name] then
				table.insert(stack, { name = name, under = under })
			elseif closers[name] then
				local match
				for i = #stack, 1, -1 do
					if stack[i].name == closers[name] then
						match = i
						break
					end
				end
				if not match then
					table.insert(out, News.errorText(state, 'unopened', { name = name:sub(6), place = place(under) }))
				else
					if match < #stack then
						local top = stack[#stack]
						table.insert(
							out,
							News.errorText(state, 'crossed', {
								name = name:sub(6),
								place = place(under),
								open = top.name:sub(6),
								where = place(top.under),
							})
						)
					end
					for i = #stack, match, -1 do
						table.remove(stack, i)
					end
				end
			end
			if name:sub(1, 5) == 'News/' then
				for _, message in ipairs(unknown(state, name, parser.getTemplateParameters(template))) do
					table.insert(out, message)
				end
			end
		end
	end
	for _, item in ipairs(stack) do
		table.insert(out, News.errorText(state, 'unclosed', { name = item.name:sub(6), place = place(item.under) }))
	end
	return News.finish(state, table.concat(out, ' '))
end

return p