Jump to content

Module:New texts/sandbox

From Wikisource
-- This is a module to implement listings of new texts

require('strict')

local p = {} --p stands for package

local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local error_function = require('Module:Error')['error']

local DEFAULTS = {
	data_source = "Template:New texts/data",
	currentyear = tonumber(os.date("%Y"))
}

local function get_data(src, year)
	local data
	
	if not src or src == '' then
		src = DEFAULTS["data_source"]
	end
	year = tonumber(year) or DEFAULTS["currentyear"]
	src = src .. "/" .. year .. ".json"
	
	local data
	if pcall(function()
		local jsondata = mw.title.new(tostring(src)):getContent()
		data = mw.text.jsonDecode(jsondata)
	end) then
      -- no errors while loading
    else
    	error("Failed to load new text data from [[" .. src .. "]]")
    end
	
	return data
end

--[=[
Construct creator link/nonlink
]=]
local function construct_author(str, nowiki)
	-- strip "Portal:" prefixes if not piped
	str = string.gsub(str, "%[%[%s*[Pp]ortal:([^|]-)|?%]%]", function (target)
		return "[[Portal:" .. target .. "|" .. target .. "]]"
	end)
	-- plain portal syntax
	str = string.gsub(str, "^[Pp]ortal:(.*)$", function (target)
		if nowiki then
			return target
		else
			return "[[Portal:" .. target .. "|" .. target .. "]]"
		end
	end)
	
	-- strip "Author:" prefixes if not piped
	str = string.gsub(str, "%[%[%s*[Aa]uthor:([^|]-)|?%]%]", function (target)
		return "[[Author:" .. target .. "|" .. target .. "]]"
	end)
	-- plain author syntax
	str = string.gsub(str, "^[Aa]uthor:(.*)$", function (target)
		if nowiki then
			return target
		else
			return "[[Author:" .. target .. "|" .. target .. "]]"
		end
	end)
	
	-- auto-strip bracketed dates
	if not nowiki then
		str = string.gsub(str, "^((.*) +%([0-9]+[-–][0-9]+%))$", function (full_target, no_date)
			return "[[Author:" .. full_target .. "|" .. no_date .. "]]"
		end, 1)
	end
	
	-- if the string has its own links, return it now
	if string.match(str, "%[%[") then
		return str
	end
	
	-- if the author is anonymous
	if string.match(str, "[uU]nknown") or string.match(str, "[aA]non") or string.match(str, "[aA]nonymous") then
		if nowiki then
			return "Anonymous"
		else
			return "[[Portal:Anonymous texts|Anonymous]]"
		end
	end
	
	if string.match(str, "[Vv]arious") then
		return "Various authors"
	end
	
	-- if nowiki then don't make link
	if nowiki then
		return str
	end
	
	-- if a pipe is provided
	if string.match(str, "|") then
		return "[[Author:" .. str .. "]]"
	end
	
	-- make our own piped link
	return "[[Author:" .. str .. "|" .. str .. "]]"
end

