Difference between revisions of "Module:Category handler/sandbox"

From blackwiki
Jump to navigation Jump to search
blackwiki>Mr. Stradivarius
(sync)
 
blackwiki>Mr. Stradivarius
(use Module:Category handler/data instead of using the cfg table directly in the module, change the nsDetect variable to mNamespaceDetect, and convert whitespace to tabs)
Line 1: Line 1:
----------------------------------------------------------------------------------------------------------
+
--------------------------------------------------------------------------------
--                                                                                                     --
+
--                                                                           --
--                                         CATEGORY HANDLER                                           --
+
--                             CATEGORY HANDLER                             --
--                                                                                                     --
+
--                                                                           --
--      This module implements the {{category handler}} template in Lua, with a few improvements: all   --
+
--      This module implements the {{category handler}} template in Lua,     --
--      namespaces and all namespace aliases are supported, and namespace names are detected           --
+
--      with a few improvements: all namespaces and all namespace aliases    --
--      automatically for the local wiki. This module requires [[Module:Namespace detect]] and          --
+
--      are supported, and namespace names are detected automatically for    --
--      [[Module:Yesno]] to be available on the local wiki. It can be configured for different wikis    --
+
--      the local wiki. This module requires [[Module:Namespace detect]]     --
--      by altering the values in the "cfg" table.                                                      --
+
--      and [[Module:Yesno]] to be available on the local wiki. It can be     --
--                                                                                                     --
+
--      configured for different wikis by altering the values in             --
----------------------------------------------------------------------------------------------------------
+
--     [[Module:Category handler/config]], and pages can be blacklisted      --
 +
--     from categorisation by using [[Module:Category handler/blacklist]].  --
 +
--                                                                           --
 +
--------------------------------------------------------------------------------
  
----------------------------------------------------------------------------------------------------------
+
local data = mw.loadData('Module:Category handler/data')
--                                          Configuration data                                         --
 
--                      Language-specific parameter names and values can be set here.                  --
 
----------------------------------------------------------------------------------------------------------
 
 
 
local cfg = {}
 
 
 
-- The following config values set the names of parameters that suppress categorisation. They are used
 
-- with Module:Yesno, and work as follows:
 
--
 
-- cfg.nocat:
 
-- Result of yesno(args[cfg.nocat])        Effect
 
-- true                                    Categorisation is suppressed
 
-- false                                    Categorisation is allowed, and the blacklist check is skipped
 
-- nil                                      Categorisation is allowed
 
--
 
-- cfg.categories:
 
-- Result of yesno(args[cfg.categories])    Effect
 
-- true                                    Categorisation is allowed, and the blacklist check is skipped
 
-- false                                    Categorisation is suppressed
 
-- nil                                      Categorisation is allowed
 
cfg.nocat = 'nocat'   
 
cfg.categories = 'categories'
 
 
 
-- The parameter name for the legacy "category2" parameter. This skips the blacklist if set to the
 
-- cfg.category2Yes value, and suppresses categorisation if present but equal to anything other than
 
-- cfg.category2Yes or cfg.category2Negative.
 
cfg.category2 = 'category2'
 
cfg.category2Yes = 'yes'
 
cfg.category2Negative = '¬'
 
 
 
-- cfg.subpage is the parameter name to specify how to behave on subpages. cfg.subpageNo is the value to
 
-- specify to not categorise on subpages; cfg.only is the value to specify to only categorise on subpages.
 
cfg.subpage = 'subpage'
 
cfg.subpageNo = 'no'
 
cfg.subpageOnly = 'only'
 
 
 
-- The parameter for data to return in all namespaces.
 
cfg.all = 'all'
 
 
 
-- The parameter name for data to return if no data is specified for the namespace that is detected. This
 
-- must be the same as the cfg.other parameter in [[Module:Namespace detect]].
 
cfg.other = 'other'
 
 
 
-- The parameter name used to specify a page other than the current page; used for testing and
 
-- demonstration. This must be the same as the cfg.page parameter in [[Module:Namespace detect]].
 
cfg.page = 'page'
 
 
 
-- The categorisation blacklist. Pages that match Lua patterns in this list will not be categorised.
 
-- (However, see the explanation of cfg.nocat, cfg.categories and cfg.category2 for some exceptions.)
 
