Jump to content

Module:Archive list

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Mr. Stradivarius (talk | contribs) at 13:48, 2 July 2013 (add function to return the archive count to #invoke). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

-- Checks whether a page exists, going through pcall
-- in case we are over the expensive function limit.
local function checkPageExists( title )
    if not title then
        error('No title passed to checkArchiveExists', 2)
    end
    local noError, titleObject = pcall(mw.title.new, title)
    if not noError then
        -- If we are over the expensive function limit then assume
        -- that the page doesn't exist.
        return false
    else
        if titleObject then
            return titleObject.exists
        else
            return false -- Return false if given a bad title.
        end
    end
end

-- Checks every nth archive to see if it exists, and returns the
-- number of the first archive that *doesn't* exist. It is
-- necessary to do this in batches because each check is an
-- expensive function call, and we want to avoid making too many
-- of them so as not to go over the expensive function limit.
local function checkArchives( prefix, n, start )
    local i = start
    local exists = true
    while exists do
        exists = checkPageExists( prefix .. tostring( i ) )
        if exists then
            i = i + n
        end
    end
    return i
end

-- Return the biggest archive number, using checkArchives()
-- and starting in intervals of 1000. This should get us a
-- maximum of 500,000 possible archives before we hit the
-- expensive function limit.
local function getBiggestArchiveNum( prefix )
    local check1000 = checkArchives( prefix, 1000, 1 )
    if check1000 == 1 then
        return 0 -- Return 0 if no archives were found.
    end
    local check200 = checkArchives( prefix, 200, check1000 - 1000 )
    local check50 = checkArchives( prefix, 50, check200 - 200 )
    local check10 = checkArchives( prefix, 10, check50 - 50 )
    local check1 = checkArchives( prefix, 1, check10 - 10 )
    -- check1 is the first page that doesn't exist, so we want to
    -- subtract it by one to find the biggest existing archive.
    return check1 - 1
end

local function getPrefix( args )
    -- Get the root page.
    local root = args.root or mw.title.getCurrentTitle().prefixedText
    
    -- Get the prefix.
    local prefix = root .. '/'
    if args.prefix then
        prefix = prefix .. args.prefix
        if args.prefixspace == 'yes' then
            prefix = prefix .. ' '
        end
    else
        prefix = prefix .. 'Archive '
    end
    return prefix
end

-- Generate the archive links from the values found in the
-- other functions.
local function _main( args )
    -- Get the number of archives to put on one line. Set to
    -- math.huge if there should be no line breaks.
    local links = tonumber( args.links )
    local lineNum
    if args.nobr == 'yes' or (args.links and not links) then
        lineNum = math.huge
    -- If links is a number, process it. Negative values and expressions
    -- such as links=8/2 produced some interesting values with the old
    -- template, but we will ignore those for simplicity.
    elseif type(links) == 'number' and links >= 0 then
        -- The old template rounded down decimals to the nearest integer.
        lineNum = math.floor( links )
        if lineNum == 0 then
            -- In the old template, values of links between 0 and 0.999
            -- suppressed line breaks.
            lineNum = math.huge
        end
    else
        lineNum = 10 -- Default to 10 links.
    end
    
    -- Get the prefix and the biggest archive number.
    local prefix = getPrefix ( args )
    local archiveMax = getBiggestArchiveNum( prefix )
    
    -- If there are no archives yet, return a message and a
    -- link to create Archive one.
    if archiveMax == 0 then
        return 'no archives yet ([[' .. prefix .. '1|create]])'
    end
    
    -- Get the link prefix.
    local linkprefix = ''
    if args.linkprefix then
        linkprefix = args.linkprefix
        if args.linkprefixspace == 'yes' then
            linkprefix = linkprefix .. ' '
        end
    end
    
    -- Generate the archive links.
    local lineCounter = 1 -- The counter to see whether we need a line break or not.
    local ret = '' -- The string to return.
    for archiveNum = 1, archiveMax do
        ret = ret .. '[[' .. prefix .. tostring(archiveNum) .. '|' .. linkprefix .. tostring(archiveNum) .. ']]'
        -- If we don't need a new line, output a comma. We don't need
        -- a comma after the last link. 
        if lineCounter < lineNum and archiveNum < archiveMax then
            ret = ret .. ', '
            lineCounter = lineCounter + 1
        -- Output new lines if needed. We don't need a new line after
        -- the last link.
        elseif lineCounter >= lineNum and archiveNum < archiveMax then
            ret = ret .. '<br />'
            lineCounter = 1
        end
    end
    return ret
end

-- A wrapper function to make getBiggestArchiveNum() available from
-- #invoke.
local function _count( args )
    local prefix = getPrefix( args )
    local archiveMax = getBiggestArchiveNum( prefix )
    return archiveMax
end

function makeWrapper( func )
    return function( frame )
        -- If we are being called from #invoke, get the args from #invoke
        -- if they exist, or else get the arguments passed to the parent
        -- frame. Otherwise, assume the arguments are being passed directly
        -- in from another module or from the debug console.
        local origArgs
        if frame == mw.getCurrentFrame() then
            origArgs = frame:getParent().args
            for k, v in pairs( frame.args ) do
                origArgs = frame.args
                break
            end
        else
            origArgs = frame
        end
        
        -- Trim whitespace from all arguments, and ignore blank values for
        -- parameters other than "links", which functions differently
        -- depending on whether it is blank or absent.
        local args = {}
        for k, v in pairs( origArgs ) do
            v = mw.text.trim(v)
            if k == 'links' or v ~= '' then
                args[k] = v
            end
        end
        
        return func( args )
    end
end

return {
    main = makeWrapper( _main ),
    count = makeWrapper( _count )
}