Jump to content

Module:Signpost

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Mr. Stradivarius (talk | contribs) at 05:52, 20 March 2015 (allow matching any or all tags). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

local INDEX_MODULE = 'Module:Signpost/index'
local lang = mw.language.getContentLanguage()

--------------------------------------------------------------------------------
-- Article class
--------------------------------------------------------------------------------

local Article = {}
Article.__index = Article

function Article.new(data)
	local self = setmetatable({}, Article)
	self.data = data
	return self
end

function Article:getSortKey()
	return self.data.sortKey
end

function Article:getPage()
	return self.data.page
end

function Article:getDate()
	return self.data.date
end

function Article:getTitle()
	return self.data.title
end

function Article:getSubpage()
	return self.data.subpage
end

function Article:hasAllTags(t)
	local tags = self.data.tags
	for i, testTag in ipairs(t) do
		local hasTag = false
		for j, tag in ipairs(tags) do
			if tag == testTag then
				hasTag = true
			end
		end
		if not hasTag then
			return false
		end
	end
	return true
end

--------------------------------------------------------------------------------
-- List class
--------------------------------------------------------------------------------

local List = {}
List.__index = List

List.rowParams = {
	PAGE = 'getPage',
	DATE = 'getDate',
	TITLE = 'getTitle',
	SUBPAGE = 'getSubpage',
}

function List.new(args, index)
	local self = setmetatable({}, List)
	self.index = index or mw.loadData(INDEX_MODULE)
	self.rowtemplate = args.rowtemplate
	self.rowformat = args.rowformat
	
	-- Get article objects, filtered by page, date and tag, and sort them.
	if args.page then
		self.articles = { self:getPageArticle(args.page) }
	elseif args.date then
		self.articles = self:getDateArticles(args.date)
	else
		self.articles = self:getTagArticles(args.tags, args.tagmatch)
		if not self.articles then
			self.articles = self:getAllArticles()
		end
		self:filterArticlesByDate(args.startdate, args.enddate)
	end
	self:sortArticles(args.sortdir, args.sortfield)

	return self
end

-- Static methods

function List.parseTagString(s)
	local tags = mw.text.split(s, '%s*,%s')
	local ret = {}
	for i, tag in ipairs(tags) do
		if tag ~= '' then
			table.insert(ret, mw.ustring.lower(tag))
		end
	end
	return ret
end

function List.normalizeDate(date)
	if not date then
		return nil
	end
	return lang:formatDate('Y-m-d', date)
end

-- Normal methods

function List:getPageArticle(page)
	local data = self.index.pages[page]
	if not data then
		error(string.format(
			'[[%s]] is not an indexed Signpost article',
			page
		), 2)
	end
	return Article.new(data)
end

function List:getDateArticles(date)
	date = self.normalizeDate(date)
	local dates = self.index.dates[date]
	if not dates then
		error(string.format(
			'no Signpost articles are indexed for the date %s',
			date
		), 2)
	end
	local ret = {}
	for i, data in ipairs(dates) do
		ret[i] = Article.new(data)
	end
	return ret
end

function List:getTagArticles(s, tagMatch)
	if not s then
		return nil
	end
	local tagIndex = self.index.tags
	local ret, pages = {}, {}
	local tags = self.parseTagString(s)
	for i, tag in ipairs(tags) do
		local data = tagIndex[tag]
		if data then
			local obj = Article.new(data)
			pages[obj:getPage()] = obj
		end
	end
	for page, obj in pairs(pages) do
		if not tagMatch
			or tagMatch == 'any'
			or tagMatch == 'all' and obj:hasAllTags(tags)
		then
			table.insert(ret, obj)
		end
	end
	if #ret < 1 then
		error(string.format('no valid tags found in the string "%s"', s), 2)
	end
	return ret
end

function self:getAllArticles()
	local ret = {}
	for i, data in ipairs(self.index.list) do
		ret[i] = Article.new(data)
	end
	return ret
end

function List:filterArticlesByDate(startDate, endDate)
	startDate = self.normalizeDate(startDate) or '2005-01-01'
	endDate = self.normalizeDate(endDate) or lang:formatDate('Y-m-d')
	local ret = {}
	for i, article in ipairs(self.articles) do
		local date = article:getDate()
		if startDate <= date and date <= endDate then
			table.insert(ret, article)
		end
	end
	self.articles = ret
end

function List:sortArticles(direction, field)
	local accessor
	if not field or field == 'date' then
		accessor = function (article) return article:getSortKey() end
	elseif field == 'page' then
		accessor = function (article) return article:getPage() end
	elseif field == 'title' then
		accessor = function (article) return article:getTitle() end
	else
		error(string.format("'%s' is not a valid sort field", field), 2)
	end
	local sortFunc
	if not direction or direction == 'ascending' then
		sortFunc = function (a, b)
			return accessor(a) < accessor(b)
		end
	elseif direction == 'descending' then
		sortFunc = function (a, b)
			return accessor(a) > accessor(b)
		end
	else
		error(string.format("'%s' is not a valid sort direction", direction), 2)
	end
	table.sort(self.articles, sortFunc)
end

--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------

local p = {}

function p._tag(tag)
	local data = mw.loadData(INDEX_MODULE)
	if not tag then
		return nil
	end
	local articles = data.tags[tag]
	if not articles then
		return nil
	end
	local ret = {}
	ret[#ret + 1] = '<ul>'
	for i, t in ipairs(articles) do
		ret[#ret + 1] = string.format(
			'<li>%s, %s: [[%s|%s]]</li>',
			t.date, t.subpage, t.page, t.title
		)
	end
	ret[#ret + 1] = '</ul>'
	return table.concat(ret, '\n')
end

function p.tag(frame)
	local tag = frame.args[1]
	return p._tag(tag)
end

return p