-- If the namespace name has a space in, it must be written with an underscore, e.g. "Wikipedia_talk".
 
-- Other parts of the title can have either underscores or spaces.
 
cfg.blacklist = {
 
    '^Main Page$', -- don't categorise the main page.
 
   
 
    -- Don't categorise the following pages or their subpages.
 
    '^Wikipedia:Cascade%-protected items$',
 
    '^Wikipedia:Cascade%-protected items/.*$',
 
    '^User:UBX$', -- The userbox "template" space.
 
    '^User:UBX/.*$',
 
    '^User_talk:UBX$',
 
    '^User_talk:UBX/.*$',
 
   
 
    -- Don't categorise subpages of these pages, but allow
 
    -- categorisation of the base page.
 
    '^Wikipedia:Template messages/.+$',
 
   
 
    '/[aA]rchive' -- Don't categorise archives.
 
}
 
 
 
-- This is a table of namespaces to categorise by default. They should be in the format of parameter
 
-- names accepted by [[Module:Namespace detect]].
 
cfg.defaultNamespaces = {
 
    'main',
 
    'file',
 
    'help',
 
    'category'
 
}
 
 
 
----------------------------------------------------------------------------------------------------------
 
--                                          End configuration data                                      --
 
----------------------------------------------------------------------------------------------------------
 
  
 
-- Get dependent modules
 
-- Get dependent modules
local nsDetect = require('Module:Namespace detect')
+
local mNamespaceDetect = require('Module:Namespace detect')
 
local yesno = require('Module:Yesno')
 
local yesno = require('Module:Yesno')
  
----------------------------------------------------------------------------------------------------------
+
local p = {}
--                                          Local functions                                            --
 
--      The following are internal functions, which we do not want to be accessible from other modules. --
 
----------------------------------------------------------------------------------------------------------
 
  
-- Find whether we need to return a category or not.
 
 
local function needsCategory(pageObject, args)
 
local function needsCategory(pageObject, args)
    -- Don't categorise if the relevant options are set.
+
-- This function finds whether we need to return a category or not.
    if yesno(args[cfg.nocat])
+
        or yesno(args[cfg.categories]) == false
+
-- Don't categorise if the relevant options are set.
        or (
+
if yesno(args[data.nocat])
            args[cfg.category2]  
+
or yesno(args[data.categories]) == false
            and args[cfg.category2] ~= cfg.category2Yes  
+
or (
            and args[cfg.category2] ~= cfg.category2Negative
+
args[data.category2]  
        )
+
and args[data.category2] ~= data.category2Yes  
    then
+
and args[data.category2] ~= data.category2Negative
        return false
+
)
    end
+
then
    -- If there is no pageObject available, then that either means that we are over
+
return false
    -- the expensive function limit or that the title specified was invalid. Invalid
+
end
    -- titles will probably only be a problem during testing, so we choose the best
+
 
    -- fallback for being over the expensive function limit. The fallback behaviour
+
-- If there is no pageObject available, then that either means that we are over
    -- of the old template was to assume the page was not a subpage, so we will do
+
-- the expensive function limit or that the title specified was invalid. Invalid
    -- the same here.
+
-- titles will probably only be a problem during testing, so we choose the best
    if args[cfg.subpage] == cfg.subpageNo and pageObject and pageObject.isSubpage then
+
-- fallback for being over the expensive function limit. The fallback behaviour
        return false
+
-- of the old template was to assume the page was not a subpage, so we will do
    end
+
-- the same here.
    if args[cfg.subpage] == cfg.subpageOnly  
+
if args[data.subpage] == data.subpageNo and pageObject and pageObject.isSubpage then
        and (not pageObject or (pageObject and not pageObject.isSubpage))
+
return false
    then
+
end
        return false
+
if args[data.subpage] == data.subpageOnly  
    end
+
and (not pageObject or (pageObject and not pageObject.isSubpage))
    return true
+
then
 +
return false
 +
end
 +
return true
 
end
 
end
  
 
-- Find whether we need to check the blacklist or not.
 
-- Find whether we need to check the blacklist or not.
 
local function needsBlacklistCheck(args)
 
local function needsBlacklistCheck(args)
    if yesno(args[cfg.nocat]) == false
