Jump to content

Module:Mock title

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Mr. Stradivarius (talk | contribs) at 14:48, 12 March 2021 (fix types in comment for constructMockTitle). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

local checkType = require('libraryUtil').checkType
local mRepr = require('Module:Repr')

local p = {}
local mockTitleRegistry = {}

--[[
-- Check that a named argument is one of multiple types.
--]]
local function checkTypeForNamedArgMulti(name, argName, arg, expectTypes)
	local argType = type(arg)
	for _, expectedType in ipairs(expectTypes) do
		if argType == expectedType then
			return
		end
	end
	error(
		string.format(
			"bad named argument %s to '%s' (%s expected, got %s)",
			argName,
			name,
			mw.text.listToText(expectTypes, ", ", " or "),
			argType
		),
		3
	)
end

--[[
-- Set a property on an object to the given value, if that value is not nil.
--]]
local function maybeSetProperty(object, property, value)
	if value ~= nil then
		rawset(object, property, value)
	end
end

--[[
-- Construct a mock title from a string, an ID or an options table. If we were
-- passed a mock title object to start with, return it as-is.
--]]
local function constructMockTitle(titleOrOptions)
	if titleOrOptions == nil then
		return nil
	end
	local titleOrOptionsType = type(titleOrOptions)
	if titleOrOptionsType == "string" or titleOrOptionsType == "number" then
		return p.MockTitle{page = titleOrOptions}
	elseif titleOrOptionsType == "table" then
		if type(titleOrOptions.getContent) == "function" then
			return titleOrOptions
		else
			return p.MockTitle(titleOrOptions)
		end
	else
		error(
			string.format(
				"Invalid type specified to constructMockTitle (expected string, table or nil; received %s",
				titleOrOptionsType
			),
			2
		)
	end
end

--[[
-- Patch an existing title object with the given options.
--]]
function p.patchTitleObject(title, options)
	-- Set simple properties
	for _, property in ipairs{"id", "isRedirect", "exists", "contentModel"} do
		maybeSetProperty(title, property, options[property])
	end
	
	-- Set redirect title
	maybeSetProperty(title, "redirectTitle", constructMockTitle(options.redirectTitle))
	
	-- Set protection levels
	local levels = {
		edit = {},
		move = {},
		create = {},
		upload = {},
	}
	local protectionSet = false
	for action in pairs(levels) do
		local level = options[action .. "Protection"]
		if level then
			levels[action][1] = level
			protectionSet = true
		end
	end
	if protectionSet then
		rawset(title, "protectionLevels", levels)
	end

	return title
end

--[[
-- Construct a new mock title.
--]]
function p.MockTitle(options)
	checkType("MockTitle", 1, options, "table")
	checkTypeForNamedArgMulti("MockTitle", "page", options.page, {"string", "number"})
	local title = mw.title.new(options.page)
	return p.patchTitleObject(title, options)
end

--[[
-- Register a mock title.
-- This can be a string, a mock title object or a table of options for
-- MockTitle.
--]]
function p.registerMockTitle(titleOrOptions)
	checkType("registerMockTitle", 1, titleOrOptions, "table")
	title = constructMockTitle(titleOrOptions)
	mockTitleRegistry[title.prefixedText] = title
end

--[[
-- Remove a title from the mock title registry.
-- Returns the title that was removed.
--]]
function p.deregisterMockTitle(titleOrOptions)
	checkType("deregisterMockTitle", 1, title, "table")
	title = constructMockTitle(titleOrOptions)
	registeredTitle = mockTitleRegistry[title.prefixedText]
	mockTitleRegistry[title.prefixedText] = nil
	return registeredTitle
end

--[[
-- Register multiple mock titles.
--]]
function p.registerMockTitles(...)
	checkType("registerMockTitles", 1, titleData, "table")
	for _, titleOrOptions in ipairs{...} do
		p.registerMockTitle(titleOrOptions)
	end
end

--[[
-- Deregister multiple mock titles.
-- Returns a sequence of titles that were removed.
--]]
function p.deregisterMockTitles(...)
	checkType("deregisterMockTitles", 1, titleData, "table")
	local removedTitles = {}
	for _, titleOrOptions in ipairs{...} do
		table.insert(removedTitles, p.deregisterMockTitle(titleOrOptions))
	end
	return removedTitles
end

--[[
-- Clear the mock title registry.
--]]
function p.clearMockTitleRegistry()
	mockTitleRegistry = {}
end

--[[
-- Patch the given title function.
-- This replaces the title function with a function that looks in the title
-- registry for the given title.
--]]
local function patchTitleFunc(funcName)
	local oldFunc = mw.title[funcName]
	mw.title[funcName] = function(...)
		local title = oldFunc(...)
		if not title then
			error(
				string.format(
					"Could not make title object from invocation %s",
					mRepr.invocationRepr{
						funcName = "mw.title." .. funcName,
						args = {...}
					}
				),
				2
			)
		end
		local mockTitle = mockTitleRegistry[title.prefixedText]
		if mockTitle then
			return mockTitle
		else
			return title
		end
	end
	return oldTitleNew
end

--[[
-- Patch mw.title.new.
-- The original mw.title.new is restored after running the given function.
--]]
function p.patchTitleNew(func, ...)
	local oldTitleNew = patchTitleFunc("new")
	func(...)
	mw.title.new = oldTitleNew
end

--[[
-- Patch mw.title.makeTitle.
-- The original mw.title.makeTitle is restored after running the given function.
--]]
function p.patchMakeTitle(func, ...)
	local oldMakeTitle = patchTitleFunc("makeTitle")
	func(...)
	mw.title.makeTitle = oldMakeTitle
end

--[[
-- Patch mw.title.new and mw.title.makeTitle.
-- The original title functions are restored after running the given function.
--]]
function p.patchTitleConstructors(func, ...)
	local oldTitleNew = patchTitleFunc("new")
	local oldMakeTitle = patchTitleFunc("makeTitle")
	func(...)
	mw.title.new = oldTitleNew
	mw.title.makeTitle = oldMakeTitle
end

return p