Module:Proofreadpage index template/sandbox

--[=[
This is a module to implement logic for [[MediaWiki:Proofreadpage index template]]

It doesn't do everything yet, but over time it can accrete functions from
the template, which will become simpler and simpler until it's just an invoke.

Implemented so far:
 * Status categorisation and field
 * Some auxiliary data fields
]=]

local getArgs = require('Module:Arguments').getArgs
local messageBox = require('Module:Message box')
local ISO_639 = require('Module:ISO 639')
local index_talk_remarks = require('Module:Index talk remarks')._index_talk_remarks
local error_text = require('Module:Error')['error']

local p = {} --p stands for package

local cfg = require('Module:Proofreadpage index template/config/sandbox')

local function process_args(args)
	-- set any defaults
	for k, v in pairs(cfg.defaults) do
		if args[k] == nil then
			args[k] = cfg.defaults[k]
		end
	end
	args.pageTitle = (args.pageTitle and mw.title.new(args.pageTitle)) or mw.title.getCurrentTitle()
	args.fileTitle = mw.title.makeTitle('File', args.pageTitle.rootText)
	args.talkPageTitle = args.pageTitle.talkPageTitle
	return args
end

local function construct_cat(cat)
	return '[[Category:' .. cat .. ']]'
end

local function construct_cat_link(cat, text)
	return '[[:Category:' .. cat .. '|' .. (text or cat) .. ']]'
end

-- construct a basic "field" row
local function construct_field(id, content)
	if id == nil or content == nil then
		return nil
	elseif not cfg.headings[id] then
		error(cfg.missing_heading_id(id))
	end
	
	local tr = mw.html.create('tr')
		:attr('id', 'ws-index-' .. id .. '-row')
		:addClass('ws-index-row')
		:tag('th')
			:attr('scope', 'row')
			:attr('id', 'ws-index-' .. id .. '-label')
			:addClass('ws-index-label')
			:wikitext(cfg.headings[id]['txt'])
			:done()
		:tag('td')
			:attr('id', 'ws-index-' .. id .. '-value')
			:addClass('ws-index-value')
			:wikitext(content)
			:allDone()
	return tr
end

local function construct_status_field(args, statusArgs)
	local key = statusArgs.key
	local lower_key = string.lower(key)
	local config_key = statusArgs.config_key or lower_key
	
	local index_status = args[key] or '_missing'
	local sd = cfg[config_key][index_status] or cfg[config_key]['_default']
	
	local index_status_message = sd['txt']
	if type(index_status_message) == 'function' then
		index_status_message = index_status_message(index_status)
	end
	if sd['error'] then
		index_status_message = error_text({['message'] = index_status_message, ['tag'] = 'span'})
	else
		index_status_message = construct_cat_link(sd['cat'], index_status_message)
	end
	
	return {row = construct_field(lower_key, index_status_message), cat = construct_cat(sd['cat'])}
end

--[=[
Create indicator markup based on config

Loads the config module and creates indicator extension tags after pattern
	<indicator name="foo">[[File:Foo.svg|20px|link=bar|caption|alt=Alt text.]]</indcator>
]=]
local function construct_indicator(args, iArgs)
	if not (iArgs.include or cfg.indicator_defaults.include)(args) then
		return nil
	end
	
	local indicator_parts = {
		iArgs.image or cfg.indicator_defaults.image,
		iArgs.width or cfg.indicator_defaults.width
	}
	
	local alt = iArgs.alt or cfg.indicator_defaults.alt
	table.insert(indicator_parts, alt and 'alt=' .. alt)
	
	local link = iArgs.link or cfg.indicator_defaults.link
	if type(link) == 'function' then
		link = link(args)
	end
	table.insert(indicator_parts, link and 'link=' .. link)
	
	local caption = iArgs.caption or cfg.indicator_defaults.caption
	if type(caption) == 'function' then
		caption = caption(args)
	end
	table.insert(indicator_parts, caption)
	
	return mw.getCurrentFrame():extensionTag{
		['name'] = 'indicator',
		['content'] = '[[' .. table.concat(indicator_parts, '|') .. ']]',
		['args'] = {
			['name'] = iArgs.name or cfg.indicator_defaults.name
		}
	}
end

local function _metadata(args)
	local metadatatable = mw.html.create('table')
		:attr('id', 'ws-index-metadata')
	
	local cats = {}
	local indicators = {}
	
	metadatatable:node(construct_field('title', (args['Title'] and table.concat({args['Title'], args['Volume']}, ', ')) or args['Volume']))
	
	local simple_args = {{'Author'}, {'Translator'}, {'Editor'}, {'Illustrator'}, {'Year'}, {'Publisher'}, {'Location', 'place'}}
	for i, v in ipairs(simple_args) do
		metadatatable:node(construct_field(v[2] or string.lower(v[1]), args[v[1]]))
	end
	
	local fileTitle = args.fileTitle
	local source = args['Source']
	-- expensive
	if fileTitle.file.exists then
		source = '[[:' .. fileTitle.fullText .. '|' .. source .. ']]'
	end
	metadatatable:node(construct_field('source', source))
	
	-- Progress
	local progress_data = construct_status_field(args, {['key'] = 'Progress', ['config_key'] = 'status'})
	metadatatable:node(progress_data.row)
	table.insert(cats, progress_data.cat)
	
	-- Transclusion status
	local transclusion_data = construct_status_field(args, {['key'] = 'Transclusion'})
	metadatatable:node(transclusion_data.row)
	table.insert(cats, transclusion_data.cat)
	
	local vdate = args['Validation_date']
	if vdate then
		local vcat = 'Indexes validated in ' .. vdate
		metadatatable:node(construct_field('validation_date', construct_cat_link(vcat, vdate)))
		table.insert(cats, construct_cat(vcat))
	end
	
	local info_args = {'ISBN', 'OCLC', 'LCCN', 'ARK', --[=['National Archives',]=] 'DOI'}
	for i, v in ipairs(info_args) do
		local val = args[v]
		if val then
			local lower_key = string.lower(v)
			local link_fn = cfg.url_gens[lower_key]
			if link_fn then
				metadatatable:node(construct_field(lower_key, link_fn(val, val)))
			end
		end
	end
	
	metadatatable:node(construct_field('volumes', args['Volumes']))
	
	-- language categorisations
	if args.Language then
		local langs = mw.text.split(args.Language, ',%s*', false)
		for _, lang in pairs(langs) do
			table.insert(cats, construct_cat('Index pages of works originally in ' .. (ISO_639.language_name(lang) or 'an unknown language')))
		end
		if #langs > 1 then
			table.insert(cats, construct_cat('Index pages of works originally in multiple languages'))
		end
	end
	
	return {['table'] = metadatatable, cats = table.concat(cats), indicators = table.concat(indicators)}