+
if yesno(args[data.nocat]) == false
        or yesno(args[cfg.categories]) == true
+
or yesno(args[data.categories]) == true
        or args[cfg.category2] == cfg.category2Yes
+
or args[data.category2] == data.category2Yes
    then
+
then
        return false
+
return false
    else
+
else
        return true
+
return true
    end
+
end
 
end
 
end
  
Line 149: Line 70:
 
-- [[Module:Namespace detect]].
 
-- [[Module:Namespace detect]].
 
local function nsParamsExist(mappings, args)
 
local function nsParamsExist(mappings, args)
    if args[cfg.all] or args[cfg.other] then
+
if args[data.all] or args[data.other] then
        return true
+
return true
    end
+
end
    for ns, params in pairs(mappings) do
+
for ns, params in pairs(mappings) do
        for i, param in ipairs(params) do
+
for i, param in ipairs(params) do
            if args[param] then
+
if args[param] then
                return true
+
return true
            end
+
end
        end
+
end
    end
+
end
    return false
+
return false
 
end
 
end
 
----------------------------------------------------------------------------------------------------------
 
--                                          Global functions                                            --
 
--      The following functions are global, because we want them to be accessible from #invoke and      --
 
--      from other Lua modules.                                                                        --
 
----------------------------------------------------------------------------------------------------------
 
 
local p = {}
 
  
 
-- Find if a string matches the blacklist. Returns the match if one is found, or nil otherwise.
 
-- Find if a string matches the blacklist. Returns the match if one is found, or nil otherwise.
 
-- Input should be a page title with a namespace prefix, e.g. "Wikipedia talk:Articles for deletion".
 
-- Input should be a page title with a namespace prefix, e.g. "Wikipedia talk:Articles for deletion".
 
function p.matchesBlacklist(page)
 
function p.matchesBlacklist(page)
    if type(page) ~= 'string' then return end
+
if type(page) ~= 'string' then return end
    for i, pattern in ipairs(cfg.blacklist) do
+
for i, pattern in ipairs(data.blacklist) do
        local match = mw.ustring.match(page, pattern)
+
local match = mw.ustring.match(page, pattern)
        if match then
+
if match then
            return match
+
return match
        end
+
end
    end
+
end
 
end
 
end
  
Line 185: Line 98:
 
-- and then passes the relevant arguments to [[Module:Namespace detect]].
 
-- and then passes the relevant arguments to [[Module:Namespace detect]].
 
function p._main(args)
 
function p._main(args)
    -- Get the page object and argument mappings from
+
-- Get the page object and argument mappings from
    -- [[Module:Namespace detect]], to save us from having to rewrite the
+
-- [[Module:Namespace detect]], to save us from having to rewrite the
    -- code.
+
-- code.
    local pageObject = nsDetect.getPageObject(args[cfg.page])
+
local pageObject = mNamespaceDetect.getPageObject(args[data.page])
    local mappings = nsDetect.getParamMappings()
+
local mappings = mNamespaceDetect.getParamMappings()
   
+
    if not needsCategory(pageObject, args) then return end
+
if not needsCategory(pageObject, args) then return end
   
+
    local ret = ''
+
local ret = ''
    -- Check blacklist if necessary.
+
-- Check blacklist if necessary.
    if not needsBlacklistCheck(args) or not p.matchesBlacklist(pageObject.prefixedText) then
+
if not needsBlacklistCheck(args) or not p.matchesBlacklist(pageObject.prefixedText) then
        if not nsParamsExist(mappings, args) then
+
if not nsParamsExist(mappings, args) then
            -- No namespace parameters exist; basic usage. Pass args[1] to
+
-- No namespace parameters exist; basic usage. Pass args[1] to
            -- [[Module:Namespace detect]] using the default namespace
+
-- [[Module:Namespace detect]] using the default namespace
            -- parameters, and return the result.
+
-- parameters, and return the result.
            local ndargs = {}
+
local ndargs = {}
            for _, ndarg in ipairs(cfg.defaultNamespaces) do
+
for _, ndarg in ipairs(data.defaultNamespaces) do
                ndargs[ndarg] = args[1]
+
ndargs[ndarg] = args[1]
            end
+
end
            ndargs.page = args.page
