<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://mywikibiz.com/index.php?action=history&amp;feed=atom&amp;title=Module%3ASignpost</id>
	<title>Module:Signpost - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://mywikibiz.com/index.php?action=history&amp;feed=atom&amp;title=Module%3ASignpost"/>
	<link rel="alternate" type="text/html" href="https://mywikibiz.com/index.php?title=Module:Signpost&amp;action=history"/>
	<updated>2026-06-14T00:36:30Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.35.3</generator>
	<entry>
		<id>https://mywikibiz.com/index.php?title=Module:Signpost&amp;diff=479442&amp;oldid=prev</id>
		<title>Zoran: Pywikibot 6.4.0</title>
		<link rel="alternate" type="text/html" href="https://mywikibiz.com/index.php?title=Module:Signpost&amp;diff=479442&amp;oldid=prev"/>
		<updated>2021-07-16T06:01:58Z</updated>

		<summary type="html">&lt;p&gt;Pywikibot 6.4.0&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local INDEX_MODULE = 'Module:Signpost/index'&lt;br /&gt;
local lang = mw.language.getContentLanguage()&lt;br /&gt;
local libraryUtil = require('libraryUtil')&lt;br /&gt;
local checkType = libraryUtil.checkType&lt;br /&gt;
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Article class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local Article = {}&lt;br /&gt;
Article.__index = Article&lt;br /&gt;
&lt;br /&gt;
Article.rowMethods = {&lt;br /&gt;
	page = 'getPage',&lt;br /&gt;
	fullpage = 'getFullPage',&lt;br /&gt;
	date = 'getDate',&lt;br /&gt;
	title = 'getTitle',&lt;br /&gt;
	subpage = 'getSubpage',&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function Article.new(data)&lt;br /&gt;
	local self = setmetatable({}, Article)&lt;br /&gt;
	self.data = data&lt;br /&gt;
	self.matchedTags = {}&lt;br /&gt;
	return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:getSortKey()&lt;br /&gt;
	return self.data.sortKey&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:getPage()&lt;br /&gt;
	return self.data.page&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:getDate()&lt;br /&gt;
	return self.data.date&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:getTitle()&lt;br /&gt;
	return self.data.title&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:getSubpage()&lt;br /&gt;
	return self.data.subpage&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:getFragment()&lt;br /&gt;
	local fragment = self:getMatchedTags()[1]&lt;br /&gt;
	if fragment then&lt;br /&gt;
		return mw.uri.anchorEncode(fragment)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:getFullPage()&lt;br /&gt;
	local page = self:getPage()&lt;br /&gt;
	local fragment = self:getFragment()&lt;br /&gt;
	if fragment then&lt;br /&gt;
		return page .. '#' .. fragment&lt;br /&gt;
	else&lt;br /&gt;
		return page&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:addMatchedTag(tag)&lt;br /&gt;
	table.insert(self.matchedTags, tag)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:getMatchedTags()&lt;br /&gt;
	table.sort(self.matchedTags)&lt;br /&gt;
	return self.matchedTags&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:hasAllTags(t)&lt;br /&gt;
	local tags = self.data.tags&lt;br /&gt;
	for i, testTag in ipairs(t) do&lt;br /&gt;
		local hasTag = false&lt;br /&gt;
		for j, tag in ipairs(tags) do&lt;br /&gt;
			if tag == testTag then&lt;br /&gt;
				hasTag = true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if not hasTag then&lt;br /&gt;
			return false&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:makeRowArgs()&lt;br /&gt;
	local methods = self.rowMethods&lt;br /&gt;
	local args = setmetatable({}, {&lt;br /&gt;
		__index = function (t, key)&lt;br /&gt;
			local method = methods[key]&lt;br /&gt;
			if method then&lt;br /&gt;
				return self[method](self)&lt;br /&gt;
			else&lt;br /&gt;
				error(string.format(&lt;br /&gt;
					&amp;quot;'%s' is not a valid parameter name&amp;quot;,&lt;br /&gt;
					key&lt;br /&gt;
				), 2)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	})&lt;br /&gt;
	return args&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:renderTemplate(template, frame)&lt;br /&gt;
	frame = frame or mw.getCurrentFrame()&lt;br /&gt;
	local args = {}&lt;br /&gt;
	for key, method in pairs(self.rowMethods) do&lt;br /&gt;
		args[key] = self[method](self)&lt;br /&gt;
	end&lt;br /&gt;
	return frame:expandTemplate{&lt;br /&gt;
		title = template,&lt;br /&gt;
		args = args&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Article:renderFormat(format)&lt;br /&gt;
	local args = self:makeRowArgs(articleObj)&lt;br /&gt;
	local ret = format:gsub('(%${(%a+)})', function (match, key)&lt;br /&gt;
		return args[key] or match&lt;br /&gt;
	end)&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- List class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local List = {}&lt;br /&gt;
List.__index = List&lt;br /&gt;
&lt;br /&gt;
function List.new(options)&lt;br /&gt;
	checkType('List.new', 1, options, 'table')&lt;br /&gt;
	checkTypeForNamedArg('List.new', 'args', options.args, 'table', true)&lt;br /&gt;
	local self = setmetatable({}, List)&lt;br /&gt;
	self.index = options.index or mw.loadData(INDEX_MODULE)&lt;br /&gt;
	self.frame = options.frame or mw.getCurrentFrame()&lt;br /&gt;
	local args = options.args or {}&lt;br /&gt;
&lt;br /&gt;
	-- Set output formats&lt;br /&gt;
	if not options.suppressFormatErrors&lt;br /&gt;
		and args.rowtemplate&lt;br /&gt;
		and args.rowformat&lt;br /&gt;
	then&lt;br /&gt;
		error(&amp;quot;you cannot use both the 'rowtemplate' and the 'rowformat' arguments&amp;quot;, 2)&lt;br /&gt;
	elseif not options.suppressFormatErrors&lt;br /&gt;
		and not args.rowtemplate&lt;br /&gt;
		and not args.rowformat&lt;br /&gt;
	then&lt;br /&gt;
		error(&amp;quot;you must use either the 'rowtemplate' or the 'rowformat' argument&amp;quot;, 2)&lt;br /&gt;
	else&lt;br /&gt;
		self.rowtemplate = args.rowtemplate&lt;br /&gt;
		self.rowformat = args.rowformat&lt;br /&gt;
	end&lt;br /&gt;
	if args.rowseparator == 'newline' then&lt;br /&gt;
		self.rowseparator = '\n'&lt;br /&gt;
	else&lt;br /&gt;
		self.rowseparator = args.rowseparator&lt;br /&gt;
	end&lt;br /&gt;
	self.noarticles = args.noarticles&lt;br /&gt;
	&lt;br /&gt;
	-- Get article objects, filtered by page, date and tag, and sort them.&lt;br /&gt;
	if args.page then&lt;br /&gt;
		self.articles = { self:getPageArticle(args.page) }&lt;br /&gt;
	elseif args.date then&lt;br /&gt;
		self.articles = self:getDateArticles(args.date)&lt;br /&gt;
	else&lt;br /&gt;
		self.articles = self:getTagArticles(args.tags, args.tagmatch)&lt;br /&gt;
		if not self.articles then&lt;br /&gt;
			self.articles = self:getAllArticles()&lt;br /&gt;
		end&lt;br /&gt;
		self:filterArticlesByDate(args.startdate, args.enddate)&lt;br /&gt;
	end&lt;br /&gt;
	self:sortArticles(args.sortdir, args.sortfield)&lt;br /&gt;
	if (args.limit and tonumber(args.limit)) or (args.start and tonumber(args.start)) then&lt;br /&gt;
		self:limitArticleCount(tonumber(args.start), tonumber(args.limit))&lt;br /&gt;
	end&lt;br /&gt;
	return self&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Static methods&lt;br /&gt;
&lt;br /&gt;
function List.normalizeDate(date)&lt;br /&gt;
	if not date then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	return lang:formatDate('Y-m-d', date)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Normal methods&lt;br /&gt;
&lt;br /&gt;
function List:parseTagString(s)&lt;br /&gt;
	local ret = {}&lt;br /&gt;
&lt;br /&gt;
	-- Remove whitespace and punctuation&lt;br /&gt;
	for i, tag in ipairs(mw.text.split(s, ',')) do&lt;br /&gt;
		tag = mw.ustring.gsub(tag, '[%s%p]', '')&lt;br /&gt;
		if tag ~= '' then&lt;br /&gt;
			tag = mw.ustring.lower(tag)&lt;br /&gt;
			table.insert(ret, tag)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Resolve aliases&lt;br /&gt;
	for i, tag in ipairs(ret) do&lt;br /&gt;
		ret[i] = self.index.aliases[tag] or tag&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Remove duplicates&lt;br /&gt;
	local function removeDuplicates(t)&lt;br /&gt;
		local vals, ret = {}, {}&lt;br /&gt;
		for i, val in ipairs(t) do&lt;br /&gt;
			vals[val] = true&lt;br /&gt;
		end&lt;br /&gt;
		for val in pairs(vals) do&lt;br /&gt;
			table.insert(ret, val)&lt;br /&gt;
		end&lt;br /&gt;
		table.sort(ret)&lt;br /&gt;
		return ret&lt;br /&gt;
	end&lt;br /&gt;
	ret = removeDuplicates(ret)&lt;br /&gt;
&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function List:getPageArticle(page)&lt;br /&gt;
	local data = self.index.pages[page]&lt;br /&gt;
	if data then&lt;br /&gt;
		return Article.new(data)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function List:getDateArticles(date)&lt;br /&gt;
	date = self.normalizeDate(date)&lt;br /&gt;
	local dates = self.index.dates[date]&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	if dates then&lt;br /&gt;
		for i, data in ipairs(dates) do&lt;br /&gt;
			ret[i] = Article.new(data)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function List:getTagArticles(s, tagMatch)&lt;br /&gt;
	if not s then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local tagIndex = self.index.tags&lt;br /&gt;
	local ret, pages = {}, {}&lt;br /&gt;
	local tags = self:parseTagString(s)&lt;br /&gt;
	for i, tag in ipairs(tags) do&lt;br /&gt;
		local dataArray = tagIndex[tag]&lt;br /&gt;
		if dataArray then&lt;br /&gt;
			for i, data in ipairs(dataArray) do&lt;br /&gt;
				local obj = Article.new(data)&lt;br /&gt;
				-- Make sure we only have one object per page.&lt;br /&gt;
				if pages[obj:getPage()] then&lt;br /&gt;
					obj = pages[obj:getPage()]&lt;br /&gt;
				else&lt;br /&gt;
					pages[obj:getPage()] = obj&lt;br /&gt;
				end&lt;br /&gt;
				-- Record which tag we matched.&lt;br /&gt;
				obj:addMatchedTag(tag)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	for page, obj in pairs(pages) do&lt;br /&gt;
		if not tagMatch&lt;br /&gt;
			or tagMatch == 'any'&lt;br /&gt;
			or tagMatch == 'all' and obj:hasAllTags(tags)&lt;br /&gt;
		then&lt;br /&gt;
			table.insert(ret, obj)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function List:getAllArticles()&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	for i, data in ipairs(self.index.list) do&lt;br /&gt;
		ret[i] = Article.new(data)&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function List:getArticleCount()&lt;br /&gt;
	return #self.articles&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function List:filterArticlesByDate(startDate, endDate)&lt;br /&gt;
	startDate = self.normalizeDate(startDate) or '2005-01-01'&lt;br /&gt;
	endDate = self.normalizeDate(endDate) or lang:formatDate('Y-m-d')&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	for i, article in ipairs(self.articles) do&lt;br /&gt;
		local date = article:getDate()&lt;br /&gt;
		if startDate &amp;lt;= date and date &amp;lt;= endDate then&lt;br /&gt;
			table.insert(ret, article)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	self.articles = ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function List:sortArticles(direction, field)&lt;br /&gt;
	local accessor&lt;br /&gt;
	if not field or field == 'date' then&lt;br /&gt;
		accessor = function (article) return article:getSortKey() end&lt;br /&gt;
	elseif field == 'page' then&lt;br /&gt;
		accessor = function (article) return article:getPage() end&lt;br /&gt;
	elseif field == 'title' then&lt;br /&gt;
		accessor = function (article) return article:getTitle() end&lt;br /&gt;
	else&lt;br /&gt;
		error(string.format(&amp;quot;'%s' is not a valid sort field&amp;quot;, field), 2)&lt;br /&gt;
	end&lt;br /&gt;
	local sortFunc&lt;br /&gt;
	if not direction or direction == 'ascending' then&lt;br /&gt;
		sortFunc = function (a, b)&lt;br /&gt;
			return accessor(a) &amp;lt; accessor(b)&lt;br /&gt;
		end&lt;br /&gt;
	elseif direction == 'descending' then&lt;br /&gt;
		sortFunc = function (a, b)&lt;br /&gt;
			return accessor(a) &amp;gt; accessor(b)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		error(string.format(&amp;quot;'%s' is not a valid sort direction&amp;quot;, direction), 2)&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(self.articles, sortFunc)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function List:limitArticleCount(start, limit) &lt;br /&gt;
	local ret = {}&lt;br /&gt;
	for i, article in ipairs(self.articles) do &lt;br /&gt;
		if limit and #ret &amp;gt;= limit then&lt;br /&gt;
			break&lt;br /&gt;
		end&lt;br /&gt;
		if not start or i &amp;gt; start then&lt;br /&gt;
			table.insert(ret, article)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	self.articles = ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function List:renderRow(articleObj)&lt;br /&gt;
	if self.rowtemplate then&lt;br /&gt;
		return articleObj:renderTemplate(self.rowtemplate, self.frame)&lt;br /&gt;
	elseif self.rowformat then&lt;br /&gt;
		return articleObj:renderFormat(self.rowformat)&lt;br /&gt;
	else&lt;br /&gt;
		error('neither rowtemplate nor rowformat were specified')&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function List:__tostring()&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	for i, obj in ipairs(self.articles) do&lt;br /&gt;
		table.insert(ret, self:renderRow(obj))&lt;br /&gt;
	end&lt;br /&gt;
	if #ret &amp;lt; 1 then&lt;br /&gt;
		return self.noarticles&lt;br /&gt;
			or '&amp;lt;span style=&amp;quot;font-color: red;&amp;quot;&amp;gt;' ..&lt;br /&gt;
			'No articles found for the arguments specified&amp;lt;/span&amp;gt;'&lt;br /&gt;
	else&lt;br /&gt;
		return table.concat(ret, self.rowseparator)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Exports&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local function makeInvokeFunc(func)&lt;br /&gt;
	return function (frame, index)&lt;br /&gt;
		local args = require('Module:Arguments').getArgs(frame, {&lt;br /&gt;
			parentOnly = true&lt;br /&gt;
		})&lt;br /&gt;
		return func(args, index)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._exportClasses()&lt;br /&gt;
	return {&lt;br /&gt;
		Article = Article,&lt;br /&gt;
		List = List&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._count(args, index)&lt;br /&gt;
	local list = List.new{&lt;br /&gt;
		args = args,&lt;br /&gt;
		index = index,&lt;br /&gt;
		suppressFormatErrors = true&lt;br /&gt;
	}&lt;br /&gt;
	return list:getArticleCount()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.count = makeInvokeFunc(p._count)&lt;br /&gt;
&lt;br /&gt;
function p._main(args, index)&lt;br /&gt;
	return tostring(List.new{args = args, index = index})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.main = makeInvokeFunc(p._main)&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
</feed>