Module:Category handler: Difference between revisions
From All Skies Encyclopaedia
| imported>Mr. Stradivarius  (Rewrite this using Module:Middleclass. It is now more efficient: it only parses parameters when necessary, and caches data with mw.loadData where it can.) | imported>WOSlinker   (apply changes from sandbox as requested by User:Jackmcbarn which removes middleclass dependency) | ||
| Line 15: | Line 15: | ||
| -- Load required modules | -- Load required modules | ||
| local class = require('Module:Middleclass').class | |||
| local yesno = require('Module:Yesno') | local yesno = require('Module:Yesno') | ||
| Line 47: | Line 46: | ||
| -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ||
| local CategoryHandler =  | local CategoryHandler = {} | ||
| CategoryHandler.__index = CategoryHandler | |||
| function CategoryHandler | function CategoryHandler.new(data, args) | ||
| 	local obj = setmetatable({ _data = data, _args = args }, CategoryHandler) | |||
| 	self._data = data | |||
| 	self._args = args | |||
| 	-- Set the title object | 	-- Set the title object | ||
| 	do | 	do | ||
| 		local pagename =  | 		local pagename = obj:parameter('demopage') | ||
| 		local success, titleObj = pcall(mw.title.new, pagename) | 		local success, titleObj = pcall(mw.title.new, pagename) | ||
| 		if success and titleObj then | 		if success and titleObj then | ||
| 			obj.title = titleObj | |||
| 		else | 		else | ||
| 			obj.title = mw.title.getCurrentTitle() | |||
| 			obj._usesCurrentTitle = true | |||
| 		end | 		end | ||
| 	end | 	end | ||
| Line 67: | Line 66: | ||
| 	-- Set suppression parameter values | 	-- Set suppression parameter values | ||
| 	for _, key in ipairs{'nocat', 'categories'} do | 	for _, key in ipairs{'nocat', 'categories'} do | ||
| 		local value =  | 		local value = obj:parameter(key) | ||
| 		value = trimWhitespace(value, true) | 		value = trimWhitespace(value, true) | ||
| 		obj['_' .. key] = yesno(value) | |||
| 	end | 	end | ||
| 	do | 	do | ||
| 		local subpage =  | 		local subpage = obj:parameter('subpage') | ||
| 		local category2 =  | 		local category2 = obj:parameter('category2') | ||
| 		if type(subpage) == 'string' then | 		if type(subpage) == 'string' then | ||
| 			subpage = mw.ustring.lower(subpage) | 			subpage = mw.ustring.lower(subpage) | ||
| Line 80: | Line 79: | ||
| 			subpage = mw.ustring.lower(category2) | 			subpage = mw.ustring.lower(category2) | ||
| 		end | 		end | ||
| 		obj._subpage = trimWhitespace(subpage, true) | |||
| 		obj._category2 = trimWhitespace(category2) -- don't remove blank values | |||
| 	end | 	end | ||
| 	return obj | |||
| end | end | ||
| Line 239: | Line 239: | ||
| function p._main(args, data) | function p._main(args, data) | ||
| 	data = data or mw.loadData('Module:Category handler/data') | 	data = data or mw.loadData('Module:Category handler/data') | ||
| 	local handler = CategoryHandler | 	local handler = CategoryHandler.new(data, args) | ||
| 	if handler:isSuppressed() then | 	if handler:isSuppressed() then | ||
| 		return nil | 		return nil | ||
Revision as of 17:57, 13 July 2014
Documentation for this module may be created at Module:Category handler/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]].   --
--                                                                            --
--------------------------------------------------------------------------------
-- Load required modules
local yesno = require('Module:Yesno')
-- Lazily load things we don't always need
local mShared, mappings
local p = {}
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function trimWhitespace(s, removeBlanks)
	if type(s) ~= 'string' then
		return s
	end
	s = s:match('^%s*(.-)%s*$')
	if removeBlanks then
		if s ~= '' then
			return s
		else
			return nil
		end
	else
		return s
	end
end
--------------------------------------------------------------------------------
-- CategoryHandler class
--------------------------------------------------------------------------------
local CategoryHandler = {}
CategoryHandler.__index = CategoryHandler
function CategoryHandler.new(data, args)
	local obj = setmetatable({ _data = data, _args = args }, CategoryHandler)
	
	-- Set the title object
	do
		local pagename = obj:parameter('demopage')
		local success, titleObj = pcall(mw.title.new, pagename)
		if success and titleObj then
			obj.title = titleObj
		else
			obj.title = mw.title.getCurrentTitle()
			obj._usesCurrentTitle = true
		end
	end
	-- Set suppression parameter values
	for _, key in ipairs{'nocat', 'categories'} do
		local value = obj:parameter(key)
		value = trimWhitespace(value, true)
		obj['_' .. key] = yesno(value)
	end
	do
		local subpage = obj:parameter('subpage')
		local category2 = obj:parameter('category2')
		if type(subpage) == 'string' then
			subpage = mw.ustring.lower(subpage)
		end
		if type(category2) == 'string' then
			subpage = mw.ustring.lower(category2)
		end
		obj._subpage = trimWhitespace(subpage, true)
		obj._category2 = trimWhitespace(category2) -- don't remove blank values
	end
	return obj