+
ndargs.page = args.page
            ndargs.demospace = args.demospace
+
ndargs.demospace = args.demospace
            local ndresult = nsDetect._main(ndargs)
+
local ndresult = mNamespaceDetect._main(ndargs)
            if ndresult then
+
if ndresult then
                ret = ret .. ndresult
+
ret = ret .. ndresult
            end
+
end
        else
+
else
            -- Namespace parameters exist; advanced usage.
+
-- Namespace parameters exist; advanced usage.
            -- If the all parameter is specified, return it.
+
-- If the all parameter is specified, return it.
            local all = args.all
+
local all = args.all
            if type(all) == 'string' then
+
if type(all) == 'string' then
                ret = ret .. all
+
ret = ret .. all
            end
+
end
           
+
            -- Get the arguments to pass to [[Module:Namespace detect]].
+
-- Get the arguments to pass to [[Module:Namespace detect]].
            local ndargs = {}
+
local ndargs = {}
            for ns, params in pairs(mappings) do
+
for ns, params in pairs(mappings) do
                for _, param in ipairs(params) do
+
for _, param in ipairs(params) do
                    ndargs[param] = args[param] or args[cfg.other] or nil
+
ndargs[param] = args[param] or args[data.other] or nil
                end
+
end
            end
+
end
            ndargs.other = args.other
+
ndargs.other = args.other
            ndargs.page = args.page
+
ndargs.page = args.page
            ndargs.demospace = args.demospace
+
ndargs.demospace = args.demospace
           
+
            local data = nsDetect._main(ndargs)
+
local data = mNamespaceDetect._main(ndargs)
           
+
            -- Work out what to return based on the result of the namespace detect call.
+
-- Work out what to return based on the result of the namespace detect call.
            local datanum = tonumber(data)
+
local datanum = tonumber(data)
            if type(datanum) == 'number' then
+
if type(datanum) == 'number' then
                -- "data" is a number, so return that positional parameter.
+
-- "data" is a number, so return that positional parameter.
                -- Remove non-positive integer values, as only positive integers
+
-- Remove non-positive integer values, as only positive integers
                -- from 1-10 were used with the old template.
+
-- from 1-10 were used with the old template.
                if datanum > 0 and math.floor(datanum) == datanum then
+
if datanum > 0 and math.floor(datanum) == datanum then
                    local dataArg = args[datanum]
+
local dataArg = args[datanum]
                    if type(dataArg) == 'string' then
+
if type(dataArg) == 'string' then
                        ret = ret .. dataArg
+
ret = ret .. dataArg
                    end
+
end
                end
+
end
            else
+
else
                -- "data" is not a number, so return it as it is.
+
-- "data" is not a number, so return it as it is.
                if type(data) == 'string' then
+
if type(data) == 'string' then
                    ret = ret .. data
+
ret = ret .. data
                end
+
end
            end
+
end
        end
+
end
    end
+
end
    return ret
+
return ret
 
end
 
end
  
 
function p.main(frame)
 
function p.main(frame)
    -- If called via #invoke, use the args passed into the invoking
+
-- If called via #invoke, use the args passed into the invoking
    -- template, or the args passed to #invoke if any exist. Otherwise
+
-- template, or the args passed to #invoke if any exist. Otherwise
    -- assume args are being passed directly in.
+
-- assume args are being passed directly in.
    local origArgs
+
local origArgs
    if frame == mw.getCurrentFrame() then
+
if frame == mw.getCurrentFrame() then
        origArgs = frame:getParent().args
+
origArgs = frame:getParent().args
        for k, v in pairs(frame.args) do
+
for k, v in pairs(frame.args) do
            origArgs = frame.args
+
origArgs = frame.args
            break
+
break
        end
+
end
    else
+
else
        origArgs = frame
+
origArgs = frame
    end
+
end
  
    -- Trim whitespace and remove blank arguments for the following args:
+
-- Trim whitespace and remove blank arguments for the following args:
    -- 1, 2, 3 etc., "nocat", "categories", "subpage", and "page".
+
-- 1, 2, 3 etc., "nocat", "categories", "subpage", and "page".
    local args = {}
+
local args = {}
    for k, v in pairs(origArgs) do
