Module:Talk archive

From Path of Exile Wiki
Jump to navigation Jump to search
Module documentation[view] [edit] [history] [purge]

Implements {{Talk archive}}.

-------------------------------------------------------------------------------
-- 
--                           Module:Talk archive
-- 
-- This module implements Template:Talk archive.
-------------------------------------------------------------------------------

require('Module:No globals')
local m_util = require('Module:Util')

local f_exp_search = require('Module:Exponential search')

-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Talk archive')

-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = use_sandbox and mw.loadData('Module:Talk archive/config/sandbox') or mw.loadData('Module:Talk archive/config')

local i18n = cfg.i18n

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

local h = {}

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

-- Checks whether a page exists, going through pcall
-- in case we are over the expensive function limit.
function h.check_page_exists(page)
    local success, exists = pcall(function()
        return mw.title.new(page).exists
    end)
    return success and exists
end


-- Return the highest archive number, using exponential search 
-- algorithm.
function h.get_highest_archive_number(prefix, start)
    return f_exp_search(function (i)
        return h.check_page_exists(prefix .. tostring(i))
    end, start)
end

-- ----------------------------------------------------------------------------
-- Banner_Nav class
-- ----------------------------------------------------------------------------

local Banner_Nav = {}
Banner_Nav.__index = Banner_Nav

function Banner_Nav.new(args)
    -- Create a new Banner_Nav object
    local obj = setmetatable({}, Banner_Nav)

    obj.args = args
    obj.current_title = obj.args.page

    -- Decode HTML entities of user supplied prefix so that users can enter 
    -- things like "Archive " from wikicode. The encoding is needed because 
    -- trailing spaces are trimmed from template args.
    obj.prefix = obj.args.prefix and mw.text.decode(obj.args.prefix) or i18n.archive_prefix

    -- Get the current archive page number
    local pattern = string.format(
        '^%s([1-9][0-9]*)$',
        h.escape_pattern(obj.prefix)
    )
    obj.current_page = tonumber(obj.current_title.subpageText:match(pattern))

    -- Find the highest archive page number
    obj.highest_page = h.get_highest_archive_number(string.format(
        '%s:%s/%s',
        obj.current_title.nsText,
        obj.current_title.baseText,
        obj.prefix
    ))

    return obj
end

function Banner_Nav:message_box()
    local title
    if self.args.period then
        title = string.format(i18n.Banner_Nav.title_blurb_period, self.args.period)
    else
        title = i18n.Banner_Nav.title_blurb_noperiod
    end
    local talk_page = string.format('%s:%s', self.current_title.nsText, self.current_title.baseText)
    local text = string.format(i18n.Banner_Nav.text_blurb, talk_page)
    return mw.getCurrentFrame():expandTemplate{
        title = i18n.templates.message_box,
        args = {
            type = 'lock',
            image = i18n.archive_image,
            title = title,
            text = text,
        }
    }
end

function Banner_Nav:get_pages()
    -- Returns an array of archive page numbers

    if not self.current_page then
        return {}
    end

    local range = self.args.range and tonumber(self.args.range) or cfg.nav_link_range
    range = math.floor(range)

    if range < 1 then
        return {self.current_page}
    end

    local pages = {}

    -- Pages numbered lower than current page
    for i = -range, -1 do
        local page = self.current_page + i
        if page >= 1 then
            table.insert(pages, page)
        end
    end

    -- Current page
    table.insert(pages, self.current_page)

    -- Pages numbered higher than current page
    for i = 1, range do
        local page = self.current_page + i
        if page <= self.highest_page then
            table.insert(pages, page)
        end
    end

    return pages
end

function Banner_Nav:navigation()
    local pages = self:get_pages()

    if #pages < 1 then
        return ''
    end

    local html = mw.html.create('div')
        :cssText('text-align:center;')
    local list = html:tag('ul')
        :addClass('hlist')
        :newline()
    for i, p in ipairs(pages) do
        local list_item = list:tag('li')
        if p then
            local subpage = self.prefix .. tostring(p)
            local display = p == self.current_page and string.format(i18n.Banner_Nav.link_text, p) or tostring(p)
            local link = m_util.html.wikilink('../' .. subpage, display)
            if i == 1 and p ~= self.current_page then
                list_item:wikitext(string.format(i18n.Banner_Nav.before_fmt, link))
            elseif i == #pages and p ~= self.current_page then
                list_item:wikitext(string.format(i18n.Banner_Nav.after_fmt, link))
            else
                list_item:wikitext(link)
            end
        end
        list:newline()
    end
    return tostring(html)
end

function Banner_Nav:__tostring()
    return table.concat({
        self:message_box(),
        '\n',
        self:navigation(),
        ' __NONEWSECTIONLINK__ __NOEDITSECTION__',
    })
end

-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------

local function _talk_archive(args)
    args.page = args.page and mw.title.new(args.page) or mw.title.getCurrentTitle()
    local t = Banner_Nav.new(args)
    return tostring(t)
end

-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------

local p = {}

-- 
-- Template:Talk archive
-- 
p.talk_archive = m_util.misc.invoker_factory(_talk_archive, {
    wrappers = cfg.wrappers.talk_archive,
})

return p