Module:Proofreadpage index template

--[=[ 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 p = {} --p stands for package

local DEFAULTS = { Source = '_empty_', }

-- mapping of field ID to field properties local headings = { title = { txt = 'Title', },	progress = { txt = 'Progress', },	oclc = { txt = 'OCLC', },   transclusion = { txt = 'Transclusion', },   validation_date = { txt = 'Validated in', },   source = { txt = 'Source' },   place = { txt = 'Location' },   year = { txt = 'Year' },   publisher = { txt = 'Publisher' },   author = { txt = 'Author' },   editor = { txt = 'Editor' },   translator = { txt = 'Translator' },   illustrator = { txt = 'Illustrator' },   doi = { txt = 'DOI' },   volumes = { txt = 'Volumes' } }

local indicators = { pagegame = { name = "WS Page Game", image = "File:OOjs UI icon page number.svg", link = "", caption = "Wikisource Page Game (step-by-step pagelist builder)" },	purge = { name = "purge file", image = "File:OOjs UI icon reload.svg", link = "", caption = "Purge file" },	book2scroll = { name = "book2scroll", image = "File:Library-logo-blue-outline.png", link = "", caption = "Open in Book2Scroll" },	bookreader = { name = "bookreader", image = "File:BookReader-favicon.svg", link = "", caption = "Open file in BookReader" }, }

local function get_heading(id) if headings[id] then return headings[id]['txt'] end error( "Can't find heading for ID: " .. id ) end

-- mapping from status code to category and status text local index_status_data = { T = { cat = 'Index Validated', txt = 'Done—All pages of the work proper are validated', },	V = { cat = 'Index Proofread', txt = 'Proofread—All pages of the work proper are proofread, but not all are validated', },	C = { cat = 'Index Not-Proofread', txt = 'To be proofread', },	MS = { cat = 'Index - Ready for Match and Split', txt = 'Ready for Match and Split', },	OCR = { cat = 'Index - Text Layer Requested', txt = 'Source file needs an OCR text layer', },	L = { cat = 'Index - File to fix', txt = 'Source file must be fixed before proofreading', error = true, },	X = { cat = 'Index - File to check', txt = 'Create a pagelist for the source file before commencing proofreading (to verify file is correct)', error = true },	_default = { cat = 'Index - Unknown progress', txt = 'Unknown progress (template error)', error = true, } }

-- construct a basic "field" row local function construct_field(id, content) if id == nil or content == nil then return '' end local title = get_heading( id )

local s = '' s = s .. mw.ustring.format('\n', id) s = s .. mw.ustring.format('%s \n', id, title) s = s .. mw.ustring.format('%s \n', id, content) s = s .. ' \n'

return s end

local function construct_link(url, text) return '[' .. url .. ' ' .. text .. ']' end

local function int_link(target, text) return '[' .. '[' .. target .. '|' .. text .. ']]'	end

-- handy URL generators (remember to use mw.uri.encode on the bits you need to) local url_gens = { oclc = function(id) return 'https://www.worldcat.org/oclc/' .. id end, ark = function(id) return 'https://n2t.net/' .. mw.uri.encode(id) end, doi = function(id) return 'https://doi.org/' .. id	end }

-- construct a span which might be classed "error" local function maybe_error_span(text, error) local span = mw.html.create('span') :wikitext(text) if error then span:addClass('error') end return span end

-- Construct the Title field (includes Vol. after Title if present) local function construct_title_field(id, title, volume) local content = title or "" if volume ~= nil and volume ~= "" then content = content .. ", " .. volume end return construct_field(id, content) end

-- construct the source field: -- - if a file exists, link it -- - else just the text local function construct_source_field(source) local value local file = mw.title.new( mw.title.getCurrentTitle.text, 'File' )

if file.file.exists then value = int_link( ':' .. file.fullText, source ) else value = source end

local out = construct_field( 'source', value ) return out end

local function construct_location_field( loc ) return construct_field('place', loc) end

-- construct the status (proofread/validated/...) field local function construct_status_field(status)

local out = '' local sd = index_status_data[status] if not sd then sd = index_status_data['_default'] end out = out .. ''

local link_text = tostring(maybe_error_span(sd['txt'], sd['error'])) local catlink =  .. link_text ..  out = out .. construct_field("progress", catlink)

return out end

local transclusion_data = { yes = { cat = 'Fully transcluded', text = 'Fully transcluded', help = 'The work is fully transcluded, including front and end matter and images' },	notimg = { cat = 'Images not fully transcluded', text = 'Images not fully transcluded', help = 'The work is fully transcluded, but that some images are still missing or need improvement', },	notadv = { cat = 'Advertising not transcluded', text = 'Advertising not transcluded', help = 'The main body of the work is properly transcluded, though front- or end-matter advertising material is not' },	held = { cat = 'Transclusion on hold', text = 'Transclusion on hold', help = 'There is a problem with transcluding the work (which should be explained on the talk page)' },	check = { cat = 'Transclusion check required', text = 'Transclusion check required', help = ' The transclusion of the work is incomplete or needs checking' },	no = { cat = 'Index not transcluded', text = 'Index not transcluded or unreviewed', help = ' This work is not transcluded or has not been reviewed for transclusion' } }

local function construct_cat(cat) return "" end

-- construct the transclusion status local function construct_transclusion_status(status, date) -- handle templates with no explicit status if status == nil then status = "no" end local td = transclusion_data[status] if td == nil then error("Unknown index status: " .. status) end

local catlink =  .. td['text'] ..  local out = construct_field("transclusion", catlink) out = out .. construct_cat( td['cat']) return out end

local function construct_validated_date(date) local cat = 'Indexes validated in ' .. date local indicator_text = 'Validated in ' .. date local out = '' local catlink =  .. date ..  out = out .. construct_field("validation_date", catlink) out = out .. ' ' return out end

local function getLanguageCat(l) -- get the name or nil local lang = ISO_639.language_name(l) if lang then return "Index pages of works originally in " .. lang end end

function p.fields(frame) local args = getArgs(frame) -- set any defaults for k, v in pairs( DEFAULTS ) do		if args[ k ] == nil then args[ k ] = DEFAULTS[ k ] end end

local s = ""

s = s .. construct_title_field('title', args['Title'], args['Volume']) s = s .. construct_field('author', args['Author']) s = s .. construct_field('translator', args['Translator']) s = s .. construct_field('editor', args['Editor']) s = s .. construct_field('illustrator', args['Illustrator']) s = s .. construct_field('year', args['Year']) s = s .. construct_field('publisher', args['Publisher']) s = s .. construct_location_field(args['Address']) s = s .. construct_source_field(args['Source']) s = s .. construct_status_field(args['Progress']) -- always do this, even if the arg is not set s = s .. construct_transclusion_status(args["Transclusion"], args['Transclusion_date']) if args["Validation_date"] then s = s .. construct_validated_date(args["Validation_date"]) end

if args["OCLC"] then local link = construct_link(url_gens['oclc'](args["OCLC"]), args['OCLC']) s = s .. construct_field("oclc", link) end if args["DOI"] then local link = construct_link(url_gens['doi'](args["DOI"]), args['DOI']) s = s .. construct_field("doi", link) end

s = s .. 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			local cat = getLanguageCat(lang) if cat then s = s .. construct_cat(cat) else s = s .. construct_cat('Index pages of works originally in an unknown language') end end if #langs > 1 then s = s .. construct_cat('Index pages of works originally in multiple languages') end end

return s end

function p.talkremarks(frame) return _talkremarks(frame) end

local function _talkremarks(frame) local talkTitle = mw.title.getCurrentTitle.talkPageTitle local talkText = talkTitle.prefixedText if talkTitle.exists then local notes = frame:callParserFunction("#lsth", talkText, "Quick notes") return frame:expandTemplate{ title = "Index talk remarks", args = { notes = "\n" .. notes }		}	end

return '' end

function p.indicators(frame) return _indicators(frame) end

local function _indicators(frame) local s = '' for k, v in pairs(indicators) do		local link = frame:preprocess(v.link) local img = mw.ustring.format("20px|link=%s|%s", v.image, link, v.caption) s = s .. frame:extensionTag{ name = "indicator", content = img, args = { name = v.name }		}	end return s end

function p.cover(frame) local args = getArgs(frame)

-- 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 "" end

-- If Image param is not a (page) number then this is an image-based -- Index, so use the provided image as the cover image. if tonumber(args['Image']) == nil then local s = "" if mw.ustring.find(args['Image'], '^%[%[') ~= nil then -- It's a full image specification s = s .. args['Image'] else -- Assume it's a filename with or without File: prefix local file = mw.ustring.gsub(args['Image'], "^[Ff]ile:", "") s = s .. "250px|class=ws-cover" end -- Add a tracking category s = s .. "" .. "Category:Image based indexes" .. "" return s	end

-- Otherwise it's a DjVu/PDF-backed index, in which case we fetch the -- cover image from a page in the (multipage) file. local indexTitle = mw.title.getCurrentTitle local fileTitle = mw.title.makeTitle('File', indexTitle.text) if not fileTitle.file.exists then -- Our associated file doesn't exist so use a placeholder local s = "" s = s .. "" .. "Category:Indexes with missing files" .. "" return s	end

-- File exists and we have page number for the cover local s = "250px"	s = s .. "|page=" .. tonumber(args['Image']) .. "|class=ws-cover" return s end

function p.remarks(frame) local args = getArgs(frame)

if args['Remarks'] ~= nil and args['Remarks'] ~= "" then return '' .. args['Remarks'] .. ' '	else return ' ' end end

function p.sortkey(frame) local args = getArgs(frame)

local sortkey = ""

if args['Key'] ~= nil and args['Key'] ~= "" then sortkey = args['Key'] else sortkey = mw.title.getCurrentTitle.text end return frame:preprocess{text = ""} end

-- Wrapper to output .fields with containing html table element. function p.metadata(frame) local metadatatable = mw.html.create("table") metadatatable:attr("id", "ws-index-metadata") local rows = p.fields(frame) metadatatable:node(rows) return tostring(metadatatable) end

-- -- NEW EXPERIMENTAL CODE BELOW THIS LINE -- --

local cfg = mw.loadData("Module:Proofreadpage index template/config")

local CATS = { categories = {} } function CATS:addCat(cat) table.insert(self.categories, cat) end function CATS:getCats local cats = "" for _, cat in ipairs(self.categories) do cats = cats .. "" .. "Category:" .. cat .. "" end return cats end function CATS:makeCatLink(cat, label) if label ~= nil and label ~= "" then return "" .. label .. "" end return "" .. "Category:" .. cat .. "" end

-- construct a basic "field" row local function __makeRow(id, content) if id == nil or id == "" or content == nil then return '' end

local row = mw.html.create("tr") row :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 tostring(row) end

-- Create indicator markup based on config -- -- Loads the config module and creates indicator extension tags after pattern --   -- The link is preprocessed as wikitext so it can contain parser functions etc. local function __getIndicators(frame) local indicators = mw.loadData('Module:Proofreadpage index template/indicators') local s = '' for _, v in pairs(indicators) do		local link = frame:preprocess(v.link) local img = mw.ustring.format("20px|link=%s|%s", v.image, link, v.caption) s = s .. frame:extensionTag{ name = "indicator", content = img, args = { name = v.name }		}	end return s end

-- Get talk page notice if present -- -- If the talk page associated with the current page exists, we use Labelled -- Section Transclusion to transclude the section titled "Quick notes" and if -- the result contains any text, return a message box with its contents. local function __getTalkNotice(frame) local talkTitle = mw.title.getCurrentTitle.talkPageTitle local talkText = talkTitle.prefixedText if talkTitle.exists then local notes = frame:callParserFunction("#lsth", talkText, "Quick notes")

local s = "Formatting guidelines specific to this work may have " s = s .. "already been established. Please check " s = s .. "the talk page for details."

local box = messageBox.main("ombox", {			type = "content",		   text = s .. notes		}) return box end

return '' 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 __getCoverImage(args) local s = ""

-- If Image param is not a (page) number then this is an image-based -- Index, so use the provided image as the cover image. if tonumber(args['Image']) == nil then if mw.ustring.find(args['Image'], '^%[%[') ~= nil then s = s .. args['Image'] -- It's a full image specification else -- Assume it's a filename with or without File: prefix local file = mw.ustring.gsub(args['Image'], "^[Ff]ile:", "") s = s .. "250px|class=ws-cover ws-index-cover" end -- Add a tracking category CATS:addCat("Image based indexes") return s	end

-- Otherwise it's a DjVu/PDF-backed index, in which case we fetch the -- cover image from a page in the (multipage) file. local indexTitle = mw.title.getCurrentTitle local fileTitle = mw.title.makeTitle('File', indexTitle.text) if not fileTitle.file.exists then -- Our associated file doesn't exist so use a placeholder s = s .. ""		CATS:addCat("Indexes with missing files") return s	end

-- File exists and we have page number for the cover s = s .. "250px"	s = s .. "|page=" .. tonumber(args['Image']) .. "|class=ws-cover ws-index-cover" return s end

-- Get the pagelist provided by PRP -- -- PRP furnished a finished rendered pagelist, so we just need to wrap it in a -- suitable container, add a legend, etc. local function __getPagelist(args) -- Main container local plcontainer = mw.html.create("div") plcontainer :addClass("ws-index-pagelist-container") :addClass("mw-collapsible") -- make it collapsible :tag("div") -- The heading/legend :addClass("ws-index-pagelist-heading") :wikitext("Pages") :wikitext(" ") -- Force a space between heading and legend. :tag("span") :addClass("ws-index-pagelist-heading-legend") :wikitext("(key to Page Status)") :done :done :tag("div") -- The pagelist itself :addClass("ws-index-pagelist") :addClass("index-pagelist") -- legacy support :addClass("mw-collapsible-content") -- make it collapsible :wikitext('\n' .. mw.text.trim(args["Pages"]) .. '\n')

return tostring(plcontainer) end

-- Get the metadata fields. local function __getMetadata(frame, args) -- set any defaults for key, _ in pairs(cfg.defaults) do		if args[key] == nil then args[key] = cfg.defaults[key] end end

local s = ""

-- Construct the Title field (includes Vol. after Title if present) local title = args['Title'] or "" if args['Volume'] ~= nil and args['Volume'] ~= "" then title = title .. ", " .. args['Volume'] end s = s .. __makeRow('title', title)

s = s .. __makeRow('author',     args['Author']) s = s .. __makeRow('translator', args['Translator']) s = s .. __makeRow('editor',     args['Editor']) s = s .. __makeRow('illustrator', args['Illustrator']) s = s .. __makeRow('year',       args['Year']) s = s .. __makeRow('publisher',  args['Publisher']) s = s .. __makeRow('place',      args['Address'])

local location = args["Source"] local file = mw.title.new(mw.title.getCurrentTitle.text, 'File') if file.exists then location = "" .. args["Source"] .. "" end s = s .. __makeRow('source', location)

local status = cfg.status[args['Progress']] or cfg.status['_default'] CATS:addCat(status.cat) -- Add this index to that cat local linktext = mw.html.create('span'):wikitext(status.txt) if status.error then linktext:addClass('error') end local statuslink = CATS:makeCatLink(status.cat, tostring(linktext)) s = s .. __makeRow('progress', statuslink)

local transclusion = cfg.transclusion[args["Transclusion"] or "no"] -- always do this, even if the arg is not set CATS:addCat(transclusion.cat) local transclusionlink = CATS:makeCatLink(transclusion.cat, transclusion.txt) s = s .. __makeRow("transclusion", transclusionlink)

if args["Validation_date"] then local valcat = 'Indexes validated in ' .. args["Validation_date"] local valtxt = 'Validated in ' .. args["Validation_date"] CATS:addCat(valcat) local valcatlink = CATS:makeCatLink(valcat, args["Validation_date"]) s = s .. __makeRow("validation_date", valcatlink) s = s .. frame:extensionTag{ -- FIXME: need the frame here! name = "indicator", content = '', args = { name = "validated-index-date" }		}	end

if args["OCLC"] then local link = construct_link(url_gens['oclc'](args["OCLC"]), args['OCLC']) s = s .. __makeRow("oclc", link) end if args["DOI"] then local link = construct_link(url_gens['doi'](args["DOI"]), args['DOI']) s = s .. __makeRow("doi", link) end

s = s .. __makeRow('volumes', args['Volumes'])

-- language categorisations if args["Language"] then local langs = mw.text.split(args["Language"], ',%s?', false) for _, lang in pairs(langs) do			local langcat = "Index pages of works originally in " langcat = langcat .. ISO_639.language_name(lang) or "an unknown language" CATS:addCat(langcat) end if #langs > 1 then CATS:addCat('Index pages of works originally in multiple languages') end end

local metadatatable = mw.html.create("table") :addClass("ws-index-metadata") :wikitext(s) return tostring(metadatatable) end

local function __main(frame) local args = getArgs(frame)

local s = "" -- Where we collect all the bits and bobs to return

-- The overall html structure in which each component is inserted local indexcontainer = mw.html.create("div") :addClass("ws-index-container") if tonumber(args["tmplver"]) == 42 then indexcontainer:addClass("ws-tng") -- Tag for "TNG" mode i asked for. end

-- Get the indicators (NB! Needs access to the frame!) s = s .. __getIndicators(frame)

-- Get talk page notice if present (NB! Needs access to the frame!) s = s .. __getTalkNotice(frame)

-- Get the image to use as the cover image for this index local cover = __getCoverImage(args) indexcontainer:tag("div") :addClass("ws-index-cover-container") :wikitext(cover)

-- Get metadata table (NB! Needs access to the frame!) local metadata = __getMetadata(frame, args) indexcontainer:tag("div") :addClass("ws-index-metadata-container") :wikitext(metadata)

-- Get the pagelist for the index local pagelist = __getPagelist(args) indexcontainer:wikitext(pagelist)

-- Add the remarks field (toc) local remarks = indexcontainer:tag("div") :addClass("ws-index-remarks-container") if args['Remarks'] == nil or args['Remarks'] == "" then remarks:addClass("ws-index-remarks-empty") end remarks:wikitext(args['Remarks'])

-- Return the HTML structure and the accumulated categories. return tostring(indexcontainer) .. CATS:getCats end

function p.main(frame) local args = getArgs(frame)

local s = "" -- Where we collect all the bits and bobs to return

if args["tmplver"] ~= nil and args["tmplver"] ~= "" then return __main(frame) end

-- First, the talk remarks notes (nb! needs frame object!) s = s .. _talkremarks(frame)

-- Then the indicators (nb! needs frame object!) s = s .. _indicators(frame)

-- Construct the table local outertable = mw.html.create("table") outertable:attr("id", "ws-index-container") local outertr = outertable:tag("tr")

local maincell = outertr:tag("td") maincell:attr("id", "ws-index-main-cell")

local maintable = maincell:tag("table") maintable:attr("id", "ws-index-main-table") local firstrow = maintable:tag("tr") local maincell = firstrow:tag("td") local coverdiv = maincell:tag("div") coverdiv:attr("id", "ws-index-cover-container") coverdiv:wikitext(p.cover(frame)) maincell:wikitext(p.metadata(frame)) local secondrow = maintable:tag("tr") local plcell = secondrow:tag("td") local pldiv = plcell:tag("div") pldiv:attr("id", "ws-index-pagelist-container") pldiv:addClass("mw-collapsible") pldiv:wikitext(' Pages (key to Page Status) ') local plinner = pldiv:tag("div") plinner:attr("id", "ws-index-pagelist") plinner:addClass("index-pagelist mw-collapsible-content") plinner:wikitext('\n' .. mw.text.trim(args["Pages"]) .. '\n')

outertr:wikitext(p.remarks(frame))

s = s .. tostring(outertable)

s = s .. p.sortkey(frame)

return s end

return p