Difference between revisions of "Module:Automatic archive navigator/sandbox"

From blackwiki
Jump to navigation Jump to search
blackwiki>Mr. Stradivarius
(move getSubpageLink comment inside the function body)
m (52 revisions imported)
 
(48 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 +
-------------------------------------------------------------------------------
 +
--                      Automatic archive navigator
 
--
 
--
-- This module implements {{Automatic archive navigator}}
+
-- This module produces a talk archive banner, together with an automatically-
--
+
-- generated list of navigation links to other archives of the talk page in
+
-- question. It implements {{Automatic archive navigator}} and
local p = {}
+
-- {{Talk archive navigation}}.
 +
-------------------------------------------------------------------------------
 +
 
 +
local yesno = require('Module:Yesno')
  
local messageBox = require('Module:Message box')
+
-------------------------------------------------------------------------------
 +
-- Helper functions
 +
-------------------------------------------------------------------------------
  
local function getSubpageLink(thisPage, offset)
+
local function makeWikilink(page, display)
-- Get a formatted link to the subpage a certain distance away, or nil
+
if display then
-- if that subpage does not exist. e.g. If the current subpage is
+
return string.format('[[%s|%s]]', page, display)
-- /Archive 27, then getSubpageLink(3) returns a link to Archive 30.
 
local subpageName
 
local startIdx, endIdx, archiveNum = mw.ustring.find(thisPage.subpageText, '^Archive ([0-9]+)')
 
if archiveNum then
 
subpageName = 'Archive ' .. (archiveNum + offset)
 
elseif tonumber(thisPage.subpageText) then
 
subpageName = thisPage.subpageText + offset
 
 
else
 
else
return nil  -- Couldn't parse out a subpage number
+
return string.format('[[%s]]', page)
 
end
 
end
 +
end
 +
 +
local function escapePattern(s)
 +
-- Escape punctuation in a string so it can be used in a Lua pattern.
 +
s = s:gsub('%p', '%%%0')
 +
return s
 +
end
 +
 +
-------------------------------------------------------------------------------
 +
-- Navigator class
 +
-------------------------------------------------------------------------------
 +
 +
local Navigator = {}
 +
Navigator.__index = Navigator
 +
 +
function Navigator.new(args, cfg, currentTitle)
 +
local obj = setmetatable({}, Navigator)
 +
 +
-- Set inputs
 +
obj.args = args
 +
obj.cfg = cfg
 +
obj.currentTitle = currentTitle
  
local page = mw.title.new(thisPage.baseText .. '/' .. subpageName, thisPage.namespace)
+
-- Archive prefix
if page.exists then
+
-- Decode HTML entities so users can enter things like "Archive " from
return '[[../' .. subpageName .. '|' .. subpageName .. ']]'
+
-- wikitext.
 +
obj.archivePrefix = obj.args.prefix or obj:message('archive-prefix')
 +
obj.archivePrefix = mw.text.decode(obj.archivePrefix)
 +
 
 +
-- Current archive number
 +
do
 +
local pattern = string.format(
 +
'^%s([1-9][0-9]*)$',
 +
escapePattern(obj.archivePrefix)
 +
)
 +
obj.currentArchiveNum = obj.currentTitle.subpageText:match(pattern)
 +
obj.currentArchiveNum = tonumber(obj.currentArchiveNum)
 +
end
 +
 +
-- Highest archive number
 +
obj.highestArchiveNum = require('Module:Highest archive number')._main(
 +
obj.currentTitle.nsText ..
 +
':' ..  
 +
obj.currentTitle.baseText ..
 +
'/' ..
 +
obj.archivePrefix
 +
)
 +
 
 +
return obj
 +
end
 +
 
 +
function Navigator:message(key, ...)
 +
local msg = self.cfg[key]
 +
if select('#', ...) > 0 then
 +
return mw.message.newRawMessage(msg, ...):plain()
 
else
 
else
return nil
+
return msg
 
end
 
end
 
end
 
end
  
local function getLinksText(args, thisPage)
+
function Navigator:makeBlurb()
local arrowSpacer = '  '
+
local args = self.args
local linkSpacer = '    '
+
if args[1] == '1' then
if mw.ustring.len(thisPage.subpageText) <= 4 then
+
-- The old template used "|1" to suppress the blurb.
-- If page names are short, we want more space. But why the mix of regular, non-breaking, and em spaces?
+
return ''
local emSpace = mw.getCurrentFrame():expandTemplate({title = 'Unicode', args = {mw.ustring.char(8195)}})
+
else
arrowSpacer = '&nbsp; ' .. emSpace .. ' &nbsp;&nbsp;'
+
local ret
linkSpacer = '&nbsp; ' .. emSpace .. ' &nbsp; ' .. emSpace .. '&nbsp;'
+
if args.text then
end
+
ret = args.text
+
else
local s = ''
+
local talkPage = self.currentTitle.nsText ..
+
':' ..
for offset = -5, -3 do
+
self.currentTitle.baseText
local link = getSubpageLink(thisPage, offset)
+
if args.period then
if link then
+
ret = self:message('blurb-period', talkPage, args.period)
if offset == -3 then
 
s = s .. link .. linkSpacer
 
 
else
 
else
s = s .. link .. ' ' .. arrowSpacer
+
ret = self:message('blurb-noperiod', talkPage)
 
end
 
end
break
 
 
end
 
end
 +
return ret
 
end
 
end
 +
end
 +
 +
function Navigator:makeMessageBox()
 +
local args = self.args
 
 
for offset = -2, -1 do
+
local image
local link = getSubpageLink(thisPage, offset)
+
if args.image then
if link then
+
image = args.image
s = s .. link .. linkSpacer
+
else
end
+
local icon = args.icon or self:message('default-icon')
 +
image = string.format(
 +
'[[File:%s|%s|alt=|link=]]',
 +
icon,
 +
self:message('image-size')
 +
)
 
end
 
end
  
s = s .. '<span style="font-size:115%;">[[' .. thisPage.fullText .. '|' .. thisPage.subpageText .. ']]</span>'
+
local mbox = require('Module:Message box').main('tmbox', {
 +
image = image,
 +
imageright = args.imageright,
 +
style = args.style or 'width:80%;margin-left:auto;margin-right:auto',
 +
textstyle = args.textstyle or 'text-align:center',
 +
text = self:makeBlurb()
 +
})
 +
 
 +
return mbox
 +
end
 +
 
 +
function Navigator:getArchiveNums()
 +
-- Returns an array of the archive numbers to format.
 +
local noLinks = tonumber(self.args.links) or self:message('default-link-count')
 +
noLinks = math.floor(noLinks)
 +
-- If |noredlinks is "yes", true or absent, don't allow red links. If it is
 +
-- 'no' or false, allow red links.
 +
local allowRedLinks = yesno(self.args.noredlinks) == false
 
 
for offset = 1, 2 do
+
local current = self.currentArchiveNum
local link = getSubpageLink(thisPage, offset)
+
local highest = self.highestArchiveNum
if link then
+
 
s = s .. linkSpacer .. link
+
if not current or not highest or noLinks < 1 then
 +
return {}
 +
elseif noLinks == 1 then
 +
return {current}
 +
end
 +
 
 +
local function getNum(i, current)
 +
-- Gets an archive number given i, the position in the array away from
 +
-- the current archive, and the current archive number. The first two
 +
-- offsets are consecutive; the third offset is rounded up to the
 +
-- nearest 5; and the fourth and subsequent offsets are rounded up to
 +
-- the nearest 10. The offsets are calculated in such a way that archive
 +
-- numbers will not be duplicated.
 +
if -2 <= i and i <= 2 then
 +
return current + i
 +
elseif -3 <= i and i <= 3 then
 +
return current + 2 - (current + 2) % 5 + (i / 3) * 5
 +
elseif 4 <= i then
 +
return current + 7 - (current + 7) % 10 + (i - 3) * 10
 +
else
 +
return current + 2 - (current + 2) % 10 + (i + 3) * 10
 
end
 
end
 
end
 
end
+
 
for offset = 5, 3, -1 do
+
local nums = {}
local link = getSubpageLink(thisPage, offset)
+
 
if link then
+
-- Archive nums lower than the current page.
if offset == 3 then
+
for i = -1, -math.floor((noLinks - 1) / 2), -1 do
s = s .. linkSpacer .. link
+
local num = getNum(i, current)
else
+
if num <= 1 then
s = s .. arrowSpacer .. '→ ' .. link
+
table.insert(nums, 1, 1)
end
 
 
break
 
break
 +
else
 +
table.insert(nums, 1, num)
 
end
 
end
 
end
 
end
  
return s
+
-- Current page.
end
+
if nums[#nums] < current then
 +
table.insert(nums, current)
 +
end
  
local function getMessage(args, thisPage)
+
-- Higher archive nums.
if args[1] == '1' then
+
for i = 1, math.ceil((noLinks - 1) / 2) do
return ''
+
local num = getNum(i, current)
else
+
if num <= highest then
local msg = '----\n'
+
table.insert(nums, num)
if args.text then
+
elseif allowRedLinks and (i <= 2 or i <= 3 and num == nums[#nums] + 1) then
msg = msg .. args.text
+
-- Only insert one red link, and only if it is consecutive.
 +
table.insert(nums, highest + 1)
 +
break
 +
elseif nums[#nums] < highest then
 +
-- Insert the highest archive number if it isn't already there.
 +
table.insert(nums, highest)
 +
break
 
else
 
else
msg = msg .. "This is an '''[[Help:Archiving a talk page|archive]]''' of past discussions"
+
break
if args.period then
 
msg = msg .. "&#32;for the period '''" .. args.period .. "'''"
 
end
 
msg = msg .. ". '''Do not edit the contents of this page.''' If you wish to start a new discussion or revive an old one, please do so on the "
 
msg = msg .. "[[" .. thisPage.rootPageTitle.fullText .. "|current talk page]]."
 
 
end
 
end
return msg
 
 
end
 
end
 +
mw.logObject(nums)
 +
 +
return nums
 
end
 
end
  
function p._aan(args)
+
function Navigator:makeArchiveLinksWikitable()
-- For testing purposes, allow passing in the page name as a param.
+
local lang = mw.language.getContentLanguage()
if args.title then
+
local nums = self:getArchiveNums()
thisPage = mw.title.new(args.title)
+
local current = self.currentArchiveNum
else
+
local highest = self.highestArchiveNum
thisPage = mw.title.getCurrentTitle()
+
local noLinks = #nums
 +
if noLinks < 1 then
 +
return 'NO LINKS ' .. tostring(self.currentArchiveNum) .. ' / ' .. tostring(self.highestArchiveNum)
 
end
 
end
local image = args.image
+
 
if not image then
+
-- Make the table of links.
image = '[[File:' .. (args.icon or 'Replacement filing cabinet.svg') .. '|40x40px|alt=|link=]]'
+
local links = {}
 +
local isCompact = noLinks > 7
 +
local currentIndex
 +
for i, num in ipairs(nums) do
 +
local subpage = self.archivePrefix .. tostring(num)
 +
local display
 +
if isCompact then
 +
display = tostring(num)
 +
else
 +
display = self:message('archive-link-display', num)
 +
end
 +
local link = makeWikilink('../' .. subpage, display)
 +
if num == self.currentArchiveNum then
 +
link = string.format('<span style="font-size:115%%;">%s</span>', link)
 +
currentIndex = i
 +
end
 +
table.insert(links, link)
 
end
 
end
local mbox = messageBox.main('tmbox', {
 
image = image,
 
imageright = args.imageright,
 
style = args.style or 'width:80%;margin-left:auto;margin-right:auto;',
 
textstyle = args.textstyle or 'text-align:center;',
 
text = getLinksText(args, thisPage) .. '\n' .. getMessage(args, thisPage)
 
})
 
return mbox .. '__NONEWSECTIONLINK__ __NOEDITSECTION__'
 
end
 
  
function p.aan(frame)
+
-- Add the arrows.
local origArgs
+
-- We must do the forwards arrow first as we are adding elements to the
-- If called via #invoke, use the args passed into the invoking template.
+
-- links table. If we did the backwards arrow first the index for the
-- Otherwise, for testing purposes, assume args are being passed directly in.
+
-- current archive would be wrong.
if frame == mw.getCurrentFrame() then
+
currentIndex = currentIndex or math.ceil(#links / 2)
origArgs = frame:getParent().args
+
for i = currentIndex + 1, #links do
for k, v in pairs(frame.args) do
+
if nums[i] - nums[i - 1] > 1 then
origArgs = frame.args
+
table.insert(links, i, lang:getArrow('forwards'))
 +
break
 +
end
 +
end
 +
for i = currentIndex - 1, 1, -1 do
 +
if nums[i + 1] - nums[i] > 1 then
 +
table.insert(links, i + 1, lang:getArrow('backwards'))
 
break
 
break
 
end
 
end
 +
end
 +
 +
-- Output the wikitable.
 +
local ret = {}
 +
local width
 +
if noLinks <= 3 then
 +
width = string.format('%dem', noLinks * 10)
 +
elseif noLinks <= 7 then
 +
width = string.format('%dem', (noLinks + 3) * 5)
 
else
 
else
origArgs = frame
+
width = '50em'
 
end
 
end
-- ParserFunctions considers the empty string to be false, so to preserve the previous
+
ret[#ret + 1] = string.format(
-- template behavior, change any empty arguments to nil, so Lua will consider
+
'{| style="width:%s;background:transparent;' ..
-- them false too.
+
'margin:0 auto 0.5em;text-align:center"',
args = {}
+
width
for k, v in pairs(origArgs) do
+
)
if v ~= '' then
+
for i, s in ipairs(links) do
args[k] = v
+
if i % 20 == 1 then
 +
ret[#ret + 1] = '\n|-'
 
end
 
end
 +
ret[#ret + 1] = '\n| '
 +
ret[#ret + 1] = s
 
end
 
end
 +
ret[#ret + 1] = '\n|}'
 +
return table.concat(ret)
 +
end
 +
 +
function Navigator:__tostring()
 +
return self:makeMessageBox() ..
 +
'\n' ..
 +
self:makeArchiveLinksWikitable() ..
 +
' __NONEWSECTIONLINK__ __NOEDITSECTION__'
 +
end
 +
 +
-------------------------------------------------------------------------------
 +
-- Exports
 +
-------------------------------------------------------------------------------
 +
 +
local p = {}
 +
 +
function p._exportClasses()
 +
return {
 +
Navigator = Navigator
 +
}
 +
end
 +
 +
function p._aan(args, cfg, currentTitle)
 +
cfg = cfg or mw.loadData('Module:Automatic archive navigator/config')
 +
currentTitle = currentTitle or mw.title.getCurrentTitle()
 +
local aan = Navigator.new(args, cfg, currentTitle)
 +
return tostring(aan)
 +
end
 +
 +
function p.aan(frame)
 +
local args = require('Module:Arguments').getArgs(frame, {
 +
wrappers = 'Template:Automatic archive navigator',
 +
})
 
return p._aan(args)
 
return p._aan(args)
 
end
 
end
+
 
 
return p
 
return p

Latest revision as of 13:17, 26 September 2020

Documentation for this module may be created at Module:Automatic archive navigator/sandbox/doc

-------------------------------------------------------------------------------
--                       Automatic archive navigator
--
-- This module produces a talk archive banner, together with an automatically-
-- generated list of navigation links to other archives of the talk page in
-- question. It implements {{Automatic archive navigator}} and
-- {{Talk archive navigation}}.
-------------------------------------------------------------------------------

local yesno = require('Module:Yesno')

-------------------------------------------------------------------------------
-- Helper functions
-------------------------------------------------------------------------------

local function makeWikilink(page, display)
	if display then
		return string.format('[[%s|%s]]', page, display)
	else
		return string.format('[[%s]]', page)
	end
end

local function escapePattern(s)
	-- Escape punctuation in a string so it can be used in a Lua pattern.
	s = s:gsub('%p', '%%%0')
	return s
end

-------------------------------------------------------------------------------
-- Navigator class
-------------------------------------------------------------------------------

local Navigator = {}
Navigator.__index = Navigator

function Navigator.new(args, cfg, currentTitle)
	local obj = setmetatable({}, Navigator)
	
	-- Set inputs
	obj.args = args
	obj.cfg = cfg
	obj.currentTitle = currentTitle

	-- Archive prefix
	-- Decode HTML entities so users can enter things like "Archive&#32;" from
	-- wikitext.
	obj.archivePrefix = obj.args.prefix or obj:message('archive-prefix')
	obj.archivePrefix = mw.text.decode(obj.archivePrefix)

	-- Current archive number
	do
		local pattern = string.format(
			'^%s([1-9][0-9]*)$',
			escapePattern(obj.archivePrefix)
		)
		obj.currentArchiveNum = obj.currentTitle.subpageText:match(pattern)
		obj.currentArchiveNum = tonumber(obj.currentArchiveNum)
	end
	
	-- Highest archive number
	obj.highestArchiveNum = require('Module:Highest archive number')._main(
		 obj.currentTitle.nsText ..
		 	':' .. 
			obj.currentTitle.baseText .. 
			'/' .. 
			obj.archivePrefix
	)

	return obj
end

function Navigator:message(key, ...)
	local msg = self.cfg[key]
	if select('#', ...) > 0 then
		return mw.message.newRawMessage(msg, ...):plain()
	else
		return msg
	end
end

function Navigator:makeBlurb()
	local args = self.args
	if args[1] == '1' then
		-- The old template used "|1" to suppress the blurb.
		return ''
	else
		local ret
		if args.text then
			ret = args.text
		else
			local talkPage = self.currentTitle.nsText ..
				':' ..
				self.currentTitle.baseText
			if args.period then
				ret = self:message('blurb-period', talkPage, args.period)
			else
				ret = self:message('blurb-noperiod', talkPage)
			end
		end
		return ret
	end
end

function Navigator:makeMessageBox()
	local args = self.args
	
	local image
	if args.image then
		image = args.image
	else
		local icon = args.icon or self:message('default-icon')
		image = string.format(
			'[[File:%s|%s|alt=|link=]]',
			icon,
			self:message('image-size')
		)
	end

	local mbox = require('Module:Message box').main('tmbox', {
		image = image,
		imageright = args.imageright,
		style = args.style or 'width:80%;margin-left:auto;margin-right:auto',
		textstyle = args.textstyle or 'text-align:center',
		text = self:makeBlurb()
	})

	return mbox
end

function Navigator:getArchiveNums()
	-- Returns an array of the archive numbers to format.
	local noLinks = tonumber(self.args.links) or self:message('default-link-count')
	noLinks = math.floor(noLinks)
	-- If |noredlinks is "yes", true or absent, don't allow red links. If it is 
	-- 'no' or false, allow red links.
	local allowRedLinks = yesno(self.args.noredlinks) == false
	
	local current = self.currentArchiveNum
	local highest = self.highestArchiveNum

	if not current or not highest or noLinks < 1 then
		return {}
	elseif noLinks == 1 then
		return {current}
	end

	local function getNum(i, current)
		-- Gets an archive number given i, the position in the array away from
		-- the current archive, and the current archive number. The first two
		-- offsets are consecutive; the third offset is rounded up to the
		-- nearest 5; and the fourth and subsequent offsets are rounded up to
		-- the nearest 10. The offsets are calculated in such a way that archive
		-- numbers will not be duplicated.
		if -2 <= i and i <= 2 then
			return current + i
		elseif -3 <= i and i <= 3 then
			return current + 2 - (current + 2) % 5 + (i / 3) * 5
		elseif 4 <= i then
			return current + 7 - (current + 7) % 10 + (i - 3) * 10
		else
			return current + 2 - (current + 2) % 10 + (i + 3) * 10
		end
	end

	local nums = {}

	-- Archive nums lower than the current page.
	for i = -1, -math.floor((noLinks - 1) / 2), -1 do
		local num = getNum(i, current)
		if num <= 1 then
			table.insert(nums, 1, 1)
			break
		else
			table.insert(nums, 1, num)
		end
	end

	-- Current page.
	if nums[#nums] < current then
		table.insert(nums, current)
	end

	-- Higher archive nums.
	for i = 1, math.ceil((noLinks - 1) / 2) do
		local num = getNum(i, current)
		if num <= highest then
			table.insert(nums, num)
		elseif allowRedLinks and (i <= 2 or i <= 3 and num == nums[#nums] + 1) then
			-- Only insert one red link, and only if it is consecutive.
			table.insert(nums, highest + 1)
			break
		elseif nums[#nums] < highest then
			-- Insert the highest archive number if it isn't already there.
			table.insert(nums, highest)
			break
		else
			break
		end
	end
	mw.logObject(nums)

	return nums
end

function Navigator:makeArchiveLinksWikitable()
	local lang = mw.language.getContentLanguage()
	local nums = self:getArchiveNums()
	local current = self.currentArchiveNum
	local highest = self.highestArchiveNum
	local noLinks = #nums
	if noLinks < 1 then
		return 'NO LINKS ' .. tostring(self.currentArchiveNum) .. ' / ' .. tostring(self.highestArchiveNum)
	end

	-- Make the table of links.
	local links = {}
	local isCompact = noLinks > 7
	local currentIndex
	for i, num in ipairs(nums) do
		local subpage = self.archivePrefix .. tostring(num)
		local display
		if isCompact then
			display = tostring(num)
		else
			display = self:message('archive-link-display', num)
		end
		local link = makeWikilink('../' .. subpage, display)
		if num == self.currentArchiveNum then
			link = string.format('<span style="font-size:115%%;">%s</span>', link)
			currentIndex = i
		end
		table.insert(links, link)
	end

	-- Add the arrows.
	-- We must do the forwards arrow first as we are adding elements to the
	-- links table. If we did the backwards arrow first the index for the
	-- current archive would be wrong.
	currentIndex = currentIndex or math.ceil(#links / 2)
	for i = currentIndex + 1, #links do
		if nums[i] - nums[i - 1] > 1 then
			table.insert(links, i, lang:getArrow('forwards'))
			break
		end
	end
	for i = currentIndex - 1, 1, -1 do
		if nums[i + 1] - nums[i] > 1 then
			table.insert(links, i + 1, lang:getArrow('backwards'))
			break
		end
	end

	-- Output the wikitable.
	local ret = {}
	local width
	if noLinks <= 3 then
		width = string.format('%dem', noLinks * 10)
	elseif noLinks <= 7 then
		width = string.format('%dem', (noLinks + 3) * 5)
	else
		width = '50em'
	end
	ret[#ret + 1] = string.format(
		'{| style="width:%s;background:transparent;' ..
			'margin:0 auto 0.5em;text-align:center"',
		width
	)
	for i, s in ipairs(links) do
		if i % 20 == 1 then
			ret[#ret + 1] = '\n|-'
		end
		ret[#ret + 1] = '\n| '
		ret[#ret + 1] = s
	end
	ret[#ret + 1] = '\n|}'
	return table.concat(ret)
end

function Navigator:__tostring()
	return self:makeMessageBox() ..
		'\n' .. 
		self:makeArchiveLinksWikitable() .. 
		' __NONEWSECTIONLINK__ __NOEDITSECTION__'
end

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

local p = {}

function p._exportClasses()
	return {
		Navigator = Navigator
	}
end

function p._aan(args, cfg, currentTitle)
	cfg = cfg or mw.loadData('Module:Automatic archive navigator/config')
	currentTitle = currentTitle or mw.title.getCurrentTitle()
	local aan = Navigator.new(args, cfg, currentTitle)
	return tostring(aan)
end

function p.aan(frame)
	local args = require('Module:Arguments').getArgs(frame, {
		wrappers = 'Template:Automatic archive navigator',
	})
	return p._aan(args)
end

return p