+
for k, v in pairs(origArgs) do
        if type(v) == 'string' then
+
if type(v) == 'string' then
            v = mw.text.trim(v) -- Trim whitespace.
+
v = mw.text.trim(v) -- Trim whitespace.
        end
+
end
        if type(k) == 'number'
+
if type(k) == 'number'
            or k == cfg.nocat
+
or k == data.nocat
            or k == cfg.categories
+
or k == data.categories
            or k == cfg.subpage
+
or k == data.subpage
            or k == cfg.page
+
or k == data.page
        then
+
then
            if v ~= '' then
+
if v ~= '' then
                args[k] = v
+
args[k] = v
            end
+
end
        else
+
else
            args[k] = v
+
args[k] = v
        end
+
end
    end
+
end
   
+
    -- Lower-case "nocat", "categories", "category2", and "subpage". These
+
-- Lower-case "nocat", "categories", "category2", and "subpage". These
    -- parameters are put in lower case whenever they appear in the old
+
-- parameters are put in lower case whenever they appear in the old
    -- template, so we can just do it once here and save ourselves some work.
+
-- template, so we can just do it once here and save ourselves some work.
    local lowercase = {cfg.nocat, cfg.categories, cfg.category2, cfg.subpage}
+
local lowercase = {data.nocat, data.categories, data.category2, data.subpage}
    for _, v in ipairs(lowercase) do
+
for _, v in ipairs(lowercase) do
        local argVal = args[v]
+
local argVal = args[v]
        if type(argVal) == 'string' then
+
if type(argVal) == 'string' then
            args[v] = mw.ustring.lower(argVal)
+
args[v] = mw.ustring.lower(argVal)
        end
+
end
    end
+
end
   
+
    return p._main(args)
+
return p._main(args)
 
end
 
end
  
 
return p
 
return p

Revision as of 02:16, 7 July 2014

Documentation for this module may be created at Module:Category handler/sandbox/doc

--------------------------------------------------------------------------------
--                                                                            --
--                              CATEGORY HANDLER                              --
--                                                                            --
--      This module implements the {{category handler}} template in Lua,      --
--      with a few improvements: all namespaces and all namespace aliases     --
--      are supported, and namespace names are detected automatically for     --
--      the local wiki. This module requires [[Module:Namespace detect]]      --
--      and [[Module:Yesno]] to be available on the local wiki. It can be     --
--      configured for different wikis by altering the values in              --
--      [[Module:Category handler/config]], and pages can be blacklisted      --
--      from categorisation by using [[Module:Category handler/blacklist]].   --
--                                                                            --
--------------------------------------------------------------------------------

local data = mw.loadData('Module:Category handler/data')

-- Get dependent modules
local mNamespaceDetect = require('Module:Namespace detect')
local yesno = require('Module:Yesno')

local p = {}

local function needsCategory(pageObject, args)
	-- This function finds whether we need to return a category or not.
	
	-- Don't categorise if the relevant options are set.
	if yesno(args[data.nocat])
		or yesno(args[data.categories]) == false
		or (
			args[data.category2] 
			and args[data.category2] ~= data.category2Yes 
			and args[data.category2] ~= data.category2Negative
		)
	then
		return false
	end

	-- If there is no pageObject available, then that either means that we are over
	-- the expensive function limit or that the title specified was invalid. Invalid
	-- titles will probably only be a problem during testing, so we choose the best
	-- fallback for being over the expensive function limit. The fallback behaviour
	-- of the old template was to assume the page was not a subpage, so we will do
	-- the same here.
	if args[data.subpage] == data.subpageNo and pageObject and pageObject.isSubpage then
		return false
	end
	if args[data.subpage] == data.subpageOnly 
		and (not pageObject or (pageObject and not pageObject.isSubpage))
	then
		return false
	end
	return true
end

-- Find whether we need to check the blacklist or not.
local function needsBlacklistCheck(args)
	if yesno(args[data.nocat]) == false
		or yesno(args[data.categories]) == true
		or args[data.category2] == data.category2Yes
	then
		return false
	else
		return true
	end
end

