Documentation for this module may be created at Module:UtilsMarkup/Link/doc
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") function p.anchor(id, display) return string.format('<span id="%s">[[#%s|%s]]</span>', id, id, display or id) 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 wikitext ~= nil and not not wikitext:find("%[%[.*%]%]") end function p.file(name, options) options = options or {} local size = options.size local link = options.link local caption = options.caption local class = options.class local noBacklink = options.noBacklink if not utilsString.startsWith(name, "File:") then name = "File:" .. name end if link and noBacklink then link = utilsPage.fullUrl(link) end local linkParts = utilsTable.compact({ name, size, link and "link="..link, class and "class="..class, caption, }) local fileLink = "[[" .. table.concat(linkParts, "|") .. "]]" return fileLink 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) local linkParts = utilsString.trim(link, "][") local pipe = string.find(linkParts, "|") local page = string.sub(linkParts, 1, pipe and pipe - 1) local display = pipe and string.sub(linkParts, pipe + 1) local startsWithColon = utilsString.startsWith(page, ":") page = string.gsub(page, "^:", "") local title = mw.title.new(page) if not title or title.interwiki ~= "" or not startsWithColon and (title.nsText == "Category" or title.nsText == "File") then return link end return p.link(page, display or page, true) end -- 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 if noBacklink then local url = utilsPage.fullUrl(link) local externalLink = h.externalLink(url, text or link) local formattedLink = utilsMarkup.class("plainlinks", 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.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.sectionLink(page, section, display) local target = page if section then target = target .. "#" .. section end return p.link(target, display) 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 function p.Schemas() return { anchor = { id = { type = "string", required = true, }, display = { type = "string", }, }, 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. '''Use only when certain that the page exists'''. This method cannot render red-links (i.e. distinguish non-existent pages). ]], }, }, 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 = "class", type = "string", desc = "Sets the HTML classes of the image element.", }, { 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.", }, } } end function p.Documentation() return { anchor = { params = {"id", "display"}, returns = "A link that refers to itself. Useful for linking to specific sections of a page without necessarily using headings.", cases = { { args = {"foo"}, expect = '<span id="foo">[[#foo|foo]]</span>', }, { args = {"bar", "Bar"}, expect = '<span id="bar">[[#bar|Bar]]</span>', }, { args = {"foo bar"}, expect = '<span id="foo bar">[[#foo bar|foo bar]]</span>', }, }, }, 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 = "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">[//zeldawiki.wiki/Princess_Zelda Zelda]</span>]], }, { -- Doing red links here would require checking page existence, which requires a database read and is considered an "expensive function" by MediaWiki -- Given how often this function can be called on any given page, it's probably best to avoid this desc = "When <code>noBacklink</code> is set to <code>true</code>, red links are not rendered for pages which do not exist, for performance reasons.", args = { "Flippityfloppityfloo", nil, true }, expect = [[<span class="plainlinks">[//zeldawiki.wiki/Flippityfloppityfloo Flippityfloppityfloo]</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]]", }, { 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]]", }, { 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=//zeldawiki.wiki/Great_Fairy]]", }, { args = { "File:TWW Great Fairy Figurine Model.png", { link = "Great Fairy", size = "100px", class = "notpageimage", }}, expect = "[[File:TWW Great Fairy Figurine Model.png|100px|link=Great Fairy|class=notpageimage]]", }, }, }, 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 an internal or interwiki link, false otherwise.", cases = { { args = {"There is a [[Link]]"}, expect = true, }, { args = {"[Link] is not a link but rather an indication 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 = { { -- See "link" function for peformance reasons -- It might still be possible for us to support red links here if the performance requirements aren't as stringent as the "link" function -- There doesn't seem to be a pressing need at the moment, though desc = "Escapes basic article links - unfortunately non-existent pages are not rendered as red links for performance reasons", args = { "[[Link]], [[Princess Zelda|Zelda]], and [[Jean-Guy Rubber Boots]]" }, expect = '<span class="plainlinks">[//zeldawiki.wiki/Link Link]</span>, <span class="plainlinks">[//zeldawiki.wiki/Princess_Zelda Zelda]</span>, and <span class="plainlinks">[//zeldawiki.wiki/Jean-Guy_Rubber_Boots 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">[//zeldawiki.wiki/Category:Link Category:Link]</span>, ' .. '<span class="plainlinks">[//zeldawiki.wiki/Category:Princess_Zelda Zelda]</span>, ' .. 'and <span class="plainlinks">[//zeldawiki.wiki/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]]", {}, } } } } } end return p