Module:Category handler/sandbox

From blackwiki
< Module:Category handler
Revision as of 02:16, 7 July 2014 by 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)
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(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