end
function CategoryHandler:parameter(key)
	local parameterNames = self._data.parameters[key]
	local pntype = type(parameterNames)
	if pntype == 'string' or pntype == 'number' then
		return self._args[parameterNames]
	elseif pntype == 'table' then
		for _, name in ipairs(parameterNames) do
			local value = self._args[name]
			if value ~= nil then
				return value
			end
		end
		return nil
	else
		error(string.format(
			'invalid config key "%s"',
			tostring(key)
		), 2)
	end
end
function CategoryHandler:isSuppressedByArguments()
	return
		-- See if a category suppression argument has been set.
		self._nocat == true
		or self._categories == false
		or (
			self._category2
			and self._category2 ~= self._data.category2Yes
			and self._category2 ~= self._data.category2Negative
		)
		-- Check whether we are on a subpage, and see if categories are
		-- suppressed based on our subpage status.
		or self._subpage == self._data.subpageNo and self.title.isSubpage
		or self._subpage == self._data.subpageOnly and not self.title.isSubpage
end
function CategoryHandler:shouldSkipBlacklistCheck()
	-- Check whether the category suppression arguments indicate we
	-- should skip the blacklist check.
	return self._nocat == false
		or self._categories == true
		or self._category2 == self._data.category2Yes
end
function CategoryHandler:matchesBlacklist()
	if self._usesCurrentTitle then
		return self._data.currentTitleMatchesBlacklist
	else
		mShared = mShared or require('Module:Category handler/shared')
		return mShared.matchesBlacklist(
			self.title.prefixedText,
			mw.loadData('Module:Category handler/blacklist')
		)
	end
end
function CategoryHandler:isSuppressed()
	-- Find if categories are suppressed by either the arguments or by
	-- matching the blacklist.
	return self:isSuppressedByArguments()
		or not self:shouldSkipBlacklistCheck() and self:matchesBlacklist()
end
function CategoryHandler:getNamespaceParameters()
	if self._usesCurrentTitle then
		return self._data.currentTitleNamespaceParameters
	else
		if not mappings then
			mShared = mShared or require('Module:Category handler/shared')
			mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData
		end
		return mShared.getNamespaceParameters(
			self.title,
			mappings
		)
	end
end
function CategoryHandler:namespaceParametersExist()
	-- Find whether any namespace parameters have been specified.
	-- We use the order "all" --> namespace params --> "other" as this is what
	-- the old template did.
	if self:parameter('all') then
		return true
	end
	if not mappings then
		mShared = mShared or require('Module:Category handler/shared')
		mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData
	end
	for ns, params in pairs(mappings) do
		for i, param in ipairs(params) do
			if self._args[param] then
				return true
			end
		end
	end
	if self:parameter('other') then
		return true
	end
	return false
end
function CategoryHandler:getCategories()
	local params = self:getNamespaceParameters()
	local nsCategory
	for i, param in ipairs(params) do
		local value = self._args[param]
		if value ~= nil then
			nsCategory = value
			break
		end
	end
	if nsCategory ~= nil or self:namespaceParametersExist() then
		-- Namespace parameters exist - advanced usage.
		if nsCategory == nil then
			nsCategory = self:parameter('other')
		end
		local ret = {self:parameter('all')}
		local numParam = tonumber(nsCategory)
		if numParam and numParam >= 1 and math.floor(numParam) == numParam then
			-- nsCategory is an integer
			ret[#ret + 1] = self._args[numParam]
		else
			ret[#ret + 1] = nsCategory
		end
		if #ret < 1 then
			return nil
		else
			return table.concat(ret)
		end
	elseif self._data.defaultNamespaces[self.title.namespace] then
		-- Namespace parameters don't exist, simple usage.
		return self._args[1]
	end
	return nil
end
--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------
local p = {}
function p._exportClasses()
	-- Used for testing purposes.
	return {
		CategoryHandler = CategoryHandler
	}
end
function p._main(args, data)
	data = data or mw.loadData('Module:Category handler/data')
	local handler = CategoryHandler.new(data, args)
	if handler:isSuppressed() then
		return nil
	end
	return handler:getCategories()
end
function p.main(frame, data)
	data = data or mw.loadData('Module:Category handler/data')
	local args = require('Module:Arguments').getArgs(frame, {
		wrappers = data.wrappers,
		valueFunc = function (k, v)
			v = trimWhitespace(v)
			if type(k) == 'number' then
				if v ~= '' then
					return v
				else
					return nil
				end
			else
				return v
			end
		end
	})
	return p._main(args, data)
end
return p







