Module:Find sources

From Zoophilia Wiki
Revision as of 11:37, 26 September 2014 by meta>Mr. Stradivarius (use named keys for the template link config to make it easier to read the config page)
Jump to navigationJump to search

Documentation for this module may be created at Module:Find sources/doc

-- This module implements {{find sources}} and other similar templates, and also
-- provides a mechanism to easily create new source-finding templates.
--
-- Template settings are found in subpages of [[Module:Find sources/templates]].
-- Link functions are defined in subpages of [[Module:Find sources/links]].
-- Functions shared between the link modules are stored at
-- [[Module:Find sources/shared]].

local ROOT_PAGE = 'Module:Find sources'
local TEMPLATE_ROOT = ROOT_PAGE .. '/templates/'
local LINK_ROOT = ROOT_PAGE .. '/links/'

local checkType = require('libraryUtil').checkType

local p = {}

local function maybeLoadData(page)
	local success, data = pcall(mw.loadData, page)
	if success then
		return data
	else
		return nil
	end
end

local function substituteParams(msg, ...)
	local params = {...}
	if params[1] then
		return mw.message.newRawMessage(msg):params(params):plain()
	else
		return msg
	end
end

local function renderSearchString(searchTerms, separator, transformFunc)
	local searchStrings = {}
	for i, s in ipairs(searchTerms) do
		searchStrings[i] = s
	end
	if transformFunc then
		for i, s in ipairs(searchStrings) do
			searchStrings[i] = transformFunc(s)
		end
	end
	return table.concat(searchStrings, separator)
end

local function renderLink(code, searchTerms, display)
	-- Get link config.
	local linkCfg = maybeLoadData(LINK_ROOT .. code)
	if not linkCfg then
		error(string.format(
			"invalid link code '%s'; no link config found at [[%s]]",
			code,
			LINK_ROOT .. code
		))
	end

	-- Make URL.
	local url
	do
		local separator = linkCfg.separator or "+"
		local searchString = renderSearchString(
			searchTerms,
			separator,
			mw.uri.encode
		)
		url = linkCfg.url or error(string.format(
			"no 'url' field found in the link config at [[%s]]",
			LINK_ROOT .. code
		))
		url = substituteParams(url, searchString)
	end
	
	-- Fetch display.
	display = display or linkCfg.display or error(string.format(
		"no 'display' field found in the link config at [[%s]]",
		LINK_ROOT .. code
	))

	return string.format('[%s %s]', url, display)
end

local function checkTemplateCfgLinkTable(cfgPage, key, t)
	if type(t) ~= 'table' then
		error(string.format(
			"invalid link table found in key '%s' of template config at [[%s]] " ..
				"(expected type table, got type %s)",
			key, cfgPage, type(t)
		))
	elseif type(t.code) ~= 'string' then
		error(string.format(
			"invalid link table found in key '%s' of template config at [[%s]] " ..
				"(the first table value must be a string)",
			key, cfgPage
		))
	elseif t.display and type(t.display) ~= 'string' then
		error(string.format(
			"invalid link table found in key '%s' of template config at [[%s]] " ..
				"(if the second table value is specified, it must be a string)",
			key, cfgPage
		))
	end
end

function p._main(template, args)
	checkType('_main', 1, template, 'string')
	checkType('_main', 2, args, 'table', true)
	args = args or {}
	local title = mw.title.getCurrentTitle()

	-- Get the template config.
	local templateCfgPage = TEMPLATE_ROOT .. template
	local templateCfg = maybeLoadData(templateCfgPage)
	if not templateCfg then
		error(string.format(
			"invalid template name '%s'; no template config found at [[%s]]",
			template, templateCfgPage
		))
	end

	-- Template config sanity check.
	if templateCfg.introLink then
		checkTemplateCfgLinkTable(templateCfgPage, 'introLink', templateCfg.introLink)
	end
	if type(templateCfg.links) ~= 'table' then
		error(string.format(
			"type error in the 'links' field of the template config at [[%s]] " ..
				"(expected table, got %s)",
			templateCfgPage, type(templateCfg.links)
		))
	end
	for _, t in ipairs(templateCfg.links) do
		checkTemplateCfgLinkTable(templateCfgPage, 'links', t)
	end
	if type(templateCfg.blurb) ~= 'string' then
		error(string.format(
			"type error in the 'blurb' field of the template config at [[%s]] " ..
				"(expected string, got %s)",
			templateCfgPage, type(templateCfg.blurb)
		))
	end

	-- Namespace check.
	if not templateCfg.isUsedInMainspace and title.namespace == 0 then
		return '<strong class="error">' ..
			'Error: Please do not use this template in articles.' ..
			'</strong>' ..
			'[[Category:Pages with templates in the wrong namespace]]' ..
			'[[Category:Find sources multi transclusions with errors]]'
	end

	-- Get the search terms from the arguments.
	local searchTerms = {}
	for i, s in ipairs(args) do
		searchTerms[i] = s
	end
	searchTerms[1] = searchTerms[1] or title.subpageText
	searchTerms[1] = '"' .. searchTerms[1] .. '"'

	-- Make the intro link
	local introLink
	if templateCfg.introLink then
		local code = templateCfg.introLink.code
		local display = templateCfg.introLink.display or renderSearchString(
			searchTerms,
			'&nbsp;'
		)
		introLink = renderLink(code, searchTerms, display)
	else
		introLink = ''
	end

	-- Make the other links
	local links = {}
	for i, t in ipairs(templateCfg.links) do
		links[i] = renderLink(t.code, searchTerms, t.display)
	end
	local separator = templateCfg.separator or mw.message.new('Dot-separator'):plain()
	links = table.concat(links, separator)

	-- Make the blurb.
	local blurb = substituteParams(templateCfg.blurb, introLink, links)
	local span = mw.html.create('span')
	span
		:addClass('plainlinks')
		:addClass(templateCfg.class)
		:cssText(templateCfg.style)
		:wikitext(blurb)

	return tostring(span)
end

setmetatable(p, { __index = function(t, template)
	return function(frame)
		local args = require('Module:Arguments').getArgs(frame, {
			wrappers = mw.site.namespaces[10].name .. ':' .. template
		})
		return t._main(template, args)
	end
end})

return p