Module:Category handler/sandbox

From blackwiki
< Module:Category handler
Revision as of 02:46, 7 July 2014 by blackwiki>Mr. Stradivarius (deal with nocat/blacklist logic at the same time, use "titleObj" rather than "pageObj", and cache things with mw.loadData where we can)
Jump to navigation Jump to search

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(titleObj, 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 titleObj 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 titleObj and titleObj.isSubpage then
		return false
	end
	if args[data.subpage] == data.subpageOnly 
		and (not titleObj or (titleObj and not titleObj.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

function p.matchesBlacklist(page, blacklist)
	-- Wrapper function to maintain backwards compatibility. This is not always
	-- needed, so the /shared module is only loaded when required.
	return require('Module:Category handler/shared').matchesBlacklist
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 titleObj = mNamespaceDetect.getPageObject(args[data.demopage])
	local mappings = mNamespaceDetect.getParamMappings()
	
	if not needsCategory(titleObj, args)
		or (needsBlacklistCheck(args) and (
			args[data.demopage] and p.matchesBlacklist(
				titleObj.prefixedText,
				mw.loadData('Module:Category handler/blacklist')
			)
			or data.currentTitleMatchesBlacklist
		))
	then
		return nil
	end
		
	local ret = {}
	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 + 1] = 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 + 1] = 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 + 1] = data
			end
		end
	end
	return table.concat(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