--[=[
Format new items
]=]
function p._new_texts_item(args)
	-- parse args
	local image_name = args.image_name
	local image_size = args.image_size or '100px'
	
	local title = args.title or args[1]
	local display = args.display or title
	local edition = args.edition
	
	local work_date = args['date'] or args[3]
	local work_type = args['type']
	
	local nowiki = yesno(args.nowiki) or false
	local author = args.author or args[2]
	local translator = args.translator
	local translation_date = args.translation_date
	local editor = args.editor
	local illustrator = args.illustrator
	
	local text_item = {}
	
	-- image
	if image_name then
		-- no link to image's page in the File namespace
		table.insert(text_item, "[[File:" .. image_name .. "|right|" .. image_size .. "|link=]]")
	end
	
	-- title
	if title then
		local title_text = mw.html.create('span')
			:css({['font-style'] = 'italic', ['font-weight'] = 'bold'})
			:wikitext('[[' .. title .. '|' .. display .. ']]')
		table.insert(text_item, tostring(title_text))
	else
		table.insert(text_item, error_function({"Error: No title entered"}))
	end
	
	-- edition
	if edition then
		table.insert(text_item, ', ' .. edition)
	end
	
	-- date/type
	local work_info = {}
	if work_date then
		table.insert(work_info, work_date)
	end
	if work_date and translation_date then
		table.insert(work_info, ',')
	end
	if translation_date then
		table.insert(work_info, 'tr. ' .. translation_date)
	end
	if work_type == 'film' then
		table.insert(work_info, require('Module:Media')._media({[1] = 15, ['type'] = 'film'}))
	end
	if #work_info > 0 then
		table.insert(text_item, ' (' .. table.concat(work_info, ' ') .. ')')
	end
	
	-- creators
	local creators = {}
	if author then
		table.insert(creators, 'by ' .. construct_author(author, nowiki))
	end
	if translator then
		table.insert(creators, 'translated by ' .. construct_author(translator, nowiki))
	end
	if editor then
		table.insert(creators, 'edited by ' .. construct_author(editor, nowiki))
	end
	if illustrator then
		table.insert(creators, 'illustrated by ' .. construct_author(illustrator, nowiki))
	end
	if #creators > 0 then
		local creators_div = mw.html.create('div')
			:css({['margin-left'] = '1em', ['font-size'] = '83%'})
			:addClass('creators')
			:wikitext(table.concat(creators, ', '))
		table.insert(text_item, tostring(creators_div))
	end
	
	return table.concat(text_item)
end
function p.new_texts_item(frame)
	return p._new_texts_item(getArgs(frame))
end

