Module:Portal

From Soyjak Wiki, the free ensoyclopedia
Jump to navigationJump to search

This module implements {{Portal}} and provides other functionalities. See the template documentation for more usage info.

Portal images and aliases are defined in Module:Portal/images.

Portal image data

Portal images[a] and aliases[b]. /images uses the following format:

return {
    images = {
        ['portal name'] = 'icon.png|link=',

        ['another portal name'] = 'another icon.png|link=',

    },
    aliases = {
        ['alternate name'] = 'portal name',

        ['another alternate name'] = 'another portal name'

    },
}

Image keys must be lowercase portal names. Image values contain the filename without the File: prefix and may include additional file arguments after the filename. The only recommended argument is link=.

Alias keys must be lowercase. Each alias value must correspond directly to a key in the images table. Aliases must not point to other aliases.

Aliases affect image lookup only. They do not change the generated portal link or redirect one portal name to another.

Notes

  1. ↑ these are the small icons that appear in the template.
  2. ↑ aliases are alternate portal names that use the image assigned to another portal. They affect only image lookup and do not change portal links.

--[[
Fork of https://en.wikipedia.org/wiki/Module:Portal
Forked by User:Rickert
]]
local p = {}
local templatestyles = 'Module:Portal/styles.css'
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')

local imageData = mw.loadData('Module:Portal/images')
local badNamespaces = {4, 10}

local function checkTracking(title)
	local thisPage = title or mw.title.getCurrentTitle()
	if thisPage.isTalkPage then
		return false
	end
	local ns = thisPage.namespace
	for _, v in ipairs(badNamespaces) do
		if ns == v then
			return false
		end
	end
	return true
end

local function matchImagePage(s)
	if type(s) ~= 'string' or #s < 1 then return end
	return imageData.images[s]
end

local function getAlias(s)
	return imageData.aliases[s]
end

local defaultImage = 'Portal-puzzle.svg|link='

local function getImageName(s)
	if type(s) ~= 'string' or #s < 1 then
		return defaultImage
	end
	s = mw.ustring.lower(s)
	local image = matchImagePage(s) or matchImagePage(getAlias(s)) or defaultImage
	image = mw.ustring.gsub(image,'^File:','')
	image = mw.ustring.gsub(image,'^Image:','')
	return image
end

local function exists(title)
	local success, exists = pcall(function() return title.exists end)
	return not success or exists
end

function p._checkPortals(portals, args)
	local trackingCat = ''
	local errMsg = nil

	local trackingEnabled = args.tracking and checkTracking()

	args.minPortals = args.minPortals or 1
	args.maxPortals = args.maxPortals or -1
	if #portals < args.minPortals then
		errMsg = 'please specify at least '..args.minPortals..' portal'..(args.minPortals > 1 and 's' or '')
		trackingCat = (trackingEnabled and '[[Category:Portal templates with too few portals]]' or '')
		return portals, trackingCat, errMsg
	end
	if args.maxPortals >= 0 and #portals > args.maxPortals then
		errMsg = 'too many portals (maximum = '..args.maxPortals..')'
		trackingCat = (trackingEnabled and '[[Category:Portal templates with too many portals]]' or '')
		return portals, trackingCat, errMsg
	end
	if not args.redlinks or trackingEnabled then
		local existingPortals = {}
		for _, portal in ipairs(portals) do
			local portalTitle = mw.title.new(portal,"Portal")
			if portalTitle and exists(portalTitle) then
				table.insert(existingPortals,portal)
			elseif trackingEnabled then
				trackingCat = "[[Category:Portal templates with redlinked portals]]"
			end
		end
		portals = args.redlinks and portals or existingPortals
		if #portals == 0 and trackingEnabled then
			trackingCat = trackingCat.."[[Category:Pages with empty portal template]]"
		end
	end
	return portals, trackingCat, errMsg
end

local function portalBox(args)
	return mw.html.create('ul')
		:attr('role', 'navigation')
		:attr('aria-label', 'Portals')
		:addClass('noprint')
		:addClass(args.error and '' or 'portalbox')
		:addClass(args.border and 'portalborder' or '')
		:addClass(args.left and 'portalleft' or 'portalright')
		:css('margin', args.margin or nil)
		:newline()
end

local function fillBox(root, contents)
	for _, item in ipairs(contents) do
		local entry = root:tag('li')
		entry:addClass('portalbox-entry')
		local image = entry:tag('span')
		image:addClass('portalbox-image')
		image:wikitext(item[1])
		local link = entry:tag('span')
		link:addClass('portalbox-link')
		link:wikitext(item[2])
	end
	return root
end

local function noviewer(portalImage)
    local portalImage, hasClass = mw.ustring.gsub(portalImage, "class%s*=[^%|]+", "%0 noviewer")
    if hasClass == 0 then
        portalImage = portalImage.."|class=noviewer"
    end
    return portalImage
end

