Tài liệu mô đun[xem] [sửa] [lịch sử] [làm mới]

Mô đun triển khai bản mẫu {{status}}. Xem tài liệu về cách sử dụng tại {{status}}.

-- This module implements {{status}}

-- Initialise necessary modules
local Object = require('Module:Classic')
local CountVotes = require('Module:CountVotes')._main
local yesno = require('Module:Yesno')

-- Lazily initialise modules and objects we don't always need
local lang

-- Set constants
local CONFIG_MODULE = 'Module:Status/config'
local LINE_FEED = '
'

--------------------------------------------------------------------------------
-- Helper class
--------------------------------------------------------------------------------

local Helper = Object:extend()

function Helper:validateDate(dateString)
	lang = lang or mw.language.getContentLanguage()
	local success, result = pcall(lang.formatDate, lang, 'U', dateString)
	if success then
		result = tonumber(result)
		if result then
			return true
		end
	end
	error(string.format('ngày tháng không hợp lệ: %s', tostring(dateString)))
end

function Helper:isUnixTime(dateString)
	local success, result = pcall(os.date, '!%Y-%m-%dT%TZ', dateString)
	if success then
		return true
	end
	return false
end

function Helper:formatDate(dateString, format)
	lang = lang or mw.language.getContentLanguage()
	if Helper:isUnixTime(dateString) then
		if format == 'U' then
			return dateString
		end
		return lang:formatDate(format, os.date('!%Y-%m-%dT%TZ', dateString))
	end
	if Helper:validateDate(dateString) then
		return lang:formatDate(format, dateString)
	end
end

function Helper:substParams(msg, params)
	return msg:gsub('${(%u+)}', params)
end

function Helper:dayToSecond(dayNumber)
	return tonumber(dayNumber) * 24 * 3600
end

function Helper:breakLines(title)
	return title:gsub('%. ', '. ' .. LINE_FEED) -- Phải dùng bản mẫu trung gian để escape
end

function Helper:countdown(duration)
	return string.format(
		'%s ngày %s giờ %s phút %s giây',
		duration.days or 0,
		duration.hours or 0,
		duration.minutes or 0,
		duration.seconds or 0
	)
end

function Helper:votesNumberTitle(positiveVotesNumber, negativeVotesNumber)
	return string.format(
		' ' .. LINE_FEED .. 'Số phiếu thuận là %s. '
			.. LINE_FEED .. 'Số phiếu chống là %s.',
		positiveVotesNumber,
		negativeVotesNumber
	)
end

--------------------------------------------------------------------------------
-- Status class
--------------------------------------------------------------------------------

local Status = Object:extend()

function Status:new(...)
    local args, cfg = unpack({...})

    -- Set cfg
    self.cfg = cfg

	-- Set startTime
	if args[1] then args.start = args[1] end -- Tương thích ngược phiên bản cũ
	if args.start and mw.text.trim(args.start) ~= '' then
    	self.startTime = tonumber(Helper:formatDate(args.start, 'U'))
	else
    	error('phải có tham số start và start không được rỗng')
	end

	-- Set maxTime
    self.maxTime = self:_getMaxTime(...)
end

function Status:_getMaxTime(...)
    local args, cfg, title = unpack({...})
    if args[2] then args.max = args[2] end -- Tương thích ngược phiên bản cũ
    if title then
        return self.startTime + Helper:dayToSecond(tonumber(args.max)
            or (cfg.rootTitles[title.rootText] or {}).max or cfg.DEFAULT_MAX)
    end
	return self.startTime + Helper:dayToSecond(tonumber(args.max) or cfg.DEFAULT_MAX)
end

function Status:_getMinTime(...)
    local _, cfg, title = unpack({...})
    return self.startTime + Helper:dayToSecond((cfg.rootTitles[title.rootText] or {}).min or cfg.DEFAULT_MIN)
end

function Status:_getConsiderExtensionTime(...)
    local _, cfg, title = unpack({...})
    return self.startTime + Helper:dayToSecond((cfg.rootTitles[title.rootText] or {}).considerExtension
        or cfg.DEFAULT_CONSIDER_EXTENSION)
end

function Status:_getExtendedTime(...)
    local _, cfg, title = unpack({...})
    return self.startTime + Helper:dayToSecond((cfg.rootTitles[title.rootText] or {}).extended
        or cfg.DEFAULT_EXTENDED)
end

function Status:_getInfo()
    local statusName, titleIndex
    local now = os.time()

    if now <= self.maxTime then
        statusName, titleIndex = 'VALID', 1
    else
        statusName, titleIndex = 'INVALID', 1
    end

    return statusName, titleIndex
end

