Module:Header
Jump to navigation
Jump to search
This module depends on the following other modules: |
This module is used by a number of modules and templates, chiefly {{header}}. Check test results at Template:Header/testcases and Template:Translation header/testcases.
--[=[
This is a module to implement logic for [[Template:Header]] and [[Template:Translation header]]
TODO:
- centuries are defined as starting on XX01, but WS categorizes them as starting on XX00
-- check whether that's a considered policy choice
]=]
require('strict')
local p = {} --p stands for package
local yesno = require('Module:Yesno')
local getArgs = require('Module:Arguments').getArgs
local TableTools = require('Module:TableTools')
local ISO_639_language_name = require('Module:ISO 639').language_name
local parent_links = require('Module:Auto parents')._parent_links
local construct_header = require('Module:Header structure').construct_header
local headerAttributions = require('Module:Header/attribution')
local construct_defaultsort = require('Module:Header/sort')._construct_defaultsort
local construct_year = require('Module:Header/year').construct_year
local current_title = mw.title.getCurrentTitle()
--[=[
Wrap stylesheet in noexport div
]=]
local function get_noexport_stylesheet(template)
return tostring(mw.html.create('div'):addClass('ws-noexport'):wikitext(mw.getCurrentFrame():extensionTag('templatestyles', '', {src = template .. '/styles.css'})))
end
--[=[
Get badge if any
]=]
local function badge()
return require('Module:Edition').badge({args = {category = '1', indicator = '1'}})
end
--[=[
Detect explicit formatting in fields like 'section' and 'title'
]=]
local function explicit_formatting(str)
return str and (string.match(str, "\'\'\'?") or string.match(str, '<%s*/?%s*[iIbB]%s*>'))
-- add more cases here or come up with a less silly way to do things
end
local function check_non_existent_author_pages(args, categories, checkArgs)
-- check for cases that aren't supposed to produce a valid link
local param = checkArgs.param
local tracking_cat = checkArgs.tracking_cat or 'Works with non-existent author pages'
if not param or not args[param] or yesno(args[param .. '-nolink']) then
return
end
local lower_arg = string.lower(args[param])
local attr_data = headerAttributions.attr_data[param] or headerAttributions.attr_data[string.gsub(param, 'section%-', '')]
if attr_data and attr_data['special_cases'] and attr_data['special_cases'][lower_arg] then
return
end
-- check if page exists
local target = mw.title.makeTitle('Author', args[param])
-- expensive function!
if not target or not target.exists then
table.insert(categories, tracking_cat)
end
return
end
--[=[
Construct the automatic categories for the header
]=]
local function language_category_name(cat_works_start, lang)
local cat_language_name = ISO_639_language_name(lang) or 'an undefined language'
if lang == 'el' then
cat_language_name = 'Modern Greek'
end
return cat_works_start .. ' ' .. cat_language_name
end
local function construct_categories(args, argsWithBlanks)
local categories = {}
-- categorize subpages
local title = current_title
local parent_exists = false
while title.isSubpage and not parent_exists do
title = mw.title.new(title.baseText, title.nsText)
parent_exists = title.exists
end
if parent_exists and title:inNamespaces(0) then
table.insert(categories, 'Subpages')
elseif parent_exists then
table.insert(categories, title.nsText .. ' subpages')
end
-- add categories from the categories parameter
local manual_categories = (args.categories and mw.text.split(args.categories, '%s*/%s*', false)) or {}
local using_cat = false
for i, category in ipairs(manual_categories) do
local cat = mw.text.trim(category)
if cat ~= '' then
table.insert(categories, cat)
using_cat = true
end
end
if using_cat then
table.insert(categories, 'Works using categories parameter')
end
local known_override_types = {
['default'] = 'contributor type',
['author'] = 'author',
['translator'] = 'translator',
['section-author'] = 'contributor'
}
for k, v in pairs(argsWithBlanks) do
-- Check for numerical parameters (which shouldn't be used)
if type(k) == 'number' then
table.insert(categories, 'Headers with numerical arguments')
-- Check for 'override-' parameters
elseif string.match(k, '^override%-') then
local contrib_type = mw.text.split(k, '-', true)
contrib_type = string.gsub(contrib_type[2], '%d*$', '')
table.insert(categories, 'Pages with override ' .. (known_override_types[contrib_type] or known_override_types['default']))
end
end
-- check contributor parameters
if args['section-author'] then
table.insert(categories, 'Pages with contributor')
end
local editor = args['override-editor'] or args['editor']
if editor and not args['nocat'] then
editor = string.lower(editor)
if editor == 'unknown' or editor == '?' then
table.insert(categories, 'Works with unknown editors')
elseif editor == 'not mentioned' then
table.insert(categories, 'Works with unmentioned editors')
end
end
local translator = args['override-translator'] or args['translator']
if translator and not args['nocat'] then
translator = string.lower(translator)
if translator == 'unknown' or translator == 'not mentioned' or translator == '?' then
table.insert(categories, 'Translations without translator information specified')
end
end
local author = args['override-author'] or args['author']
if author and (string.lower(author) == 'unknown') and not args['nocat'] then
if args.template_name == 'Translation header' then
table.insert(categories, 'Translations of anonymous works')
else
table.insert(categories, 'Anonymous texts')
end
end
-- check for non-existent contributor pages
if current_title:inNamespaces(0, 114) or args.testing then
local params_to_check = {
{param = 'author'},
{param = 'editor'},
{param = 'translator'},
{param = 'composer', tracking_cat = 'Works with non-existent composer pages'},
{param = 'illustrator', tracking_cat = 'Works with non-existent illustrator pages'}
}
for k, v in pairs(params_to_check) do
check_non_existent_author_pages(args, categories, v)
check_non_existent_author_pages(args, categories, {param = 'section-' .. v.param, tracking_cat = v.tracking_cat})
end
end
if args['shortcut'] then
if current_title:inNamespaces(0) then
table.insert(categories, 'Mainspace pages with shortcuts')
elseif current_title:inNamespaces(114) then
table.insert(categories, 'Translation namespace pages with shortcuts')
end
end
if args['noyear'] then
table.insert(categories, 'Pages with noyear')
end
if args['noyearcat'] then
table.insert(categories, 'Pages with noyearcat')
end
if args['nolanguagecat'] then
table.insert(categories, 'Pages with nolanguagecat')
end
if args['cover'] then
table.insert(categories, 'Pages with an export cover')
end
-- sanity/maintenance checks on various parameters
-- allow-explicit-formatting parameter suppresses this check
-- used by, for example, [[Template:Versions]]
if not args['allow-explicit-formatting'] and (explicit_formatting(args['title']) or explicit_formatting(args['section'])) then
table.insert(categories, 'Pages with explicit formatting in header fields')
end
-- translation header categories
local isMainPage = ((current_title:inNamespaces(0, 114) and not current_title.isSubpage) or args['testing'])
if not args['nocat'] and isMainPage then
local cat_works_start = 'Works originally in'
local cat_translations_start = 'Translations'
if args.template_name == 'Translation header' then
cat_works_start = 'Wikisource translations of works in'
cat_translations_start = 'Wikisource translations'
end
if not args['nolanguagecat'] then
if args['languages'] then
for i, lang in ipairs(args['languages']) do
table.insert(categories, language_category_name(cat_works_start, lang))
end
end
if args['interwiki-prefix'] then
table.insert(categories, language_category_name(cat_works_start, args['interwiki-prefix']))
end
end
if not args['languages'] and args['language-required'] then
table.insert(categories, cat_translations_start .. ' with no original language')
end
if args.template_name == 'Translation header' and not args.original then
table.insert(categories, 'Wikisource translations with no original source')
end
end
-- detect inappropriate template use
--[=[
if (args['template-name'] ~= 'Translation header' and translator and string.lower(translator) == 'wikisource')
or (current_title:inNamespaces(114) and args['template-name'] ~= 'Translation header') then
-- tracking category for pages that should be using translation header?
end
if current_title:inNamespaces(0) and args['template-name'] == 'Translation header' then
-- tracking category for translation header in mainspace?
end
]=]
categories = TableTools.removeDuplicates(categories)
local category_links = {}
for k, v in pairs(categories) do
table.insert(category_links, '[[Category:' .. v .. ']]')
end
return table.concat(category_links)
end
--[=[
For debugging
]=]
--[=[
function p.construct_categories(args)
return construct_categories(args, args)
end
]=]
--[=[
Assemble the title
]=]
local function header_title(args)
local title = args.title or ''
local titleSpan = tostring(mw.html.create('span'):attr('id', 'header-title-text'):wikitext(title))
local year = construct_year(args)
local attr = headerAttributions.construct_attributions(args)
local section = headerAttributions.construct_section(args)
if attr ~= '' and title ~= '' then
attr = tostring(mw.html.create('br'):attr('id', 'header-title-break')) .. attr
end
return table.concat({titleSpan, year, attr, section})
end
local function get_languages(args, prefix)
prefix = (prefix and prefix .. '%-') or ''
-- language handling
local languages = {}
for k, v in pairs(args) do
local n
local nText = string.match(k, '^' .. prefix .. 'language%d*$')
if nText then
n = string.gsub(nText, 'language(%d*)$', '%1')
n = tonumber(n) or 1
languages[n] = v
end
end
languages = TableTools.compressSparseArray(languages)
if #languages == 0 then
return {}
end
local language_name
local language_names = {}
for i, lang in ipairs(languages) do
local name = ISO_639_language_name(lang)
if name then
table.insert(language_names, name)
end
end
if #language_names == 1 then
language_name = language_names[1]
elseif #language_names > 1 then
language_name = table.concat(language_names, ', ', 1, #language_names - 1) .. ' and ' .. language_names[#language_names]
end
return {
languages = languages,
language_name = language_name
}
end
--[=[
[[Template:Header]]
]=]
function p._header(args, argsWithBlanks)
argsWithBlanks = argsWithBlanks or args
-- aliases
local dup_cat
local newArgs = {}
local aliases = {
['section-author'] = 'contributor',
['section-translator'] = 'contributing%-translator'
}
for k, v in pairs(args) do
local newkey = string.lower(string.gsub(string.gsub(tostring(k), '_', '-'), ' ', '-'))
for arg, alias in pairs(aliases) do
newkey = string.gsub(newkey, alias, arg)
end
if newkey ~= tostring(k) then
if argsWithBlanks[newkey] then
dup_cat = 'Pages using duplicate arguments in template calls'
end
if not args[newkey] then
newArgs[newkey] = newArgs[newkey] or v
end
end
if newkey == 'testing' or newkey == 'nocat' or newkey == 'nolanguagecat' or newkey == 'language-required' or string.match(newkey, '%-nolink$') then
newArgs[newkey] = newArgs[newkey] or yesno(v)
end
end
for k, v in pairs(newArgs) do
args[k] = v
argsWithBlanks[k] = v
end
if dup_cat then
table.insert(categories, dup_cat)
end
args.sortkey = args.defaultsort or args.sortkey
-- default values
args.template_name = args.template_name or 'Header'
if args.testing == nil then
args.testing = current_title.fullText == 'Template:Header/testcases' or current_title.fullText == 'Template:Translation header/testcases'
end
-- noyearcat has different behavior for nil and false
args.noyearcat = args.nocat == true or nil
local language_res = get_languages(args)
args.languages = language_res.languages
args.language_name = language_res.language_name
local section_language_res = get_languages(args, 'section')
args.section_languages = section_language_res.languages
args.section_language_name = section_language_res.language_name
if not args['interwiki-prefix'] and args['languages'] then
if #(args['languages']) > 1 then
args['interwiki-prefix'] = 'mul'
else
args['interwiki-prefix'] = args['languages'][1]
end
end
-- add values to argsWithBlanks
for k, v in pairs(args) do
if not argsWithBlanks[k] then
argsWithBlanks[k] = v
end
end
-- default values for title and section (allow override by setting to blank)
if not argsWithBlanks['title'] then
args['title'] = parent_links({})
argsWithBlanks['title'] = args['title']
end
if not argsWithBlanks['section'] and current_title.isSubpage then
args['section'] = current_title.subpageText
argsWithBlanks['section'] = args['section']
end
-- header args
args.pre_container = badge()
args.header_class = 'wst-header ws-header ws-noexport noprint dynlayout-exempt ' .. (args.header_class or '')
args.main_class = 'headertemplate'
-- title
args.main_title = header_title(args)
-- FIXME: just use Wikidata instead of interwiki links?
local interwiki = ''
if args.template_name == 'Translation header' and args['interwiki-prefix'] then
interwiki = tostring(mw.html.create('span'):addClass('interwiki-info'):attr('id', args['interwiki-prefix']):attr('title', '(original)'))
if args.original and (args['interwiki-prefix'] == 'ang' or args['interwiki-prefix'] == 'enm' or args['interwiki-prefix'] == 'sco') then
-- cycle to mul.ws and back around to en.ws
interwiki = interwiki .. '[[' .. args['interwiki-prefix'] .. ':en:' .. args.original .. ']]'
elseif args.original then
-- general interwiki link
interwiki = interwiki .. '[[' .. args['interwiki-prefix'] .. ':' .. args.original .. ']]'
end
end
-- set defaultsort tracking categories
args.equalsortcat = '[[Category:' .. 'Headers with DefaultSort equal to page title' .. ']]'
args.diffsortcat = '[[Category:' .. 'Headers applying DefaultSort key' .. ']]'
args.post_notes = table.concat({
headerAttributions.construct_microformat(args),
construct_categories(args, argsWithBlanks),
construct_defaultsort(args),
interwiki
})
return get_noexport_stylesheet('Header') .. construct_header(args)
end
function p.header(frame)
return p._header(
getArgs(frame),
getArgs(frame, {removeBlanks = false})
)
end
--[=[
[[Template:Translation header]]
]=]
function p._translation_header(args, argsWithBlanks)
argsWithBlanks = argsWithBlanks or args
args.header_class = 'wst-translation-header'
args.template_name = 'Translation header'
args.notes_class = 'header-notes'
args['language-required'] = true
return get_noexport_stylesheet('Translation header') .. p._header(args, argsWithBlanks)
end
function p.translation_header(frame)
return p._translation_header(
getArgs(frame),
getArgs(frame, {removeBlanks = false})
)
end
return p