function p._portal(portals, args)
	if args.redlinks == 'include' then args.redlinks = true end
	args.addBreak = args['break']
	for key, default in pairs({left=false,tracking=true,nominimum=false,
		                       redlinks=false,addBreak=false,border=true}) do
		if args[key] == nil then args[key] = default end
		args[key] = yesno(args[key], default)
	end

	local root = portalBox(args)

	local trackingCat = ''
	local errMsg = nil
	args.minPortals = args.nominimum and 0 or 1
	args.maxPortals = -1
	portals, trackingCat, errMsg = p._checkPortals(portals, args)
	root:wikitext(trackingCat)
	if errMsg then
		if args.border then
			args.error = true
			root = portalBox(args)
			root:wikitext(trackingCat)
			local errTag = root:tag('strong')
			errTag:addClass('error')
			errTag:css('padding','0.2em')
			errTag:wikitext('Error: '..errMsg)
		end
		return tostring(root)
	end
	if #portals == 0 then
		return trackingCat
	end

	local contents = {}
	local defaultUsed = nil
	for _, portal in ipairs(portals) do
		local portalImage = getImageName(portal)
		if portalImage == defaultImage then
			defaultUsed = portal
        else
            portalImage = noviewer(portalImage)
		end
		local image = string.format('[[File:%s|32x28px]]',
			portalImage)
		local link = string.format('[[Portal:%s|%s%sportal]]',
			portal, portal, args.addBreak and '<br />' or ' ')
		table.insert(contents, {image, link})
	end
	if defaultUsed and args.tracking and checkTracking() then
		local cat = string.format('[[Category:Portal templates with default image|%s]]',
			                      defaultUsed)
		root:wikitext(cat)
	end
	return tostring(fillBox(root, contents))
end

function p._demo(imageList, args)
	for key, default in pairs({left=false,border=true}) do
		if args[key] == nil then args[key] = default end
		args[key] = yesno(args[key], default)
	end

	local root = portalBox(args)

	local contents = {}
	for _, fn in ipairs(imageList) do
		local image = string.format('[[File:%s|32x28px]]',noviewer(fn))
		local link = string.format('[[:File:%s|%s]]',fn,fn)
		table.insert(contents,{image,link})
	end

	return tostring(fillBox(root,contents))
end

function p._image(portal,keep)
	if type(portal) == 'table' then
		portal = portal[1]
	end
	local name = getImageName(portal)
	local keepargs = yesno(keep)
    if keepargs then
        return noviewer(name)
    end
	local args = mw.text.split(name, "|", true)
	local result = {args[1]}
	local category = ''
	for i = 2,#args do
		local m = mw.ustring.match(args[i],  "^%s*category%s*=")
		if m then
			table.insert(result, args[i])
		end
	end
	return table.concat(result,"|")
end

local function getAllImageTable()
	local images = {}
	for portal, image in pairs(imageData.images) do
		local args = mw.text.split(image,"|", true)
		images[portal] = args[1]
	end
	return images
end

function p._displayAll(portals, args)
	local lang = mw.language.getContentLanguage()
	portals = portals or {}
	for portal in pairs(getAllImageTable()) do
		table.insert(portals,lang:ucfirst(portal))
	end
	table.sort(portals)
	args.redlinks = args.redlinks or "yes"
	return p._portal(portals, args)
end

function p._imageDupes()
	local exists, dupes = {}, {}
	for portal, image in pairs(getAllImageTable()) do
		if not exists[image] then
			exists[image] = portal
		else
			table.insert(dupes, string.format('The image "[[:File:%s|%s]]" is used for both portals "%s" and "%s".', image, image, exists[image], portal))
		end
	end
	if #dupes < 1 then
		return 'No duplicate images found.'
	else
		return 'The following duplicate images were found:\n* ' .. table.concat(dupes, '\n* ')
	end
end

local function processPortalArgs(args)
	args = type(args) == 'table' and args or {}
	local portals = {}
	local namedArgs = {}
	for k, v in pairs(args) do
		if type(k) == 'number' and type(v) == 'string' then
			table.insert(portals, k)
		elseif type(k) ~= 'number' then
			namedArgs[k] = v
		end
	end
	table.sort(portals)
	for i, v in ipairs(portals) do
		portals[i] = args[v]
	end
	return portals, namedArgs
end

function p._processPortalArgs(args)
	return processPortalArgs(args)
end

function p.image(frame)
	local origArgs = getArgs(frame)
	local portals, args = processPortalArgs(origArgs)
	return p._image(portals[1],args.border)
end

function p.demo(frame)
	local args = getArgs(frame)
	local styles = frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles} }
	return styles..p._demo(args,args)
end

local function makeWrapper(funcName)
	return function (frame)
		local origArgs = getArgs(frame)
		local portals, args = processPortalArgs(origArgs)
		local results = ''
		if funcName == '_portal' or funcName == '_displayAll' then
			results = frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles} }
		end
		return results .. p[funcName](portals, args)
	end
end

for _, funcName in ipairs{'portal', 'imageDupes', 'displayAll'} do
	p[funcName] = makeWrapper('_' .. funcName)
end

return p