--[=[
Construct an author link for the given key (e.g "author" or "translator")
]=]
local function construct_author_from_item(item, key, nowiki)
	if not item or not item[key] then
		return nil
	end
	
    -- explicit nowiki
	if item[key .. "_nowiki"] then
		return item[key]	
	end
	
	local author_data = item[key]
	
	if type(author_data) ~= 'table' then
		author_data = {author_data}
	end
	
	local entries = {}
	for k, v in pairs(author_data) do
		table.insert(entries, construct_author(v, nowiki))
	end
	
	if #entries == 0 then
		return nil
	elseif #entries == 1 then
		return entries[1]
	else
		return table.concat(entries, ', ', 1, #entries - 1) .. ' and ' .. entries[#entries]
	end
end

--[=[
Construct an entry using new_texts_item with the data from item
]=]
local function item_to_new_texts_item(item)
	-- suppress auto links for the authors, etc
	local nowiki = yesno(item.nowiki)
	
	local args = {
		["nowiki"] = "yes", -- we always construct this ourselves
		
		["image_name"] = item["image_name"],
		["image_size"] = item["image_size"],
		
		["title"] = item["title"],
		
		["author"] = construct_author_from_item(item, "author", nowiki),
		["edition"] = item["edition"],
		["editor"] = construct_author_from_item(item, "editor", nowiki),
		["illustrator"] = construct_author_from_item(item, "editor", nowiki),
		["translator"] = construct_author_from_item(item, "translator", nowiki),
		["translation_date"] = item["translation_year"],
		
		["date"] = item["year"],
		
		["display"] = item["display"]
	}
	
	if not args["display"] then
		-- if title is a subpage of some other title, use subpage as display title
		local mwt = mw.title.new(tostring(args["title"]))
		if mwt and mwt.isSubpage then
			args["display"] = mwt.subpageText
		end
	end
	
	return p._new_texts_item(args)
end

local function table_len(data)
	-- count the items (#data doesn't work because keys are non-numeric)
	local count = 0
	for k, _ in pairs(data) do
		count = count + 1
	end
	return count
end

--[=[
Construct the new texts list from instances of new_texts_item

Arguments:
* limit: how many items to display
* offset: how many items to skip the display of

not in use AFAICT
]=]
function p._new_texts(args)
	-- pull in the data from the data module at [[Template:New texts/data(/YEAR).json]]
	local data = get_data(args["data_source"], nil)
	
	-- how many items to show
	local offset = tonumber(args["offset"]) or 0
	local limit = tonumber(args["limit"]) or 7
	
	-- count the months
	local months = table_len(data)
	local texts = {}
	
	local count = 0
	local broken = false
	-- iterate in reverse, because we want the most recent months
	for i = months, 1, -1 do
		local month = data[i]
		
		for k, v in pairs(month) do
			if count >= offset then
				table.insert(texts, item_to_new_texts_item(v))
			end
			
			count = count + 1
			if count >= limit + offset then
				broken = true
				break
			end
		end
		
		if broken then
			break
		end
	end
	return table.concat(texts, '\n')
end
function p.new_texts(frame)
	return p._new_texts(getArgs(frame))
end

local function construct_month_list(items, start_num)
	local count = 0
	local first_in_month = true
	local items_len = table_len(items)
	
	if items_len == 0 then
		return '', count
	end
	
	local ol = mw.html.create('ol')
	
	for i = items_len, 1, -1 do
		local li = ol:tag('li')
		
		if i == items_len then
			li:attr('value', start_num + 1)
		end
		li:wikitext(item_to_new_texts_item(items[i]))
		
		count = count + 1
	end
	
	return ol, count
end

--[=[
Construct the list of archived items for the given month

Arguments:
* month: the month to show (nil to show whole year)
* year: the year to show
]=]
function p._archive_list(args)
	local month = tonumber(args["month"])
	local year = tonumber(args["year"]) or DEFAULTS["currentyear"]
	
	-- pull in the data from the relevant archive
	local data = get_data(args["data_source"], year)
	local a_list = mw.html.create('div')
	local count = 1
	
	local months
	if month then
		months = {}
		months[month] = data[month]
	else
		months = data
	end
	
	local count = 0
	local max_m = table_len(months)
	
	for k, m in pairs(months) do
		a_list:tag('span')
			:attr('id', string.format("%02d", k))
		a_list:tag('h2')
			:wikitext(os.date("%B", os.time({year = year, month = k, day = 1})))
		local content, m_count = construct_month_list(m, count)
		a_list:node(content)
		count = count + m_count
	end
	
	return a_list
end
function p.archive_list(frame)
	return p._archive_list(getArgs(frame))
end

--[=[
Construct the process header for the list of archived items for the given year

Arguments:
* year: the year to show
]=]
function p._archive_list_header(args)
	local year = tonumber(args["year"]) or tonumber(args[1]) or tonumber(mw.title.getCurrentTitle().subpageText)
	
	if not year then
		return error_function({"[[Module:New texts]] error: No year provided"})
	end
	
	local nextlink
	if year < DEFAULTS["currentyear"] then
		nextlink = "[[Wikisource:Works/" .. year + 1 .. "|" .. year + 1 .. "]]"
	end
	
	return require('Module:Process header')._process_header({
		['title'] = 'Proofread works added in ' .. year,
		['previous'] = "[[Wikisource:Works/" .. year - 1 .. "|" .. year - 1 .. "]]",
		['next'] = nextlink,
		['notes'] = 'These works are from [[Help:DjVu files|scanned texts]] and have been [[Help:proofread|proofread]] at least once, if not fully validated.'
	})
end
function p.archive_list_header(frame)
	return p._archive_list_header(getArgs(frame))
end

--[=[
Combine archive_list and archive_list_header to implement Template:New texts archive

Arguments:
* year: the year to show
]=]

function p.new_texts_archive(frame)
	local args = getArgs(frame)
	args.year = args.year or args[1]
	
	local header = p._archive_list_header(args)
	
	local styles = frame:extensionTag('templatestyles', '', {src = 'Template:Block right/styles.css'})
	local toc = mw.html.create('div')
		:addClass('wst-block-right')
		:wikitext(frame:preprocess('__TOC__'))
	local clear = mw.html.create('div'):css({['clear'] = 'both'})
	
	local list = p._archive_list(args)
	
	return styles .. header .. tostring(toc) .. tostring(clear) .. tostring(list)
end

return p