Module:Header: Difference between revisions
Jump to navigation
Jump to search
Content deleted Content added
only check for language name if language is provided |
add option to require a language (use for translations) |
||
(30 intermediate revisions by 3 users not shown) | |||
Line 17: | Line 17: | ||
local parent_links = require('Module:Auto parents')._parent_links |
local parent_links = require('Module:Auto parents')._parent_links |
||
local |
local construct_header = require('Module:Header structure').construct_header |
||
local headerAttributions = require('Module:Header/attribution') |
local headerAttributions = require('Module:Header/attribution') |
||
local construct_defaultsort = require('Module:Header/sort')._construct_defaultsort |
local construct_defaultsort = require('Module:Header/sort')._construct_defaultsort |
||
local construct_year = require('Module:Header/year').construct_year |
|||
local current_title = mw.title.getCurrentTitle() |
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 |
|||
--[=[ |
--[=[ |
||
Line 75: | Line 83: | ||
end |
end |
||
local year = |
local year = args['year'] |
||
if year then |
if year then |
||
mf['ws-year'] = year |
mf['ws-year'] = year |
||
Line 100: | Line 108: | ||
end |
end |
||
local function check_non_existent_author_pages(args, |
local function check_non_existent_author_pages(args, categories, checkArgs) |
||
-- check for cases that aren't supposed to produce a valid link |
|||
if args[param] then |
|||
-- some pages expect an invalid author |
|||
local param = checkArgs.param |
|||
local tracking_cat = checkArgs.tracking_cat or 'Works with non-existent author pages' |
|||
local attr_data = headerAttributions.attr_data[param] or headerAttributions.attr_data[string.gsub(param, 'section-', '')] |
|||
if not attr_data or not attr_data['special_cases'][lower_arg] then |
|||
if not param or not args[param] or yesno(args[param .. '-nolink']) then |
|||
local target = mw.title.makeTitle("Author", args[param]) |
|||
return |
|||
-- expensive function! |
|||
if not target or not target.exists then |
|||
table.insert(categories, "Works with non-existent author pages") |
|||
end |
|||
end |
|||
end |
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 |
end |
||
Line 123: | Line 142: | ||
if args['override-author'] then |
if args['override-author'] then |
||
table.insert(categories, "Pages with override author") |
table.insert(categories, "Pages with override author") |
||
end |
|||
if args['override-translator'] then |
|||
table.insert(categories, "Pages with override translator") |
|||
end |
end |
||
if current_title:inNamespaces(0, 114) or args.testing then |
if current_title:inNamespaces(0, 114) or args.testing then |
||
local params_to_check = { |
|||
check_non_existent_author_pages(args, 'author', categories) |
|||
{param = 'author'}, |
|||
check_non_existent_author_pages(args, 'editor', categories) |
|||
{param = 'editor'}, |
|||
check_non_existent_author_pages(args, 'translator', categories) |
|||
{param = 'translator'}, |
|||
{param = 'composer', tracking_cat = 'Works with non-existent composer pages'}, |
|||
check_non_existent_author_pages(args, 'section-author', categories) |
|||
{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 |
end |
||
Line 141: | Line 170: | ||
local author = args['override-author'] or args['author'] |
local author = args['override-author'] or args['author'] |
||
if author and (string.lower(author) == 'unknown') then |
if author and (string.lower(author) == 'unknown') and not args['nocat'] then |
||
if args.template_name == 'Translation header' then |
if args.template_name == 'Translation header' then |
||
table.insert(categories, 'Translations of anonymous works') |
table.insert(categories, 'Translations of anonymous works') |
||
Line 150: | Line 179: | ||
local editor = args['override-editor'] or args['editor'] |
local editor = args['override-editor'] or args['editor'] |
||
if editor then |
if editor and not args['nocat'] then |
||
editor = string.lower(editor) |
editor = string.lower(editor) |
||
if editor == 'unknown' or editor == '?' then |
if editor == 'unknown' or editor == '?' then |
||
Line 160: | Line 189: | ||
local translator = args['override-translator'] or args['translator'] |
local translator = args['override-translator'] or args['translator'] |
||
if translator then |
if translator and not args['nocat'] then |
||
translator = string.lower(translator) |
translator = string.lower(translator) |
||
if translator == 'unknown' or translator == 'not mentioned' or translator == '?' then |
if translator == 'unknown' or translator == 'not mentioned' or translator == '?' then |
||
Line 175: | Line 204: | ||
end |
end |
||
if args['override-year'] then |
|||
table.insert(categories, "Pages with override year") |
|||
end |
|||
if args['noyear'] then |
if args['noyear'] then |
||
table.insert(categories, "Pages with noyear") |
table.insert(categories, "Pages with noyear") |
||
Line 203: | Line 229: | ||
-- translation header categories |
-- translation header categories |
||
local isMainPage = ((current_title:inNamespaces(0, 114) and not current_title.isSubpage) or args['testing']) |
|||
if args.template_name == 'Translation header' then |
|||
if args.template_name == 'Translation header' and isMainPage then |
|||
if args.language then |
|||
if not args['nocat'] then |
|||
table.insert(categories, 'Works originally in ' .. (args.language_name or 'an undefined language')) |
|||
if args.language then |
|||
else |
|||
table.insert(categories, 'Wikisource translations |
table.insert(categories, 'Wikisource translations of works in ' .. (args.language_name or 'an undefined language')) |
||
else |
|||
table.insert(categories, 'Wikisource translations with no original language') |
|||
end |
|||
end |
end |
||
Line 213: | Line 242: | ||
table.insert(categories, 'Wikisource translations with no original source') |
table.insert(categories, 'Wikisource translations with no original source') |
||
end |
end |
||
elseif not args['nocat'] and isMainPage then |
|||
if |
if args.language then |
||
table.insert(categories, ' |
table.insert(categories, 'Works originally in ' .. (args.language_name or 'an undefined language')) |
||
elseif args['language-required'] then |
|||
table.insert(categories, 'Translations with no original language') |
|||
end |
end |
||
end |
end |
||
Line 285: | Line 316: | ||
--[=[ |
--[=[ |
||
Assemble the title |
|||
]=] |
|||
local function |
local function header_title(args) |
||
local title = args.title or '' |
|||
-- example timestamps: +2016-10-05T00:00:00Z, -1752-00-00T00:00:00Z |
|||
local |
local titleSpan = tostring(mw.html.create('span'):attr('id', 'header-title-text'):wikitext(title)) |
||
local year |
|||
local bce = '' |
|||
if split[1] == '' then |
|||
year = tonumber(split[2]) |
|||
bce = ' BCE' |
|||
else |
|||
year = tonumber(split[1]) |
|||
end |
|||
return {year = year, bce = bce} |
|||
end |
|||
local function formatYear(year, precision) |
|||
if precision == 7 then |
|||
local suffixes = { |
|||
[1] = 'st', |
|||
[2] = 'nd', |
|||
[3] = 'rd', |
|||
default = 'th' |
|||
} |
|||
local century = (year - year % 100)/100 + 1 |
|||
return century .. (suffixes[century] or suffixes['default']) .. ' century' |
|||
elseif precision == 8 then |
|||
return year .. 's' |
|||
else |
|||
return year |
|||
end |
|||
end |
|||
local function getYearFromSingleStatement(statement, args) |
|||
local snak = statement.mainsnak |
|||
if not snak or not snak.datavalue or not snak.datavalue.value or not snak.datavalue.value.time then |
|||
return nil |
|||
end |
|||
local year = construct_year(args) |
|||
if args['noprint'] and args['nocat'] then |
|||
local attr = headerAttributions.construct_attributions(args) |
|||
return nil |
|||
local section = headerAttributions.construct_section(args) |
|||
end |
|||
if attr ~= '' and title ~= '' then |
|||
local cats = {} -- tracking categories |
|||
attr = '<br id=\"header-title=break\" />' .. attr |
|||
--[=[ Precision: |
|||
0 - billion years |
|||
1 - hundred million years, |
|||
2 - ten million years, |
|||
3 - million years, |
|||
4 - hundred thousand years, |
|||
5 - ten thousand years, |
|||
6 - millenia, |
|||
7 - centuries, |
|||
8 - decades, |
|||
9 - years, |
|||
10 - months, |
|||
11 - days |
|||
12 - hours |
|||
13 - minutes |
|||
14 - seconds |
|||
]=] |
|||
local precision = math.min(snak.datavalue.value.precision, 9) |
|||
local year |
|||
local circa = '' |
|||
local start_times = {} |
|||
local end_times = {} |
|||
local start_years = {} |
|||
local end_years = {} |
|||
local start_year |
|||
local end_year |
|||
if statement.qualifiers then |
|||
-- Check if date is approximate |
|||
-- P1480 = sourcing circumstances, Q5727902 = circa |
|||
if statement.qualifiers.P1480 then |
|||
for _, qualifier in pairs(statement.qualifiers.P1480) do |
|||
if qualifier.datavalue and qualifier.datavalue.value.id == 'Q5727902' then |
|||
precision = precision - 1 |
|||
circa = 'c. ' |
|||
break |
|||
end |
|||
end |
|||
end |
|||
-- P580 = start time |
|||
if statement.qualifiers.P580 then |
|||
for k, v in pairs(statement.qualifiers.P580) do |
|||
local startt = getYearFromSingleStatement({mainsnak = v}, {nocat = true, noprint = false, ['table'] = true}) |
|||
if startt then |
|||
table.insert(start_times, startt) |
|||
end |
|||
end |
|||
for k, v in pairs(start_times) do |
|||
table.insert(start_years, v.year) |
|||
end |
|||
start_years = tableTools.removeDuplicates(start_years) |
|||
table.sort(start_years) |
|||
if #start_years > 1 then |
|||
table.insert(cats, '[[Category:' .. 'Works of uncertain date' .. ']]') |
|||
end |
|||
start_year = start_years[1] |
|||
end |
|||
-- P582 = end time |
|||
if statement.qualifiers.P582 then |
|||
for k, v in pairs(statement.qualifiers.P582) do |
|||
local endt = getYearFromSingleStatement({mainsnak = v}, {nocat = true, noprint = false, ['table'] = true}) |
|||
if endt then |
|||
table.insert(end_times, endt) |
|||
end |
|||
end |
|||
for k, v in pairs(end_times) do |
|||
table.insert(end_years, v.year) |
|||
end |
|||
end_years = tableTools.removeDuplicates(end_years) |
|||
table.sort(end_years) |
|||
if #end_years > 1 then |
|||
table.insert(cats, '[[Category:' .. 'Works of uncertain date' .. ']]') |
|||
end |
|||
end_year = end_years[1] |
|||
end |
|||
end |
end |
||
return table.concat({titleSpan, year, attr, section}) |
|||
if precision < 9 then |
|||
table.insert(cats, '[[Category:' .. 'Works of uncertain date' .. ']]') |
|||
end |
|||
if precision < 7 then |
|||
if args['noprint'] then |
|||
year = '' |
|||
else |
|||
year = string.gsub(string.gsub(mw.wikibase.formatValue(statement.mainsnak), '^<span>', ''), '</span>$', '') |
|||
end |
|||
return circa .. year .. table.concat(cats) |
|||
end |
|||
local timestamp_info = getYearFromTimestamp(snak.datavalue.value.time) |
|||
year = timestamp_info.year |
|||
local bce = timestamp_info.bce |
|||
-- approximate year, precision 8: year - year % 10 for decade |
|||
-- approximate decade, precision 7: year - year % 100 for century |
|||
if circa ~= '' then |
|||
year = year - (year % math.pow(10, 9 - precision)) |
|||
end |
|||
year = formatYear(year, precision) |
|||
if start_year and start_year ~= year then |
|||
table.insert(cats, '[[Category:' .. 'Works of uncertain date' .. ']]') |
|||
end |
|||
local printed_year = circa .. year .. bce |
|||
if (start_year or end_year) and start_year ~= end_year then |
|||
printed_year = circa .. (start_year or '') .. '–' .. (end_year or '') .. bce |
|||
end |
|||
if start_year and end_year and start_year ~= end_year then |
|||
local start_decade = start_year - start_year % 10 |
|||
local end_decade = end_year - end_year % 10 |
|||
local start_century = start_year - start_year % 100 |
|||
local end_century = end_year - end_year % 100 |
|||
if start_decade == end_decade then |
|||
table.insert(cats, '[[Category:' .. start_decade .. 's' .. bce .. ' works' .. ']]') |
|||
elseif start_century == end_century then |
|||
table.insert(cats, '[[Category:' .. formatYear(end_year, 7) .. bce .. ' works' .. ']]') |
|||
end |
|||
else |
|||
table.insert(cats, '[[Category:' .. year .. bce .. ' works' .. ']]') |
|||
end |
|||
if args['noprint'] then |
|||
printed_year = '' |
|||
end |
|||
if not args['nocat'] then |
|||
cats = tableTools.removeDuplicates(cats) |
|||
printed_year = printed_year .. table.concat(cats) |
|||
end |
|||
if args['table'] then |
|||
return {year = year, printed_year = printed_year, circa = circa, bce = bce} |
|||
end |
|||
return printed_year |
|||
end |
|||
local function parse_wikidata_year_and_categorise(args) |
|||
-- Fetch entity object for Wikidata item connected to the current page |
|||
-- Let manually-specified Wikidata ID override if given and valid |
|||
if not (args.wikidata and mw.wikibase.isValidEntityId(args.wikidata)) then |
|||
args.wikidata = mw.wikibase.getEntityIdForCurrentPage() |
|||
end |
|||
local item = mw.wikibase.getEntity(args.wikidata) |
|||
if not item then |
|||
return nil |
|||
end |
|||
local statements = item:getBestStatements('P577') -- publication date |
|||
if #statements == 0 then |
|||
return nil |
|||
end |
|||
local years = {} |
|||
for _, statement in pairs(statements) do |
|||
local year = getYearFromSingleStatement(statement, args) |
|||
if year then |
|||
table.insert(years, year) |
|||
end |
|||
end |
|||
local cat = '' |
|||
years = tableTools.removeDuplicates(years) |
|||
if #years == 0 then |
|||
return nil |
|||
elseif #years > 1 and not args['nocat'] then |
|||
cat = '[[Category:' .. 'Works of uncertain date' .. ']]' |
|||
end |
|||
return table.concat(years, '/') .. cat |
|||
end |
|||
local function parse_year_and_categorise(args) |
|||
if args['override-year'] then |
|||
return args['override-year'] |
|||
end |
|||
local year = args['year'] |
|||
-- Use Wikidata if year param is empty |
|||
if not year then |
|||
return parse_wikidata_year_and_categorise(args) |
|||
end |
|||
local cats = {} |
|||
local bce = '' |
|||
local circa = '' |
|||
local ret = '' |
|||
-- Extract common era info to make it easier to process |
|||
if string.match(year, "BCE$") then |
|||
bce = ' BCE' |
|||
year = string.gsub(year, '%s*BCE', '') |
|||
-- Also tag it as a non-numeric year |
|||
table.insert(cats, '[[Category:' .. 'Works with non-numeric dates' .. ']]') |
|||
end |
|||
-- If the year provided is a plain year (all digits) |
|||
if tonumber(year) then |
|||
table.insert(cats, '[[Category:' .. year .. bce .. ' works]]') |
|||
if not args['noprint'] then |
|||
ret = year .. bce |
|||
end |
|||
if not args['nocat'] then |
|||
ret = ret .. table.concat(cats) |
|||
end |
|||
-- For simple years we're done. |
|||
return ret |
|||
end |
|||
-- Add tracking category for all non-numeric dates |
|||
table.insert(cats, '[[Category:' .. 'Works with non-numeric dates' .. ']]') |
|||
-- Explicitly tagged as being of unknown date |
|||
if year == '?' or string.lower(year) == 'unknown' then |
|||
table.insert(cats, '[[Category:' .. 'Works of unknown date' .. ']]') |
|||
if not args['noprint'] then |
|||
ret = 'unknown' |
|||
end |
|||
if not args['nocat'] then |
|||
ret = ret .. table.concat(cats) |
|||
end |
|||
-- For explicitly given unknown years we're done. |
|||
return ret |
|||
end |
|||
-- Now figure out a complex date |
|||
-- Year ranges |
|||
year = string.gsub(string.gsub(year, '%-', '–'), '—', '–') |
|||
-- Approximate years |
|||
-- Lua patterns can't do ^c(irca)?( |%.|/)* because they don't do alternation or apply quantifiers to groups |
|||
if string.match(year, '^circa') or string.match(year, '^c%s*%.') or string.match(year, '^c%s*/') then |
|||
circa = 'c. ' |
|||
year = string.gsub(string.gsub(string.gsub(year, '^circa%s*', ''), '^c%s*%.%s*', ''), '^c%s*/%s*', '') |
|||
table.insert(cats, '[[Category:' .. 'Works of uncertain date' .. ']]') |
|||
-- circa a specific year |
|||
if tonumber(year) then |
|||
year = tonumber(year) |
|||
local decade = (year - year % 10) .. 's' |
|||
table.insert(cats, '[[Category:' .. decade .. bce .. ' works]]') |
|||
if not args['noprint'] then |
|||
ret = circa .. year .. bce |
|||
end |
|||
if not args['nocat'] then |
|||
ret = ret .. table.concat(cats) |
|||
end |
|||
-- For approximate years with a known decade we're done |
|||
return ret |
|||
end |
|||
end |
|||
-- Check if it looks like a decade |
|||
if string.match(year, '^%d*0s$') then |
|||
table.insert(cats, '[[Category:' .. 'Works of uncertain date' .. ']]') |
|||
table.insert(cats, '[[Category:' .. year .. bce .. ' works]]') |
|||
-- For explicitly given decades we're done |
|||
if not args['noprint'] then |
|||
ret = circa .. year .. bce |
|||
end |
|||
if not args['nocat'] then |
|||
ret = ret .. table.concat(cats) |
|||
end |
|||
return ret |
|||
end |
|||
-- Or a century |
|||
-- Lua patterns can't do '^%d*(st|nd|rd|th) century$' |
|||
if string.match(year, '^%d*st century$') or string.match(year, '^%d*nd century$') or string.match(year, '^%d*rd century$') or string.match(year, '^%d*th century$') then |
|||
table.insert(cats, '[[Category:' .. 'Works of uncertain date' .. ']]') |
|||
table.insert(cats, '[[Category:' .. year .. bce .. ' works]]') |
|||
-- For explicitly given centuries we're done |
|||
if not args['noprint'] then |
|||
ret = circa .. year .. bce |
|||
end |
|||
if not args['nocat'] then |
|||
ret = ret .. table.concat(cats) |
|||
end |
|||
return ret |
|||
end |
|||
-- Or a range of years |
|||
local start_year, end_year |
|||
if string.match(year, '^%d*–%d*$') then |
|||
start_year, end_year = string.match(year, '^(%d*)–(%d*)$') |
|||
elseif string.match(year, '^%d*/%d*$') then |
|||
start_year, end_year = string.match(year, '^(%d*)/(%d*)$') |
|||
table.insert(cats, '[[Category:' .. 'Works of uncertain date' .. ']]') |
|||
end |
|||
if start_year or end_year then |
|||
start_year = tonumber(start_year) |
|||
end_year = tonumber(end_year) |
|||
if start_year and end_year and start_year == end_year and circa == '' then |
|||
table.insert(cats, '[[Category:' .. start_year .. bce .. ' works' .. ']]') |
|||
if not args['noprint'] then |
|||
ret = circa .. start_year .. bce |
|||
end |
|||
if not args['nocat'] then |
|||
ret = ret .. table.concat(cats) |
|||
end |
|||
return ret |
|||
elseif start_year and end_year then |
|||
local start_decade = start_year - start_year % 10 |
|||
local end_decade = end_year - end_year % 10 |
|||
local start_century = start_year - start_year % 100 |
|||
local end_century = end_year - end_year % 100 |
|||
if start_decade == end_decade then |
|||
table.insert(cats, '[[Category:' .. start_decade .. 's' .. bce .. ' works' .. ']]') |
|||
elseif start_century == end_century then |
|||
table.insert(cats, '[[Category:' .. formatYear(end_year, 7) .. bce .. ' works' .. ']]') |
|||
end |
|||
elseif end_year then |
|||
table.insert(cats, '[[Category:' .. 'Works of uncertain date' .. ']]') |
|||
end |
|||
-- check isn't redundant since the year might just be – |
|||
if start_year or end_year then |
|||
if not args['noprint'] then |
|||
ret = circa .. year .. bce |
|||
end |
|||
if not args['nocat'] then |
|||
ret = ret .. table.concat(cats) |
|||
end |
|||
return ret |
|||
end |
|||
end |
|||
-- If we're here we didn't manage to parse it. |
|||
table.insert(cats, '[[Category:' .. 'Works with unrecognised dates' .. ']]') |
|||
if not args['noprint'] then |
|||
ret = circa .. year .. bce |
|||
end |
|||
if not args['nocat'] then |
|||
ret = ret .. table.concat(cats) |
|||
end |
|||
-- Return whatever we have and the tracking cats |
|||
return ret |
|||
end |
|||
local function construct_year(args) |
|||
local year = mw.html.create('span'):attr('id', 'header-year-text') |
|||
local year_args = { |
|||
['year'] = args['year'], |
|||
['override-year'] = args['override-year'], |
|||
noprint = yesno(args['noyear']) or false, |
|||
wikidata = args.wikidata |
|||
} |
|||
year_args['nocat'] = yesno(args['noyearcat']) |
|||
if year_args['nocat'] == nil then |
|||
if args.testing then |
|||
year_args['nocat'] = false |
|||
else |
|||
year_args['nocat'] = ( |
|||
yesno(args['disambiguation']) -- Disambiguations never categorise |
|||
or not current_title:inNamespaces(0, 114) -- Only categorize in mainspace and Translation |
|||
or current_title.isSubpage -- Only categorise if this is a base page |
|||
) |
|||
end |
|||
end |
|||
local year_text = parse_year_and_categorise(year_args) |
|||
if year_text then |
|||
year:wikitext(year_text) |
|||
if year_args['noprint'] then |
|||
return tostring(year) |
|||
else |
|||
return ' (' .. tostring(year) .. ')' |
|||
end |
|||
elseif year_args['nocat'] then |
|||
return '' |
|||
else |
|||
return '[[Category:' .. 'Undated works' .. ']]' |
|||
end |
|||
end |
|||
--[=[ |
|||
Assemble the title |
|||
]=] |
|||
local function header_title(args) |
|||
return table.concat({ |
|||
tostring(mw.html.create('span'):attr('id', 'header-title-text'):wikitext(args.title)), |
|||
construct_year(args), |
|||
headerAttributions.construct_attributions(args), |
|||
headerAttributions.construct_section(args) |
|||
}) |
|||
end |
end |
||
Line 788: | Line 382: | ||
end |
end |
||
-- aliases |
|||
args.sortkey = args.defaultsort or args.sortkey |
args.sortkey = args.defaultsort or args.sortkey |
||
-- default values |
-- default values |
||
args.template_name = args.template_name or 'Header' |
args.template_name = args.template_name or 'Header' |
||
args.title = args.title or parent_links({}) |
|||
args.testing = yesno(args.testing) or current_title.fullText == 'Template:Header/testcases' or current_title.fullText == 'Template:Translation header/testcases' |
|||
args.testing = yesno(args.testing or current_title.fullText == 'Template:Header/testcases' or current_title.fullText == 'Template:Translation header/testcases') |
|||
-- add aliases to argsWithBlanks |
|||
args.nocat = yesno(args.nocat) or false |
|||
-- noyearcat has different behavior for nil and false |
|||
if args.nocat == true then |
|||
args.noyearcat = true |
|||
end |
|||
if args.language then |
|||
args.language_name = ISO_639_language_name(args.language) |
|||
end |
|||
args['language-required'] = yesno(args['language-required']) or false |
|||
-- add values to argsWithBlanks |
|||
for k, v in pairs(args) do |
for k, v in pairs(args) do |
||
if not argsWithBlanks[k] then |
if not argsWithBlanks[k] then |
||
Line 802: | Line 409: | ||
end |
end |
||
-- default |
-- 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 |
if not argsWithBlanks['section'] and current_title.isSubpage then |
||
args['section'] = current_title.subpageText |
args['section'] = current_title.subpageText |
||
argsWithBlanks['section'] = args['section'] |
|||
end |
end |
||
Line 812: | Line 424: | ||
args.header_class = 'wst-header ws-header ws-noexport noprint dynlayout-exempt ' .. (args.header_class or '') |
args.header_class = 'wst-header ws-header ws-noexport noprint dynlayout-exempt ' .. (args.header_class or '') |
||
args.main_class = 'headertemplate' |
args.main_class = 'headertemplate' |
||
args.notes_id = args.notes_id or 'navigationNotes' |
|||
-- title |
-- title |
||
args.main_title = header_title(args) |
args.main_title = header_title(args) |
||
Line 845: | Line 456: | ||
}) |
}) |
||
return |
return get_noexport_stylesheet('Header') .. construct_header(args) |
||
end |
end |
||
Line 863: | Line 474: | ||
args.header_class = 'wst-translation-header' |
args.header_class = 'wst-translation-header' |
||
args.template_name = 'Translation header' |
args.template_name = 'Translation header' |
||
args.main_id = 'translationheadertemplate' |
|||
args.notes_id = 'translation-notes' |
|||
args.notes_class = 'header-notes' |
args.notes_class = 'header-notes' |
||
if args.language then |
|||
args.language_name = ISO_639_language_name(args.language) |
|||
end |
|||
return |
return get_noexport_stylesheet('Translation header') .. p._header(args, argsWithBlanks) |
||
end |
end |
||
Latest revision as of 01:58, 31 August 2024
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
--[=[
Construct the [[Help:Microformat]] for the page.
This is in the form:
<div id="ws-data" ...>
<span id="ws-title">Title here...</span>
...
<div>
]=]
local function construct_microformat(args)
local mf_div = mw.html.create('div')
:addClass('ws-noexport')
:attr('id', 'ws-data')
:css({speak = 'none'})
-- override to show the microformat
if yesno(args['show-microformat']) then
mf_div:addClass('ws-data-show')
end
-- collect the MF values here
local mf = {};
mf['ws-article-id'] = current_title.id
-- add the title
if args['title'] then
mf['ws-title'] = args['title']
-- append section if there is one
if args['section'] then
mf['ws-title'] = mf['ws-title'] .. " — " .. args['section']
end
end
local author = args['override-section-author'] or args['section-author'] or args['override-author'] or args['author']
if author then
mf['ws-author'] = author
end
local translator = args['override-translator'] or args['translator']
if translator then
mf['ws-translator'] = translator
end
local year = args['year']
if year then
mf['ws-year'] = year
end
if args['cover'] then
mf['ws-cover'] = args['cover']
end
for k, v in pairs(mf) do
mf_div:tag('span'):attr('id', k):wikitext(v)
end
return tostring(mf_div)
end
--[=[
Detect explicit formatting in fields like "section" and "title"
]=]
local function explicit_formatting(str)
return 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 construct_categories(args)
local categories = {}
if args['override-author'] then
table.insert(categories, "Pages with override author")
end
if args['override-translator'] then
table.insert(categories, "Pages with override translator")
end
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['section-author'] then
table.insert(categories, 'Pages with contributor')
end
if args['override-section-author'] then
table.insert(categories, 'Pages with override contributor')
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
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
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 yesno(args['noyearcat']) then
table.insert(categories, "Pages with noyearcat")
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 yesno(args['allow-explicit-formatting']) then
if args['title'] and explicit_formatting(args['title']) then
table.insert(categories, "Pages with explicit formatting in header fields")
end
if args['section'] and explicit_formatting(args['section']) then
table.insert(categories, "Pages with explicit formatting in header fields")
end
end
-- translation header categories
local isMainPage = ((current_title:inNamespaces(0, 114) and not current_title.isSubpage) or args['testing'])
if args.template_name == 'Translation header' and isMainPage then
if not args['nocat'] then
if args.language then
table.insert(categories, 'Wikisource translations of works in ' .. (args.language_name or 'an undefined language'))
else
table.insert(categories, 'Wikisource translations with no original language')
end
end
if not args.original then
table.insert(categories, 'Wikisource translations with no original source')
end
elseif not args['nocat'] and isMainPage then
if args.language then
table.insert(categories, 'Works originally in ' .. (args.language_name or 'an undefined language'))
elseif args['language-required'] then
table.insert(categories, 'Translations with no original language')
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
]=]
local category_links = {}
for k, v in pairs(categories) do
table.insert(category_links, '[[Category:' .. v .. ']]')
end
return table.concat(category_links)
end
--[=[
Check for numerical parameters (which shouldn't be used)
]=]
local function check_for_numerical_arguments(args)
for k, v in pairs(args) do
if type(k) == 'number' then
return '[[Category:' .. 'Headers with numerical arguments' .. ']]'
end
end
return ''
end
--[=[
Add categories from the categories parameter
]=]
local function manual_categories(args)
if not args.categories then
return ''
end
-- Replace each string that ends in a slash with a category definition.
-- This does include the final one because we're adding a slash to the input string.
local categories = mw.ustring.gsub(args.categories .. '/', '([^/]+)/*', '[[Category:%1]]')
return '[[Category:' .. 'Works using categories parameter' .. ']]' .. categories
end
--[=[
Categorize subpages
]=]
local function check_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
return '[[Category:' .. 'Subpages' .. ']]'
elseif parent_exists then
return '[[Category:' .. title.nsText .. ' subpages' .. ']]'
else
return ''
end
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 = '<br id=\"header-title=break\" />' .. attr
end
return table.concat({titleSpan, year, attr, section})
end
--[=[
[[Template:Header]]
]=]
function p._header(args, argsWithBlanks)
argsWithBlanks = argsWithBlanks or args
-- aliases
local dup_cat = ''
local newArgs = {}
for k, v in pairs(args) do
local newkey = string.lower(string.gsub(string.gsub(tostring(k), '_', '-'), ' ', '-'))
if newkey ~= tostring(k) then
if argsWithBlanks[newkey] then
dup_cat = '[[Category:' .. 'Pages using duplicate arguments in template calls' .. ']]'
end
if not args[newkey] then
newArgs[newkey] = newArgs[newkey] or v
end
end
end
for k, v in pairs(newArgs) do
args[k] = v
argsWithBlanks[k] = v
end
newArgs = {}
local aliases = {
['section-author'] = 'contributor',
['section-translator'] = 'contributing%-translator'
}
for arg, alias in pairs(aliases) do
for k, v in pairs(args) do
local newkey = string.gsub(k, alias, arg)
if newkey ~= tostring(k) then
if argsWithBlanks[newkey] then
dup_cat = '[[Category:' .. 'Pages using duplicate arguments in template calls' .. ']]'
end
if not args[newkey] then
newArgs[newkey] = v
end
end
end
end
for k, v in pairs(newArgs) do
args[k] = v
argsWithBlanks[k] = v
end
-- aliases
args.sortkey = args.defaultsort or args.sortkey
-- default values
args.template_name = args.template_name or 'Header'
args.testing = yesno(args.testing or current_title.fullText == 'Template:Header/testcases' or current_title.fullText == 'Template:Translation header/testcases')
args.nocat = yesno(args.nocat) or false
-- noyearcat has different behavior for nil and false
if args.nocat == true then
args.noyearcat = true
end
if args.language then
args.language_name = ISO_639_language_name(args.language)
end
args['language-required'] = yesno(args['language-required']) or false
-- 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.language then
interwiki = tostring(mw.html.create('span'):addClass('interwiki-info'):attr('id', args.language):attr('title', '(original)'))
if args.original and (args.language == 'ang' or args.language == 'enm' or args.language == 'sco') then
-- cycle to mul.ws and back around to en.ws
interwiki = interwiki .. '[[' .. args.language .. ':en:' .. args.original .. ']]'
elseif args.original and args.language and args.language_name then
-- general interwiki link
interwiki = interwiki .. '[[' .. args.language .. ':' .. args.original .. ']]'
end
end
-- 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({
construct_microformat(args),
check_subpages(),
manual_categories(args),
construct_categories(args),
check_for_numerical_arguments(argsWithBlanks),
construct_defaultsort(args),
dup_cat,
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'
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