July 25, 2020

👭 Knight Challenge #11 👬

Want to try your hand at these challenges? There's a couple of things you can do!
From writing, to research, to images, find your preferred way to contribute with our eleventh theme: Couples!

Latest Announcements

Module:UtilsMarkup/Link

From Zelda Wiki, the Zelda encyclopedia
Jump to: navigation, search

This submodule provides utility functions for formatting text with wiki markup.

Lua error: Error in "where" parameter: the string "#" cannot be used within #cargo_query..


local p = {}
local h = {}

local utilsMarkup = require("Module:UtilsMarkup/Format")
local utilsPage = require("Module:UtilsPage")
local utilsString = require("Module:UtilsString")
local utilsTable = require("Module:UtilsTable")

-- If backlink == false then this print an external link that looks like an internal one 
-- (useful for creating links that do not appear in Special:WhatLinksHere)
function p.link(link, text, noBacklink)
	if not link then
		return ""
	end
	link = mw.text.trim(link)
	if link:find('http') then
		return h.externalLink(link, text)
	end
	local url = mw.uri.fullUrl(link)
	local isInterwiki = not url or url.host ~= mw.uri.new(mw.title.getCurrentTitle():fullUrl()).host
	if noBacklink and not isInterwiki then
		local url, exists = h.getUrl(link)
		local externalLink = h.externalLink(tostring(url), text)
		local formattedLink = utilsMarkup.class(exists and "plainlinks" or "plainlinks new", externalLink)
		return formattedLink
	end
	local title = mw.title.new(link) or link
	if title.nsText == "File" or title.nsText == "Category" then
		link = ":" .. link
		if text == "" then
			text = title.text
		end
	end
	return h.internalLink(link, text)
end

function h.getUrl(page)
	local url = mw.uri.fullUrl(page)
	local pageExists = utilsPage.exists(page) or utilsString.startsWith(page, "Special")
	if not pageExists then
		url:extend({
			action = "edit",
			redlink = "1"
		})
	end
	return tostring(url), pageExists
end

function p.sectionLink(page, section, display)
	local target = page
	if section then
		target = target .. "#" .. section
	end
	return p.link(target, display)
end

function h.internalLink(page, display)
	if not page then
		return ""
	end
	if not display then
		return ("[[%s]]"):format(page)
	end
	return ('[[%s|%s]]'):format(page, display)
end

function h.externalLink(page, display)
	if not page then
		return ""
	end
	if not display then
		return page
	end
	return ('[%s %s]'):format(page, display)
end

function p.file(name, options)
	options = options or {}
	local size = options.size
	local link = options.link
	local caption = options.caption or name
	local noBacklink = options.noBacklink
	
	if not utilsString.startsWith(name, "File:") then
		name = "File:" .. name
	end
	if link and noBacklink then
		link = h.getUrl(link)
	end
	local linkParts = utilsTable.compact({
		name,
		size,
		link and "link="..link,
		caption,
	})
	local fileLink = "[[" .. table.concat(linkParts, "|") .. "]]"
	return fileLink
end

function p.category(name, sortKey)
	if not utilsString.startsWith(name, "Category:") then
		name = "Category:" .. name
	end
	return h.internalLink(name, sortKey)
end

function p.categories(categoryNames)
	local categoryLinks = utilsTable.map(categoryNames, p.category)
	return table.concat(categoryLinks)
end

function p.containsLink(wikitext, external)
	return not not wikitext:find("%[%[.*%]%]")
end

function p.killBacklinks(wikitext)
	local link = string.match(wikitext, "%[%[[^%]]*%]%]")
	if not link then
		return wikitext
	end
	local startPos, endPos = string.find(wikitext, link, 1, true)
	local before = string.sub(wikitext, 1, startPos -1)
	local after = string.sub(wikitext, endPos + 1)
	local externalLink = p.killBacklink(link)
	return before .. externalLink .. p.killBacklinks(after)
end

function p.killBacklink(link)
	if utilsString.startsWith(link, "[[Category:") then
		return link
	end
	if utilsString.startsWith(link, "[[File:") then
		return link -- not dealing with this now, but it's doable
	end
	link = mw.text.trim(link, "][")
	link = string.gsub(link, "^:", "")
	local pipe = string.find(link, "|")
	local page = string.sub(link, 1, pipe and pipe - 1)
	local display = pipe and string.sub(link, pipe + 1)
	return p.link(page, display or page, true)
end

local categoryPattern = "%[%[Category:[^%]]*%]%]"
function p.stripCategories(wikitext)
	local categories = {}
	for category in string.gmatch(wikitext, categoryPattern) do
		local categoryWithoutSortKey = string.gsub(category, "|[^%]]*%]%]", "]]")
		local categoryLink = string.gsub(categoryWithoutSortKey, "Category", ":Category")
		if not utilsTable.keyOf(categories, categoryLink) then
			table.insert(categories, categoryLink)
		end
	end
	return string.gsub(wikitext, categoryPattern, ""), categories
end

