Module:FileInfo

From Zelda Wiki, the Zelda encyclopedia
Jump to navigation Jump to search
This is the main module for the following templates:
local p = {}
local h = {}

local Data = mw.loadData("Module:FileInfo/Data")

-- It's important to minimize the number of imports because this module is linked on every file page
-- Any change to these modules will trigger a large MediWiki job queue 
local Franchise = require("Module:Franchise")
local Util = {
	args = {
		parse = require("Module:Util/args/parse")	
	},
	pages = {
		exists = require("Module:Util/pages/exists")
	},
}

local MAX_IMAGE_AREA = require("Module:Constants/number/maxImageArea")

function h.warn(msg, details)
	local Error = require("Module:Error")
	return Error.warning(msg, details)
end

function p.StoreWidth(frame)
	return mw.title.getCurrentTitle().file.width
end
function p.StoreHeight(frame)
	return mw.title.getCurrentTitle().file.height
end

function p.Main(frame)
	local args = frame:getParent().args
	local args = h.preformat(args)
	local args, err = Util.args.parse(args, p.Templates.FileInfo)
	
	local result = h.printFileInfoTable(frame, args)
	local categories = h.categories(args.type, args.game, args.subject)
	
	-- Avoid categories from being added to [[MediaWiki:Upload-default-description]] and [[MediaWiki:Msu-comment]]
	if mw.title.getCurrentTitle().nsText == "MediaWiki" then
		return result, err
	end

	return categories, result, err -- categories first in case there's notices attached to them - see h.maintenanceCategories()
end
function h.printFileInfoTable(frame, args)
	local gameDisplay
	if args.game and args.game ~= "N/A" then
		local gameLogo = Franchise.logo(args.game)
		local gameImage = gameLogo and gameLogo ~= "" and string.format("[[%s|130x130px]]", gameLogo)
		local gameLink = Franchise.link(args.game)
		local gameText = gameLink and string.format("This is a file pertaining to %s.", gameLink)
		if gameImage and gameText then
			gameDisplay = gameImage .. " " .. gameText
		elseif gameText then
			gameDisplay = gameText
		else
			local err = h.warn(string.format("Invalid game code <code>%s</code>", args.game), "See [[:Category:Franchise codes]] for accepted codes.")
			local cat = require("Module:Constants/category/invalidArgs")
			gameDisplay = err.."[[Category:"..cat.."]]"
		end
	end
	
	local type = args.type and Data.types[args.type]
	local typeLink = type and type.category and string.format("[[:Category:%s|%s]]", type.category, args.type)

	local license
	if args.licensing and h.licenseExists(args.licensing) then
		license = frame:expandTemplate({
			title = "FileInfo/License/" .. args.licensing,
			args = {
				trademark = args.trademark
			}
		})
	else
		license = frame:expandTemplate({ title = "FileInfo/License/Unsure" })
	end

	local html = mw.html.create("table"):addClass("wikitable fileinfo")
	h.row(html, "Summary", args.summary)
	h.row(html, "Type", typeLink)
	h.row(html, "Source", args.source or frame:expandTemplate({ title = "No Source" }))
	h.row(html, "Game", gameDisplay)
	h.row(html, "Licensing", license, {
		rowspan = args.trademark and "2" or "1" 
	})
	h.row(html, "Trademark", args.trademark and frame:expandTemplate({ title = "FileInfo/License/Trademark" }))
	
	return tostring(html)
end
function h.row(html, field, value, attributes)
	if value then
		return html
			:tag("tr")
			:tag("th")
				:wikitext(field)
				:done()
			:tag("td")
				:wikitext(value)
				:done()
			:done()
	end
end

function h.categories(type, game, subjects)
	local categories = ""
	categories = categories..h.gameTypeCategories(game, type)
	categories = categories..h.subjectCategories(subjects)
	categories = categories..h.maintenanceCategories(type)
	return categories
end
function h.gameTypeCategories(game, type)
	local categories = ""
	local gameName = game and game ~= "N/A" and Franchise.shortName(game)
	local typeCat = type and Data.types[type] and Data.types[type].category
	local gameRequired = type and Data.types[type] and Data.types[type].gameRequired
	if typeCat and gameName and game ~= "Series" then
		categories = categories..string.format("[[Category:%s %s]]", gameName, typeCat)
	elseif typeCat then
		categories = categories..string.format("[[Category:%s]]", typeCat)
	elseif gameCat then
		categories = categories..string.format("[[Category:%s Files]]", gameName)
	end
	if game == "N/A" then
		categories = categories.."[[Category:Files with inapplicable game]]"
	end
	if type and gameRequired ~= false and not game then
		local errMsg = string.format("<code>game</code> parameter is required for file type <code>%s</code>.", type)
		local details = "For a list of accepted game values, see [[:Category:Franchise codes]]. If none of these values apply, set the <code>game</code> parameter to <code>N/A</code>."
		categories = categories..h.warn(errMsg, details).."[[Category:Files lacking game info]]"
	end
	return categories
end
function h.subjectCategories(subjects)
	if not subjects then
		return ""
	end
	local Term = require("Module:Term")
	
	local categories = ""
	for i, subject in ipairs(subjects) do
		local term, errCategories = Term.fetchTerm(subject, "Series")
		if not term then
			local utilsError = require("Module:UtilsError")
			local utilsMarkup = require("Module:UtilsMarkup")
			utilsError.warn(string.format("subject <code>%s</code> is not a valid [[Template:Term|term]]", subject))
			categories = categories..utilsMarkup.categories(errCategories)
		else
			term = string.gsub(term, "#", "") -- strip # from term because categories can't have them in their name
			 -- only add subject-based categories if they already exist, to avoid spamming Special:WantedCategories
			if Util.pages.exists("Category:Images of "..term) then
				categories = categories.."[[Category:Images of "..term.."]]"
			end
		end
	end
	return categories