-- Find whether any namespace parameters have been specified.
-- Mappings is the table of parameter mappings taken from
-- [[Module:Namespace detect]].
local function nsParamsExist(mappings, args)
	if args[data.all] or args[data.other] then
		return true
	end
	for ns, params in pairs(mappings) do
		for i, param in ipairs(params) do
			if args[param] then
				return true
			end
		end
	end
	return false
end

-- Find if a string matches the blacklist. Returns the match if one is found, or nil otherwise.
-- Input should be a page title with a namespace prefix, e.g. "Wikipedia talk:Articles for deletion".
function p.matchesBlacklist(page)
	if type(page) ~= 'string' then return end
	for i, pattern in ipairs(data.blacklist) do
		local match = mw.ustring.match(page, pattern)
		if match then
			return match
		end
	end
end

-- The main structure of the module. Checks whether we need to categorise,
-- and then passes the relevant arguments to [[Module:Namespace detect]].
function p._main(args)
	-- Get the page object and argument mappings from
	-- [[Module:Namespace detect]], to save us from having to rewrite the
	-- code.
	local pageObject = mNamespaceDetect.getPageObject(args[data.page])
	local mappings = mNamespaceDetect.getParamMappings()
	
	if not needsCategory(pageObject, args) then return end
	
	local ret = ''
	-- Check blacklist if necessary.
	if not needsBlacklistCheck(args) or not p.matchesBlacklist(pageObject.prefixedText) then
		if not nsParamsExist(mappings, args) then
			-- No namespace parameters exist; basic usage. Pass args[1] to
			-- [[Module:Namespace detect]] using the default namespace
			-- parameters, and return the result.
			local ndargs = {}
			for _, ndarg in ipairs(data.defaultNamespaces) do
				ndargs[ndarg] = args[1]
			end
			ndargs.page = args.page
			ndargs.demospace = args.demospace
			local ndresult = mNamespaceDetect._main(ndargs)
			if ndresult then
				ret = ret .. ndresult
			end
		else
			-- Namespace parameters exist; advanced usage.
			-- If the all parameter is specified, return it.
			local all = args.all
			if type(all) == 'string' then
				ret = ret .. all
			end
			
			-- Get the arguments to pass to [[Module:Namespace detect]].
			local ndargs = {}
			for ns, params in pairs(mappings) do
				for _, param in ipairs(params) do
					ndargs[param] = args[param] or args[data.other] or nil
				end
			end
			ndargs.other = args.other
			ndargs.page = args.page
			ndargs.demospace = args.demospace
			
			local data = mNamespaceDetect._main(ndargs)
			
			-- Work out what to return based on the result of the namespace detect call.
			local datanum = tonumber(data)
			if type(datanum) == 'number' then
				-- "data" is a number, so return that positional parameter.
				-- Remove non-positive integer values, as only positive integers
				-- from 1-10 were used with the old template.
				if datanum > 0 and math.floor(datanum) == datanum then
					local dataArg = args[datanum]
					if type(dataArg) == 'string' then
						ret = ret .. dataArg
					end
				end
			else
				-- "data" is not a number, so return it as it is.
				if type(data) == 'string' then
					ret = ret .. data
				end
			end
		end
	end
	return ret
end

function p.main(frame)
	-- If called via #invoke, use the args passed into the invoking
	-- template, or the args passed to #invoke if any exist. Otherwise
	-- assume args are being passed directly in.
	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 and remove blank arguments for the following args:
	-- 1, 2, 3 etc., "nocat", "categories", "subpage", and "page".
	local args = {}
	for k, v in pairs(origArgs) do
		if type(v) == 'string' then
			v = mw.text.trim(v) -- Trim whitespace.
		end
		if type(k) == 'number'
			or k == data.nocat
			or k == data.categories
			or k == data.subpage
			or k == data.page
		then
			if v ~= '' then
				args[k] = v
			end
		else
			args[k] = v
		end
	end
	
	-- Lower-case "nocat", "categories", "category2", and "subpage". These
	-- parameters are put in lower case whenever they appear in the old
	-- template, so we can just do it once here and save ourselves some work.
	local lowercase = {data.nocat, data.categories, data.category2, data.subpage}
	for _, v in ipairs(lowercase) do
		local argVal = args[v]
		if type(argVal) == 'string' then
			args[v] = mw.ustring.lower(argVal)
		end
	end
	
	return p._main(args)
end

return p