p.Schemas = {
	link = {
		page = {
			type = "string",
			required = true,
			desc = "The link target. Can be the name of a page on the wiki or an external URL.",
		},
		display = {
			type = "string",
			default = "page",
			desc = "The text to display for the link.",
		},
		noBacklink = {
			type = "boolean",
			desc = "When <code>true</code>, renders an internal link such that it does not register on the [[Special:WhatLinksHere]] of the target page. " 
				 .. "It also does not register on [[Special:WantedPages]] if the page doesn't exist."
		},
	},
	sectionLink = {
		page = {
			type = "string",
			required = true,
			desc = "The name of a wiki page to target.",
		},
		section = {
			type = "string",
			desc = "The name of the page section to target.",
		},
		display = {
			type = "string",
			default = "page",
			desc = "The text to display for the link.",
		},
	},
	file = {
		name = {
			type = "string",
			required = true,
			desc = "A file name, with or without the <code>File:</code> prefix.",
		},
		options = {
			type = "record",
			properties = {
				{
					name = "size",
					type = "string",
					desc = "Image size in pixels.",
				},
				{
					name = "link",
					type = "string",
					desc = "Name of a page on the wiki or an external URL for the image thumbnail to link to.",
				},
				{
					name = "caption",
					type = "string",
					desc = "[https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img Alt text] for the image.",
				},
				{
					name = "noBacklink",
					type = "boolean",
					desc = "When <code>true</code>, the <code>link</code> property is rendered such that it does not register on the [[Special:WhatLinksHere]] of the target page. " 
						 .. "It also does not register on [[Special:WantedPages]] if the page doesn't exist.",
				}
			}
		},
	},
	category = {
		name = {
			type = "string",
			required = true,
			desc = "The name of the category to link to (with or without namespace prefix).",
		},
		sortKey = {
			type = "string",
			desc = "A custom {{mediawiki|Help:Categories#Sort_key|sort key}} for the category entry.",
		}
	},
	categories = {
		names = {
			type = "array",
			required = true,
			items = { type = "string" },
			desc = "An array of category names, with or without the namespace prefix.",
		}
	},
	killBacklinks = {
		wikitext = {
			type = "string",
			required = true,
			desc = "A string of wiki markup.",
		},
	},
	stripCategories = {
		wikitext = {
			type = "string",
			required = true,
			desc = "A string of wiki markup.",
		},
	}
}