end

--[=[
Get the image to use as the cover image for this index

If the Image parameter contains an integer, it refers to a page in a DjVu/PDF and can be used directly.
Otherwise, it may be a full image specification that we can use directly,
or just an image filename that we can construct an image specification for.
]=]

local function default_cover_image(link)
	return '[[' .. cfg.cover.image .. '|' .. cfg.cover.width .. '|link=' .. link .. '|class=ws-cover]]'
end

local function _cover(args)
	-- Workaround for pagelist preview that tries to parse this template without any context and with missing parameters via the API.
	if args['Image'] == nil then
		return nil
	end
	
	local image_number = tonumber(args['Image'])
	local image_spec
	local cats = {}
	
	if image_number == nil and mw.ustring.find(args['Image'], '^%[%[') ~= nil then
		-- Image param is not a (page) number and looks like a full image specification
		image_spec = args['Image']
		
		-- Add a tracking category
		table.insert(cats, construct_cat('Image based indexes'))
	elseif image_number == nil then
		-- Image param is not a (page) number and is probably a filename with or without File: prefix
		image_spec = mw.ustring.gsub(args['Image'], '^[Ff]ile:', '')
		image_spec = mw.ustring.gsub(args['Image'], '^[Ii]mage:', '')
		image_spec = '[[File:' .. image_spec .. '|' .. cfg.cover.width .. '|class=ws-cover]]'
		
		-- Add a tracking category
		table.insert(cats, construct_cat('Image based indexes'))
	elseif args.fileTitle.file.exists then
		-- It's a DjVu/PDF-backed index, so we fetch the cover image from a page in the (multipage) file.
		image_spec = '[[' .. args.fileTitle.prefixedText .. '|' .. cfg.cover.width .. '|page=' .. image_number .. '|class=ws-cover]]'
	else
		-- Our associated file doesn't exist so use a placeholder
		local image_link = args.fileTitle.prefixedText
		if  mw.ustring.find(args.fileTitle.rootText, '^.*%.%w+') == nil then
			image_link = 'Special:Upload'
		end
		image_spec = default_cover_image(image_link)
		
		-- Add a tracking category
		table.insert(cats, construct_cat('Indexes with missing files'))
	end
	
	return image_spec .. table.concat(cats)
end

local function _remarks(args)
	if args['Remarks'] then
		return mw.html.create('td')
			:attr('id', 'ws-index-remarks')
			:newline()
			:wikitext(mw.getCurrentFrame():preprocess(args['Remarks']))
	else
		return mw.html.create('td')
			:attr('id', 'ws-index-remarks-empty')
	end
end

local function _main(args)
	args = process_args(args)
	
	local talkremarks = ''
	if args.talkPageTitle.exists then
		talkremarks = index_talk_remarks({
			notes = mw.getCurrentFrame():callParserFunction('#lsth', args.talkPageTitle.prefixedText, cfg.quick_notes_header),
			index = args.talkPageTitle.text
		})
	end
	
	local metadata_data = _metadata(args)
	local sortkey = mw.getCurrentFrame():callParserFunction('DEFAULTSORT', {args['Key'] or args.pageTitle.text})
	
	local is = {}
	for _, v in pairs(cfg.indicators) do
		table.insert(is, construct_indicator(args, v))
	end
	table.insert(is, metadata_data.indicators)
	local indicators = table.concat(is)
	
	local outertable = mw.html.create('table')
		:attr('id', 'ws-index-container')
	
	local outertable_tr = outertable:tag('tr')
	
	outertable_tr
		:tag('td')
			:attr('id', 'ws-index-main-cell')
			:tag('table')
				:attr('id', 'ws-index-main-table')
				:tag('tr'):tag('td')
					:tag('div')
						:attr('id', 'ws-index-cover-container')
						:wikitext(_cover(args))
						:done()
					:node(metadata_data['table'])
					:done()
				:tag('tr'):tag('td'):tag('div')
					:attr('id', 'ws-index-pagelist-container')
					:addClass('mw-collapsible')
					:tag('em'):wikitext(cfg.pagelist.pages.txt):done()
					:wikitext(' ')
					:tag('span')
						:attr('id', 'ws-index-pagelist-legend')
						:wikitext(cfg.pagelist.legend.txt)
						:done()
					:tag('div')
						:attr('id', 'ws-index-pagelist')
						:addClass('index-pagelist mw-collapsible-content')
						:newline()
						:wikitext(args['Pages'] and mw.text.trim(args['Pages']))
						:newline()
	
	outertable_tr:node(_remarks(args))
	
	return talkremarks .. indicators .. tostring(outertable) .. metadata_data.cats .. sortkey
end

function p.main(frame)
	return _main(getArgs(frame))
end

return p
Category:Module sandboxes