Functions to provide lists of new works shown at {{new texts}} (and therefore also the Main page)

Add work data to Template:New texts/data/२०२४.json.

Archived data is pulled from Template:New texts/data/YYYY.json.

  • new_texts: function called by {{new texts}} for a list of recent new text
    • limit: how many items to show
    • skip: how many items to skip
    • data_source: alternative source base title for data (default is Template:New texts/data. Year and .json are appended.
  • archive_list: function called by {{new texts archive}} for a list of new texts by year
    • year: the year to show, omit for current year
    • month: the month to show, omit for entire year
    • data_source: alternative source base title for data (default is Template:New texts/data. Year and .json are appended.
Archives of New texts (+/−)
2022: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2021: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2020: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2019: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2018: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2017: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2016: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2015: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2014: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2013: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2012: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2011: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
2010: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec

-- This is an module to implement listings of new texts

local p = {} --p stands for package

local getArgs = require('Module:Arguments').getArgs

local DEFAULTS = {
	data_source = "Template:New texts/data",
}

function is_table(t)
	return type(t) == 'table'
end

function get_data(src, year)
	local data
	
	if src == nil then
		src = DEFAULTS["data_source"]
	end

	if year == "" or year == nil then
		year = os.date( '%Y' )
	end
	
	src = src .. "/" .. year .. ".json"
	
	local data
	if pcall(function()
		local jsondata = mw.title.new(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 an author link from a given string
function construct_author_link(str)
	
	mw.log(str)
	
	-- strip "Portal:" prefixes if not piped
	str = string.gsub(str, "%[%[%s*Portal:([^|]-)|?%]%]", function (target)
		return "[[Portal:" .. target .. "|" .. target .. "]]"
	end)
	-- plain portal syntax
	str = string.gsub(str, "^[Pp]ortal:(.*)$", function (target)
		return "[[Portal:" .. target .. "|" .. target .. "]]"
	end)
	
	-- And for authors
	str = string.gsub(str, "%[%[%s*[Aa]uthor:([^|]-)|?%]%]", function (target)
		return "[[Author:" .. target .. "|" .. target .. "]]"
	end)
	
	-- auto-strip bracketed dates
	str = string.gsub(str, "^((.*) +%([0-9]+[-–][0-9]+%))$", function (full_target, no_date)
		return "[[Author:" .. full_target .. "|" .. no_date .. "]]"
	end, 1)

	-- if the string has its own links, return it now
	if string.match(str, "%[%[") then
		return str
	end

	if string.match(str, "[uU]nknown") then
		return "[[Portal:Anonymous texts|Anonymous]]"
	end
	
	if string.match(str, "[Vv]arious") then
		return "Various authors"
	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

-- construct a list like "a, b and c"
function comma_and_list(entries)
	local s = ""
	
	--mw.logObject(entries)
	
	for k, v in pairs(entries) do
		
		if k > 1 and #entries > 1 then
			if k == #entries then
				s = s .. " and "
			else
				s = s .. ", "
			end
		end
		
		s = s .. v
	end

	return s
end

--[=[
Construct an author link for the given key (e.g "author" or "translator")

If nowiki is given, 
]=]
function construct_author(item, key, nowiki)

    -- explicit nowiki, or the string contains its own links
	if item[key .. "_nowiki"] then
		return item[key]	
	end
	
	local tab = item[key]
	
	if not is_table(tab) then
		tab = {tab}
	end
	
	entries = {}
	for k, v in pairs(tab) do
		if nowiki then
			table.insert(entries, v)
		else
			table.insert(entries, construct_author_link(v))
		end
	end

	local s = comma_and_list(entries)
	return s
end

--[=[
Construct a display text for a title

Nil if no display text seems sensible
]=]
function construct_display(title)
	mwt = mw.title.new(title)
	
	if mwt and mwt.isSubpage then
		return mwt.subpageText	
	end
	
	return nil
end

--[=[
Construct template arguments for an entry using {{new texts/item}}
]=]
function get_item_template_args(frame, item)
	
	-- suppress auto links for the authors, etc
	local nowiki = item.nowiki
	
	args = {
		item["title"],
		construct_author(item, "author", nowiki),
		item["year"],
		["nowiki"] = "yes" -- we always construct this ourselves
	}
	
	if item["display"] then
		args["display"] = item["display"]
	else 
		local auto_display = construct_display(item["title"])
		if auto_display then
			args["display"] = auto_display
		end
	end
	
	if item["image_name"] then
		args["image_name"] = item["image_name"]
	end

	if item["image_size"] then
		args["image_size"] = item["image_size"]
	end
	
	if item["edition"] then
		args["edition"] = item["edition"]
	end
	
	if item["translator"] then
		local trans = construct_author(item, "translator", nowiki)

		if item["translation_year"] then
			trans = trans .. " (" .. item["translation_year"]	.. ")"
		end

		args["translator"] = trans
	end
	
	if item["editor"] then
		local editor = construct_author(item, "editor", nowiki)
		args["editor"] = editor
	end
	
	if item["illustrator"] then
		local illustrator = construct_author(item, "illustrator", nowiki)
		args["illustrator"] = illustrator
	end
	
	if item["type"] == "film" then
		local film_indicator = frame:expandTemplate{title="media", args={15, type = "film"}}
		args[3] = mw.text.trim( args[3] ) .. ", " .. film_indicator
	end
	
	return args
end

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

function month_name(n)
	return os.date("%B", os.time({year=2000, month=n, day=1}))
end

--[=[
Construct the new tests list from instances of {{new texts/item}}

Arguments:
* limit: how many items to display
* offset: how many items to skip the display of
]=]
function p.new_texts(frame)
	
	local args = getArgs(frame)
	
	-- 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 = 0
	if args["offset"] ~= nil and args["offset"] ~= "" then
		offset = tonumber(args["offset"])
	end
	
	local limit = 7
	if args["limit"] ~= nil and args["limit"] ~= "" then
		limit = tonumber(args["limit"])
	end
	
	-- count the months (#data doesn't work?)
	local months = table_len(data)
	
	s = ""
	count = 0 
	-- iterate in reverse, because we want the most recent months
	for i = months, 1, -1 do
		
		local month = data[i]
		
		local broken = false
		
		for k, v in pairs(month) do
	
			if count >= offset then
				local template_args = get_item_template_args(frame, v)
				s = s .. frame:expandTemplate{title="new texts/item", args=template_args} .. "\n"
			end
			
			count = count + 1

			if count >= limit + offset then
				broken = true
				break
			end
		end
		
		if broken then
			break
		end
	end
	return s
end

function construct_month_list(frame, items, start_num)
	
	local s = ""
	local count = 0
	local first_in_month = true
	for i=table_len(items), 1, -1 do
		
		local v = items[i]

		local template_args = get_item_template_args(frame, v)

		s = s .. "#"
		
		if first_in_month then
			s = s .. "<li value=\"" .. (start_num + 1) .. "\">"
		end
		
		s = s .. " " .. frame:expandTemplate{title="new texts/item", args=template_args} .. "\n"
		
		count = count + 1
		-- reset
		first_in_month = false
	end
	
	return s, count
end

function construct_anchor(id)
	local anchor = mw.html.create("span")
		:attr("id", id)
	return tostring(anchor)
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(frame)
	
	local args = getArgs(frame)

	local args = frame["args"]
	local month = tonumber(args["month"])
	local year = tonumber(args["year"])
	
	-- pull in the data from the relevant archive
	local data = get_data(args["data_source"], year)

	local s = ""
	
	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
		local numeric_anchor = string.format("%02d", k)
		s = s .. construct_anchor(numeric_anchor) .. "\n"
		s = s .. "==" .. month_name(k) .. "==\n\n"
		
		--mw.logObject(m)
		content, m_count = construct_month_list(frame, m, count)
		
		s = s .. content
		count = count + m_count
	end

	return s
end

return p