function Status:_getParams()
    lang = lang or mw.language.getContentLanguage()
    local now = os.time()
    local params = {}

        params.MAXTIME                   = Helper:formatDate(self.maxTime, 'H:i:s d-m-Y')
        params.COUNTDOWNTOMAXTIME        = Helper:countdown(lang:getDurationIntervals(self.maxTime - now))

    if self.minTime then
        params.MINTIME                   = Helper:formatDate(self.minTime, 'H:i:s d-m-Y')
        params.COUNTDOWNTOMINTIME        = Helper:countdown(lang:getDurationIntervals(self.minTime - now))
    end

    if self.extendedTime then
        params.EXTENDEDTIME              = Helper:formatDate(self.extendedTime, 'H:i:s d-m-Y')
        params.COUNTDOWNTOEXTENDEDTIME   = Helper:countdown(lang:getDurationIntervals(self.extendedTime - now))
    end

    return params
end

-- Cung cấp giá trị tham số title và background
-- cho bản mẫu trung gian Status/core
function Status:getValues()
    local statusName, titleIndex = self:_getInfo()
    local title = Helper:breakLines(Helper:substParams(
    	self.cfg.statuses[statusName].titles[titleIndex], self:_getParams()))
    local votesNumberTitle = ''
    if self.positiveVotesNumber and self.negativeVotesNumber then
    	votesNumberTitle = Helper:votesNumberTitle(self.positiveVotesNumber, self.negativeVotesNumber)
    end
    local background = self.cfg.statuses[statusName].background
    return {title = title .. votesNumberTitle, background = background}
end

--------------------------------------------------------------------------------
-- StatusWithMinTime class extends Status class
--------------------------------------------------------------------------------

local StatusWithMinTime = Status:extend()

function StatusWithMinTime:new(...)
    StatusWithMinTime.super.new(self, ...)
    local _, cfg, title = unpack({...})

    -- Set minTime
    self.minTime = self:_getMinTime(...)

    -- Set positiveVotesNumber & negativeVotesNumber
    self.positiveVotesNumber, self.negativeVotesNumber = CountVotes(title)

    -- Set checkSuccessConclusionFunc
    self.checkSuccessConclusionFunc = (cfg.rootTitles[title.rootText] or {}).checkSuccessConclusionFunc
end

function StatusWithMinTime:_getInfo()
    local statusName, titleIndex
    local now = os.time()

    if now <= self.minTime then
        statusName, titleIndex = 'VALID', 2
    elseif self.minTime < now and now <= self.maxTime then
        if not self.checkSuccessConclusionFunc(self.positiveVotesNumber, self.negativeVotesNumber) then
            statusName, titleIndex = 'VALID', 3
        else
            statusName, titleIndex = 'VALID_AND_MAY_BE_CONSIDERED_CLOSED', 1
        end
    else
        statusName, titleIndex = 'INVALID', 2
    end

    return statusName, titleIndex
end

--------------------------------------------------------------------------------
-- StatusWithExtendedTime class extends Status class
--------------------------------------------------------------------------------

local StatusWithExtendedTime = Status:extend()

function StatusWithExtendedTime:new(...)
    StatusWithExtendedTime.super.new(self, ...)
    local args, cfg, title = unpack({...})

    -- Set considerExtensionTime
    self.considerExtensionTime = self:_getConsiderExtensionTime(...)

    -- Set extendedTime
    self.extendedTime = self:_getExtendedTime(...)

    -- Set positiveVotesNumber & negativeVotesNumber
    self.positiveVotesNumber, self.negativeVotesNumber = CountVotes(title)

    -- Set checkValidExtensionFunc
    self.checkValidExtensionFunc = (cfg.rootTitles[title.rootText] or {}).checkValidExtensionFunc

    -- Set extend
    self.extend = yesno(args.extend) or false
end

function StatusWithExtendedTime:_getInfo()
    local statusName, titleIndex
    local now = os.time()
    
    if now <= self.considerExtensionTime then
        statusName, titleIndex = 'VALID', 1
    elseif self.considerExtensionTime < now and now <= self.maxTime then
        if not self.checkValidExtensionFunc(self.positiveVotesNumber, self.negativeVotesNumber) then
            statusName, titleIndex = 'VALID', 4
        elseif not self.extend then
            statusName, titleIndex = 'VALID_AND_MAY_CONSIDER_EXTENSION', 1
        else
            statusName, titleIndex = 'VALID_AND_HAS_BEEN_EXTENDED', 1
        end
    elseif now <= self.extendedTime and self.extend then
        statusName, titleIndex = 'VALID_AND_IS_BEING_EXTENDED', 1
    elseif now > self.extendedTime and self.extend then
        statusName, titleIndex = 'INVALID', 3
    else
        statusName, titleIndex = 'INVALID', 1
    end

    return statusName, titleIndex