end
function h.maintenanceCategories(type)
	local categories = ""
	local frame = mw.getCurrentFrame()
	local title = mw.title.getCurrentTitle()
	local mimeType = title.file and title.file.mimeType
	
	if type == "Sprite" and mimeType == "image/gif" then
		categories = categories..frame:expandTemplate({ title = "FileInfo/Notices/GIF Sprite" })
	end
	
	local exceedsMaxImageArea = title.file and ((title.file.width or 0) * (title.file.height or 0)) > MAX_IMAGE_AREA
	if mimeType ~= "image/jpeg" and exceedsMaxImageArea then
		categories = categories..frame:expandTemplate({ title = "FileInfo/Notices/Oversized" })
	end
	
	-- See category page for why we do this ourselves instead of using Special:UnusedFiles
	if title.nsText == "File" and h.isUnused(title.text) then
		categories = categories..frame:expandTemplate({ title = "FileInfo/Notices/Unused" })
	end
	
	return categories
end
function h.isUnused(filename)
	local separator = "$separator$"
	local dplQuery = string.format("{{#dpl:|imageused=%s|count=2}}", filename, separator)
	local dplResult = mw.getCurrentFrame():preprocess(dplQuery)
	return dplResult == ""
end

-- For supporting types written in lower case or Title Case
-- Previously only the former was supported but editors had a tendency to use the latter
-- Which is understandable because the type category is in title case, Template:Media is in title case, 
-- and all other fields in FileInfo are in title case or sentence case
-- We may eventually decide to support only Title Case but that would mean 
-- running a text-replace on the thousands of file pages using lowercase
function h.preformat(args)
	local _args = {}
	for k, v in pairs(args) do
		if k == "type" then
			_args[k] = string.gsub(" "..v, "%W%l", string.upper):sub(2)
		else
			_args[k] = v
		end
	end
	return _args
end

function h.licenseExists(license)
	for i, definedLicense in ipairs(Data.licenses) do
		if license == definedLicense then
			return true
		end		
	end
	return false
end

function p.enumTypes()
	local enum = {}
	for k in pairs(Data.types) do
		table.insert(enum, k)
	end
	enum.reference = "[[Module:FileInfo/Data]]"
	return enum
end
function p.enumLicenses()
	local enum = {}
	for i, license in ipairs(Data.licenses) do
		enum[i] = license
	end
	enum.reference = "[[Module:FileInfo/Data]]"
	return enum
end

p.Templates = {
	FileInfo = {
		purpose = "Displays, categorizes, and stores file information. See [[Guidelines:Files]] for further guidance.",
		format = "block",
		paramOrder = {"summary", "type", "source", "game", "licensing", "subject", "trademark"},
		boilerplate = {
			tabs = {
				{
					label = "Zelda-Related Files",
					params = {"summary", "type", "source", "game", "licensing", "subject"},
				},
				{
					label = "Other Files",
					params = {"summary", "type", "source", "licensing"},
				},
				{
					label = "All Parameters",
					params = {"summary", "type", "source", "game", "licensing", "subject", "trademark"},
				},
			},
		},
		params = {
			summary = {
				--required = true,
				type = "content",
				desc = "A short description of the file.",
				trim = true,
				nilIfEmpty = true,
			},
			type = {
				required = "Category:Files lacking type info",
				type = "string",
				desc = "The type of file, which determines how it is [[:Category:Files by Type|categorized]].",
				enum = p.enumTypes(),
				trim = true,
				nilIfEmpty = true,
			},
			source = {
				required = "Category:Files Lacking Sources",
				type = "string",
				desc = "The original source of the file. It may be in the form of a URL or author recognition. [[Template:Source]] exists for this purpose.",
				trim = true,
				nilIfEmpty = true,
			},
			subject = {
				type = "string",
				desc = "Wiki article names of all the subjects depicted in the file. A comma-separated list.",
				split = true,
				trim = true,
				nilIfEmpty = true,
			},
			game = {
				--required = true,
				type = "string",
				desc = "<p>A valid [[:Category:Franchise codes|franchise code]] for a game, book, comic, manga, or TV show (or <code>Series</code>).</p>"
					.."<p>Required for game-based types such as <code>Artwork</code>, <code>Screenshot</code>, <code>Sprite</code>, etc. See [[Module:FileInfo/Data]] for full list.</p>"
					.."<p><code>game</code> can be set to <code>N/A</code> when a game is required for the type but no franchise code applies, namely for images of [[The Legend of Zelda in Popular Culture|unlicensed media]].",
				trim = true,
				nilIfEmpty = true,
			},
			licensing = {
				required = "Category:Unlicensed Files",
				type = "string",
				desc = "The copyright licensing for the file. For the vast majority of files, <code>Copyright</code> is the correct value here.",
				enum = p.enumLicenses(),
				trim = true,
				nilIfEmpty = true,
			},
			trademark = {
				type = "boolean",
				desc = "Enter any text to add a trademark notice to the licensing. Use on all [[:Category:Trademarks|trademarks]] (usually denoted by an ® or ™ symbol).",
				trim = true,
				nilIfEmpty = true,
			}
		}
	}
}

return p