p.Documentation = {
	link = {
		params = {"page", "display", "noBacklink"},
		returns = "A string of wikitext that evaluates to a link. Will display a red link if the target is an internal link to a page that doesn't exist.",
		cases = {
			{
				desc = "Internal link",
				args = { "Princess Zelda" },
				expect = "[[Princess Zelda]]",
			},
			{
				desc = "Link to page that doesn't exist",
				args = {"Flippityfloppityfloo"},
				expect = "[[Flippityfloppityfloo]]"
			},
			{
				desc = "Piped link",
				args = { "Princess Zelda", "Zelda" },
				expect = "[[Princess Zelda|Zelda]]",
			},
			{
				desc = "Category link",
				args = { "Category:Items" },
				expect = "[[:Category:Items]]",
			},
			{
				desc = "Category link without prefix",
				args = { "Category:Items", "" },
				expect = "[[:Category:Items|Items]]",
			},
			{
				desc = "Category link with display text",
				args = { "Category:Items in Breath of the Wild", "Items in ''Breath of the Wild''" },
				expect = "[[:Category:Items in Breath of the Wild|Items in ''Breath of the Wild'']]",
			},
			{
				desc = "File link (to file description)",
				args = { "File:MM Deku Butler Artwork.png" },
				expect = "[[:File:MM Deku Butler Artwork.png]]",
			},
			{
				desc = "External link",
				args = { "https://www.mariowiki.com/Thwomp", "Thwomp" },
				expect = "[https://www.mariowiki.com/Thwomp Thwomp]",
			},
			{
				desc = "Looks like an internal link, but it's actually an external link. This is to avoid [[Special:WhatLinksHere]]",
				args = { "Princess Zelda", "Zelda", true },
				expect = [[<span class="plainlinks">[https://zelda.gamepedia.com/Princess_Zelda Zelda]</span>]],
			},
			{
				desc = "Looks like an internal red link, but it's actually an external link. This is to avoid [[Special:WantedPages]]",
				args = { "Flippityfloppityfloo", "foobar", true },
				expect = [[<span class="plainlinks new">[https://zelda.gamepedia.com/Flippityfloppityfloo?action=edit&redlink=1 foobar]</span>]],
			},
		},
	},
	sectionLink = {
		params = {"page", "section", "display"},
		returns = "A string of wikitext rendering a section link.",
		cases = {
			{
				args = {"Link", "Ocarina of Time", "Hero of Time"},
				expect = "[[Link#Ocarina of Time|Hero of Time]]",
			},
			{
				args = {"Link", nil, "Hero of Time"},
				expect = "[[Link|Hero of Time]]",
			},
			{
				args = {"Link", ""},
				expect = "[[Link#]]",
			},
		},
	},
	file = {
		params = {"name", "options"},
		returns = "A <code>string</code> of wikitext that renders a thumbnail for the file.",
		cases = {
			{
				args = { "File:ALttP Ganon Sprite.png" },
				expect = "[[File:ALttP Ganon Sprite.png|File:ALttP Ganon Sprite.png]]",
			},
			{
				args = { "ALttP Ganon Sprite.png", {
					link = "",
					caption = "Ganon"
				}},
				expect = "[[File:ALttP Ganon Sprite.png|link=|Ganon]]",
			},
			{
				args = { "File:TWW Great Fairy Figurine Model.png", {
					link = "Great Fairy",
					size = "100px",
				}},
				expect = "[[File:TWW Great Fairy Figurine Model.png|100px|link=Great Fairy|File:TWW Great Fairy Figurine Model.png]]",
			},
			{
				args = { "File:TWW Great Fairy Figurine Model.png", {
					link = "Great Fairy",
					size = "100px",
					noBacklink = true,
				}},
				expect = "[[File:TWW Great Fairy Figurine Model.png|100px|link=https://zelda.gamepedia.com/Great_Fairy|File:TWW Great Fairy Figurine Model.png]]",
			},
		},
	},
	category = {
		params = {"name", "sortKey"},
		returns = "A category link—the kind that adds a category to a page. For the other kind that links to the actual category page, use <code>link</code>.",
		cases = {
			outputOnly = true,
			{
				desc = "Category link from category name",
				args = { "Items" },
				expect = "[[Category:Items]]"
			},
			{
				desc = "Category link from category name with namespace prefix",
				args = { "Category:Items" },
				expect = "[[Category:Items]]",
			},
			{
				desc = "Category link with sort key",
				args = { "Items", "*" },
				expect = "[[Category:Items|*]]",
			},
		},
	},
	categories = {
		params = {"names"},
		returns = "A concatenated string of category links.",
		cases = {
			outputOnly = true,
			{
				args = { {"Link", "Category:Zelda", "Category:Ganon"} },
				expect = "[[Category:Link]][[Category:Zelda]][[Category:Ganon]]"
			},
			{
				args = { {} },
				expect = ""
			},
		},
	},
	containsLink = {
		params = {"wikitext"},
		returns = "Given a string of wikitext, returns <code>true</code> if it contains a link, false otherwise.",
		cases = {
			{
				args = {"There is a [[Link]]"},
				expect = true,
			},
			{
				args = {"[Link] is not a link but rather an idication of replaced words in a quote."},
				expect = false,
			},
		},
	},
	killBacklinks = {
		params = {"wikitext"},
		returns = "The same wikitext but with all the internal links formatted as external links, to avoid additions to [[Special:WhatLinksHere]].",
		cases = {
			{
				desc = "Escapes both normal links and red links",
				args = { "[[Link]], [[Princess Zelda|Zelda]], and [[Jean-Guy Rubber Boots]]" },
				expect = '<span class="plainlinks">[https://zelda.gamepedia.com/Link Link]</span>, <span class="plainlinks">[https://zelda.gamepedia.com/Princess_Zelda Zelda]</span>, and <span class="plainlinks new">[https://zelda.gamepedia.com/Jean-Guy_Rubber_Boots?action=edit&redlink=1 Jean-Guy Rubber Boots]</span>'
			},
			{
				desc = "Does not escape file links or category links",
				args = { "[[Category:Link]] and [[File:SS Blessed Butterfly Icon.png]]"},
				expect = "[[Category:Link]] and [[File:SS Blessed Butterfly Icon.png]]"
			},
			{
				desc = "Escapes visible file and category links",
				args = { "[[:Category:Link]], [[:Category:Princess Zelda|Zelda]], and [[:File:SS Blessed Butterfly Icon.png]]"},
				expect = 
					'<span class="plainlinks">[https://zelda.gamepedia.com/Category:Link Category:Link]</span>, '
					.. '<span class="plainlinks">[https://zelda.gamepedia.com/Category:Princess_Zelda Zelda]</span>, '
					.. 'and <span class="plainlinks">[https://zelda.gamepedia.com/File:SS_Blessed_Butterfly_Icon.png File:SS Blessed Butterfly Icon.png]</span>'
			},
			{
				desc = "Does not affect interwiki links",
				args = { "[[metroidwiki:Chozo|Chozo]]" },
				expect = "[[metroidwiki:Chozo|Chozo]]"
			}
		},
	},
	stripCategories = {
		params = {"wikitext"},
		returns = {
			 "The string of wiki markup with all the category links removed (not including visual links to category description pages).",
			 "A <code>table</code> array of direct links to the removed categories.",
		},
		cases = {
			outputOnly = true,
			{
				desc = "Basic use case",
				args = {"<[[Category:Link]][[Category:Princess Zelda]][[Category:Link]]>"},
				expect = {
					"<>",
					{"[[:Category:Link]]", "[[:Category:Princess Zelda]]"},
				},
			},
			{
				desc = "Does not affect visual links to category description pages",
				args = {"[[:Category:Link]] and [[:Category:Princess Zelda]]"},
				expect = {
					"[[:Category:Link]] and [[:Category:Princess Zelda]]",
					{},
				}
			}
		}
	}
}

return p