end

--------------------------------------------------------------------------------
-- StatusWithMinTimeAndExtendedTime class extends Status class
--------------------------------------------------------------------------------

local StatusWithMinTimeAndExtendedTime = Status:extend()

function StatusWithMinTimeAndExtendedTime:new(...)
    StatusWithMinTimeAndExtendedTime.super.new(self, ...)
    local args, cfg, title = unpack({...})

    -- Set minTime
    self.minTime = self:_getMinTime(...)

    -- Set considerExtensionTime
    self.considerExtensionTime = self:_getConsiderExtensionTime(...)

    -- Set extendedTime
    self.extendedTime = self:_getExtendedTime(...)

    -- Set positiveVotesNumber & negativeVotesNumber
    self.positiveVotesNumber, self.negativeVotesNumber = CountVotes(title)

    -- Set checkSuccessConclusionFunc
    self.checkSuccessConclusionFunc = (cfg.rootTitles[title.rootText] or {}).checkSuccessConclusionFunc

    -- Set checkValidExtensionFunc
    self.checkValidExtensionFunc = (cfg.rootTitles[title.rootText] or {}).checkValidExtensionFunc

    -- Set extend
    self.extend = yesno(args.extend) or false
end

function StatusWithMinTimeAndExtendedTime:_getInfo()
    local statusName, titleIndex
    local now = os.time()
    
    statusName, titleIndex = 'VALID', 1

    if now <= self.minTime then
        statusName, titleIndex = 'VALID', 2
    elseif self.minTime < now and now <= self.considerExtensionTime then
        if not self.checkSuccessConclusionFunc(self.positiveVotesNumber, self.negativeVotesNumber) then
            statusName, titleIndex = 'VALID', 3
        else
            statusName, titleIndex = 'VALID_AND_MAY_BE_CONSIDERED_CLOSED', 1
        end
    elseif self.considerExtensionTime < now and now <= self.maxTime then
        if (not self.checkValidExtensionFunc(self.positiveVotesNumber, self.negativeVotesNumber))
            and (not self.checkSuccessConclusionFunc(self.positiveVotesNumber, self.negativeVotesNumber))
        then
            statusName, titleIndex = 'VALID', 5
        elseif not self.checkValidExtensionFunc(self.positiveVotesNumber, self.negativeVotesNumber) then
            statusName, titleIndex = 'VALID_AND_MAY_BE_CONSIDERED_CLOSED', 1
        elseif not self.extend then
            statusName, titleIndex = 'VALID_AND_MAY_CONSIDER_EXTENSION', 2
        else
            statusName, titleIndex = 'VALID_AND_HAS_BEEN_EXTENDED', 2
        end
    elseif now <= self.extendedTime and self.extend then
        statusName, titleIndex = 'VALID_AND_IS_BEING_EXTENDED', 1
    elseif now > self.extendedTime and self.extend then
        statusName, titleIndex = 'INVALID', 3
    else
        statusName, titleIndex = 'INVALID', 2
    end

    return statusName, titleIndex
end

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

local p = {}

function p._exportClasses()
	-- This is used for testing purposes.
	return {
		Status = Status,
		StatusWithMinTime = StatusWithMinTime,
		StatusWithExtendedTime = StatusWithExtendedTime,
		StatusWithMinTimeAndExtendedTime = StatusWithMinTimeAndExtendedTime,
	}
end

function p._main(args, cfg)
	args = args or {}
	cfg = cfg or require(CONFIG_MODULE)

    local statusObj = {}

    if args.id then
        if mw.text.trim(args.id) ~= '' then
            local title = {}
            if tonumber(args.id) then
            	title = mw.title.new(tonumber(args.id))
            else
            	title = mw.title.new(args.id)
            end
            local group = (cfg.rootTitles[title.rootText] or {}).group

            if group == 1 then
                statusObj = StatusWithMinTime(args, cfg, title)
            elseif group == 2 then
                statusObj = StatusWithExtendedTime(args, cfg, title)
            elseif group == 3 then
                statusObj = StatusWithMinTimeAndExtendedTime(args, cfg, title)
            else
                error('khu vực không được hỗ trợ')
            end
        else
            error('id không được rỗng')
        end
    else
    	statusObj = Status(args, cfg)
    end

    return statusObj:getValues()
end

function p.main(frame)
    local args = frame:getParent().args
    local cfg = require(CONFIG_MODULE)
    return frame:expandTemplate{title = 'Status/core', args = p._main(args, cfg)}
end

return p