Anonymous

Module:Navbox: Difference between revisions

From Zelda Wiki, the Zelda encyclopedia
no edit summary
No edit summary
No edit summary
 
(46 intermediate revisions by 2 users not shown)
Line 1: Line 1:
local p = {}
local p = {}
local h = {}


local utilsArg = require("Module:UtilsArg")
local utilsArg = require("Module:UtilsArg")
local utilsPage = require("Module:UtilsPage")


local CATEGORY_INVALID_ARGS = "[[Category:"..require("Module:Constants/category/invalidArgs").."]]"
local CATEGORY_INVALID_ARGS = "[[Category:"..require("Module:Constants/category/invalidArgs").."]]"
local CATEGORY_NAVBOXES_ATTENTION = "Navigation templates needing attention"
local CATEGORY_NAVBOXES_BIG_GROUPS = "[[Category:Navboxes with big groups]]"
local CATEGORY_NAVBOXES_OTHER = "[[Category:Navboxes with other]]"
local CATEGORY_NAVBOX_TEMPLATES = "Navbox templates"
local TITLE_IMG_SIZE = "28x28px"
local DEFAULT_IMG_SIZE = "150x150px"
local DEFAULT_IMG_SIZE = "150x150px"
local MAX_RECOMMENDED_PARTITION_SIZE = require("Module:Constants/number/maxNavboxPartitionSize")


function p.Main(frame)
function p.Main(frame)
local args, err = utilsArg.parse(frame:getParent().args, p.Templates.Navbox)
local args, err = utilsArg.parse(frame:getParent().args, p.Templates.Navbox)
local categories = err and err.categoryText or ""
local errCategories = err and err.categoryText or ""
if not args.title then
if not args.title then
return "", categories
if h.isManualNavboxPage() then
return "", errCategories.."[[Category:"..CATEGORY_NAVBOX_TEMPLATES.."]]"
elseif h.isTemplate() then
return "", errCategories
else
return ""
end
end
end
h.storeArgs(args)
-- [[MediaWiki:Gadget-Site.js]] automatically removes the "mw-collapsed" when there is only one navbox on the page.
local navbox, categories = h.printNavbox(args)
local navboxContent = mw.html.create("div")
categories = categories..errCategories
-- MediaWiki (Timeless?) automatically removes elements with class "navbox" on mobile
 
-- we _want_ to show navboxes on mobile since we've made them mobile friendly, so we use a different class name
if h.isManualNavboxPage() then
return navbox, categories.."[[Category:"..CATEGORY_NAVBOX_TEMPLATES.."]]", h.printReport()
elseif h.isNavboxPage() then
-- apply the styles that would be applied when this template is actually used
navbox = tostring(mw.html.create("div")
:addClass("zw-categories__navboxes-category-list")
:wikitext(navbox)
:done()
)
local styles = frame:extensionTag({
name = "templatestyles",
args = { src = "Template:Categories/Styles.css" }
})
navbox = styles..navbox
return navbox, categories, h.printReport()
elseif h.isTemplate() then
return navbox, categories
else
return navbox
end
end
 
function h.printNavbox(args)
local categories = ""
-- [[MediaWiki:Gadget-Navbox.js]] automatically removes the "mw-collapsed" when there is only one navbox on the page.
local navbox = mw.html.create("div")
:addClass("zw-navbox mw-collapsible mw-collapsed")
:addClass("zw-navbox mw-collapsible mw-collapsed")
local id = args.id or args.title
id = id and "navbox-"..string.gsub(string.lower(id), " ", "-") -- to kebab case which is the standard for IDs
if id then
navbox:attr("id", id)
end
local titleImages = args.titleImages or {}
local navboxTitle = mw.html.create("div")
:addClass("zw-navbox__title")
if #titleImages > 0 then
local leftImage = string.format("[[%s|%s|link=]]", titleImages[1], TITLE_IMG_SIZE)
navboxTitle:tag("span")
:addClass("zw-navbox__title-image")
:wikitext(leftImage)
end
navboxTitle:tag("span")
:addClass("zw-navbox__title-text")
:wikitext(args.title)
if #titleImages > 0 then
local rightImage = string.format("[[%s|%s|link=]]", titleImages[2] or titleImages[1], TITLE_IMG_SIZE)
navboxTitle:tag("span")
:addClass("zw-navbox__title-image")
:wikitext(rightImage)
end
local navboxContent = navbox
:tag("div")
:tag("div")
:addClass("zw-navbox__header mw-collapsible-toggle")
:addClass("zw-navbox__header mw-collapsible-toggle")
Line 27: Line 95:
:tag("span")
:tag("span")
:addClass("zw-navbox__title")
:addClass("zw-navbox__title")
:wikitext(args.title)
:node(navboxTitle)
:done()
:done()
:tag("span")
:tag("span")
Line 46: Line 114:
:addClass("zw-navbox__rows")
:addClass("zw-navbox__rows")
for i, row in ipairs(args.rows) do
for i, row in ipairs(args.rows) do
if row.group == "Other" or row.group == "Miscellaneous" then
categories = categories..CATEGORY_NAVBOXES_OTHER
end
h.storeGroupSize({
name = row.group or "Row "..i,
size = #(row.links or {}),
maxGroupSize = row.maxGroupSize or MAX_RECOMMENDED_PARTITION_SIZE
})
if row.group then
if row.group then
rows:tag("div")
rows:tag("div")
:addClass("zw-navbox__row-header")
:addClass("zw-navbox__row-header")
:wikitext(row.group)
:tag("span")
:addClass("zw-navbox__row-header-text")
:wikitext(row.group)
:done()
:done()
:done()
elseif i ~= 1 or #args.rows > 1 then
local utilsError = require("Module:UtilsError")
utilsError.warn(string.format("<code>group%d</code> parameter is required when there is more than one group.", i))
categories = categories..CATEGORY_INVALID_ARGS
end
end
local links = {}
local links = {}
for j, link in ipairs(row.links or {}) do
for j, link in ipairs(row.links or {}) do
link = h.link(link)
table.insert(links, '<span class="zw-navbox__link">'..link..'</span>')
table.insert(links, '<span class="zw-navbox__link">'..link..'</span>')
end
end


local evenOdd = (i % 2 == 0) and "even" or "odd"
local evenOdd = (i % 2 == 0) and "even" or "odd"
local rowModifiers = " zw-navbox__row-links--"..evenOdd
if #args.rows == 1 and not args.rows[1].group then
rowModifiers = rowModifiers.." zw-navbox__row-links--nogroups"
end
local links = table.concat(links, "&nbsp;• ")
local links = table.concat(links, "&nbsp;• ")
rows:tag("div")
rows:tag("div")
:addClass("zw-navbox__row-links"..rowModifiers)
:addClass("zw-navbox__row-links zw-navbox__row-links--"..evenOdd)
:addClass(not row.group and "zw-navbox__row-links--nogroups" or nil)
:tag("div")
:tag("div")
:addClass("zw-navbox__row-links-content")
:addClass("zw-navbox__row-links-content")
:addClass(not row.group and "zw-navbox__row-links-content--nogroups" or nil)
:wikitext(links)
:wikitext(links)
:done()
:done()
Line 97: Line 171:
return result, categories
return result, categories
end
end
-- Turns links like [[Link]] into <span class="plainlinks">[https://zeldawiki.wiki/wiki/Link Link]</span>
-- Prevents navbox entries from appearing in the Special:WhatLinksHere of every other navbox entry
-- Copied from Module:UtilsMarkup/Link for job queue optimization, plus some modifications
function h.link(links)
return string.gsub(links, "%[%[[^%]]+%]%]", h._link)
end
function h._link(link)
if string.find(link, "^%[%[Category:") or string.find(link, "^%[%[File:") then
return link
end
local linkParts = string.gsub(link, "^%[%[:?", "")
linkParts = string.gsub(linkParts, "%]%]$", "")
local pipe = string.find(linkParts, "|")
local page = string.sub(linkParts, 1, pipe and pipe - 1)
page = string.gsub(page, "&#44;", ",") -- unescape any commas that were escaped in input due to splitting by ,
page = string.gsub(page, "#.*", "")
page = string.gsub(page, "%s*$", "") -- trim trailing whitespace
h.storePage(page)
local display = pipe and string.sub(linkParts, pipe + 1) or page
if page == mw.title.getCurrentTitle().fullText then
return "<b>"..display.."</b>"
else
local url = mw.site.server.."/wiki/"..mw.uri.encode(page, "WIKI")
return string.format('<span class="plainlinks">[%s %s]</span>', url, display)
end
end
-- Store data for the report function below
local VAR_ARGS = "navbox_args"
local VAR_PAGES = "navbox_pages"
local VAR_GROUP_SIZES = "navbox_group_size"
function h.storePage(page)
if h.isNavboxPage() then
local utilsVar = require("Module:UtilsVar")
utilsVar.add(VAR_PAGES, page)
end
end
function h.storeGroupSize(group)
if h.isNavboxPage() then
local utilsVar = require("Module:UtilsVar")
utilsVar.add(VAR_GROUP_SIZES, group)
end
end
function h.storeArgs(args)
if h.isNavboxPage() then
local utilsVar = require("Module:UtilsVar")
utilsVar.set(VAR_ARGS, args)
end
end
-- Ensures that every page linked in the navbox uses that navbox
-- and that every page that uses the navbox is linked in the navbox
-- so that the navigation is bidirectional and doesn't have "dead ends"
function h.printReport()
local utilsError = require("Module:UtilsError")
local utilsLayout = require("Module:UtilsLayout")
local utilsMarkup = require("Module:UtilsMarkup")
local utilsTable = require("Module:UtilsTable")
local utilsVar = require("Module:UtilsVar")
local args = utilsVar.get(VAR_ARGS) or {}
local pagesInNav = utilsVar.get(VAR_PAGES) or {}
local groupSizes = utilsVar.get(VAR_GROUP_SIZES) or {}
local pagesUsingNav = utilsPage.dpl({
uses = "Template:"..h.templatePage(),
notnamespace = {"User", "Category"},
nottitlematch = {
"%/Documentation",
mw.title.getCurrentTitle().prefixedText ~= "Template:Guidelines Nav" and "Categories" or nil  -- To avoid counting examples at [[Template:Categories]] as uses (but we do want to count [[Template:Guidelines Nav]])
},
})
local missingLinks = utilsTable.difference(pagesUsingNav, pagesInNav)
local missingUses = utilsTable.difference(pagesInNav, pagesUsingNav)
local redirects = h.reportRedirects(pagesInNav)
missingLinks = utilsTable.difference(missingLinks, redirects)
missingUses = utilsTable.difference(missingUses, redirects)
local wantedPages = utilsTable.filter(pagesInNav, function(page)
return not utilsPage.exists(page)
end)
local bigGroups = utilsTable.filter(groupSizes, function(group)
return group.size > group.maxGroupSize
end)
local categoryName, missingPages, extraPages = h.reportCategoryMismatches(pagesInNav)
local issues = ""
if categoryName and args.title and categoryName ~= args.title then
issues = issues
.."\n====Title Mismatch====\n"
..string.format("<p>The navbox title should be <code>%s</code> rather than <code>%s</code>.</p>", categoryName, args.title)
.."<p>For navboxes added by [[Template:Categories]], the navbox title must match the category name, as navboxes are listed alphabetically by category name.</p>"
end
if #wantedPages > 0 then
issues = issues.."\n====Red Links====\n"
wantedPages = utilsTable.map(wantedPages, utilsMarkup.link)
issues = issues.."The above navbox contains links to pages which do not exist. Please create these pages or remove them from the navbox:"
issues = issues..utilsMarkup.bulletList(wantedPages)
end
if #redirects > 0 then
issues = issues.."\n====Redirect Links====\n"
redirects = utilsTable.map(redirects, utilsMarkup.link)
issues = issues.."The above navbox contains links to redirects. Please create an article to replace the redirect, or update the link to refer to the redirect target."
issues = issues..utilsMarkup.bulletList(redirects)
end
if #missingLinks > 0 then
issues = issues.."\n====Missing Links====\n"
missingLinks = utilsTable.map(missingLinks, utilsMarkup.link)
issues = issues.."<p>The above navbox is missing the following links to articles that use it. Please add these articles to the navbox to ensure that the navigation does not have dead ends.</p>"
issues = issues..utilsMarkup.bulletList(missingLinks)
end
if #missingUses > 0 and h.isManualNavboxPage() then -- missing uses is impossible if it's a category-based navbox and [[Template:Category]] is used everywhere
issues = issues.."\n====Missing Uses====\n"
missingUses = utilsTable.map(missingUses, utilsMarkup.link)
issues = issues.."The above navbox is missing from the following articles. Please add <code><nowiki>{{"..h.templatePage().."}}</nowiki></code> to these articles to ensure that the navigation does not have dead ends."
issues = issues..utilsMarkup.bulletList(missingUses)
end
if #bigGroups > 0 then
issues = issues.."\n====Big Groups====\n"
local dataRows = utilsTable.map(bigGroups, function(group)
return {group.name, group.size, group.maxGroupSize}
end)
bigGroups = utilsLayout.table({
headers = {"Row", "Size", "Max Size"},
rows = dataRows,
})
issues = issues.."The following navbox rows exceed their recommended maximum size. Please subdivide these rows if possible."
issues = issues..bigGroups
issues = issues.."If you are certain that a row should not be subdivided despite its size, you can increase the <code>maxGroupSize</code> of the row. See [[Template:Categories/Navbox]] for more information."
end
if #extraPages > 0 or #missingPages > 0 then
issues = issues.."\n====Category Mismatch====\n"
if #missingPages > 0 then
missingPages = utilsTable.map(missingPages, utilsMarkup.link)
issues = issues
.."This navbox is missing entries from [[:Category:"..categoryName.."]]:"
..utilsMarkup.bulletList(missingPages)
end
if #extraPages > 0 then
extraPages = utilsTable.map(extraPages, utilsMarkup.link)
issues = issues
.."This navbox contains links to pages that are not in [[:Category:"..categoryName.."]]:"
..utilsMarkup.bulletList(extraPages)
end
issues = issues.."Usually {{Template|Categories/Navbox}} should be used instead of {{Template|Navbox}} for category-based navboxes."
end
local report = "\n==Report==\n"
if issues ~= "" then
report = "<p>{{TOC}}</p>"..report -- fixes the lack of space between the navbox and the toc beneath it
report = report
.."\n===Issues===\n"
..issues
-- Special subcategory for big groups as it is by far the most common issue but also the least urgent,
-- so it helps to keep it separate to avoid burying other more important issues
if #bigGroups > 0 then
report = report..CATEGORY_NAVBOXES_BIG_GROUPS
else
report = report.."[[Category:"..CATEGORY_NAVBOXES_ATTENTION.."]]"
end
else
report = report.."<p>[[File:TFH Green Link ok.png|32px|link=]] No issues have been detected in this navbox.</p>"
end
return mw.getCurrentFrame():preprocess(report)
end
function h.reportRedirects(pagesInNav)
local utilsTable = require("Module:UtilsTable")
return utilsTable.filter(pagesInNav, function(page)
-- mw.title is more reliable than utilsPage for checking redirects
-- but mw.title uses an expensive parser function while utilsPage does not
-- we cannot "afford" the expensive parser function when there are too many pages in the nav
if #pagesInNav > 90 then
return utilsPage.isRedirect(page)
else
return mw.title.new(page).isRedirect
end
end)
end
function h.reportCategoryMismatches(pagesInNav)
local utilsTable = require("Module:UtilsTable")
if h.isManualNavboxPage() then
return nil, {}, {}
end
local usingCategoryNavbox = utilsPage.dpl({
uses = "Template:Categories/Navbox",
skipthispage = "no",
titlematch = h.templatePage(),
})
if #usingCategoryNavbox > 0 then
return nil, {}, {}
end
local title = mw.title.getCurrentTitle()
local category = title.subpageText
if title.subpageText == "Documentation" then
category = title.basePageTitle.subpageText
end
local pagesInCategory = utilsPage.dpl({ category = category, namespace = "" })
local missingPages = utilsTable.difference(pagesInCategory, pagesInNav)
local extraPages = utilsTable.difference(pagesInNav, pagesInCategory)
return category, missingPages, extraPages
end
local title = mw.title.getCurrentTitle()
function h.isTemplate()
return title.nsText == "Template"
end
function h.isNavboxPage()
return h.isTemplate() and title.rootText ~= "Navbox" and title.baseText ~= "Categories" and title.text ~= "Categories/Navbox/Documentation" and not h.isCurrentPageInNavbox() -- to prevent false positives on template pages that use navboxes, e.g. [[Template:Term]] for {{Guidelines Nav}}}
end
function h.isCurrentPageInNavbox()
local utilsTable = require("Module:UtilsTable")
local utilsVar = require("Module:UtilsVar")
local pagesInNav = utilsVar.get(VAR_PAGES) or {}
return utilsTable.includes(pagesInNav, title.basePageTitle.fullText)
end
function h.isManualNavboxPage() -- to distinguish manually curated navboxes from automated ones
return h.isNavboxPage() and title.baseText ~= "Categories/Navbox"
end
function h.templatePage()
local templatePage = title.text
if string.find("/Documentation$", templatePage) then
templatePage = title.baseText
end
return templatePage
end


p.Templates = {
p.Templates = {
["Navbox"] = {
["Navbox"] = {
format = "block",
format = "block",
purpose = "Creates [[:Category:Navbox Templates|navbox templates]].",
purpose = "Creates [[:Category:"..CATEGORY_NAVBOX_TEMPLATES.."|navbox templates]].",
categories = {"Metatemplates"},
categories = {"Metatemplates"},
boilerplate = {
paramOrder = {"id", "title", "titleImages", "image", "group", "links", "maxGroupSize", "footer"},
separateRequiredParams = false,
},
paramOrder = {"title", "image", "group", "links", "footer"},
repeatedGroup = {
repeatedGroup = {
name = "rows",
name = "rows",
params = {"group", "links"},
params = {"group", "links", "maxGroupSize"},
counts = {2, 3, 4, 5, 6, 7},
counts = {2, 3, 4, 5, 6, 7},
},
boilerplate = {
separateRequiredParams = false,
hiddenParams = {"maxGroupSize", "type"},
},
},
params = {
params = {
id = {
type = "string",
desc = "A unique ID for the navbox. Sets the [https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id id HTML attribute] so that the navbox can be linked to. Defaults to the navbox title.",
trim = true,
nilIfEmpty = true,
},
title = {
title = {
required = true,
required = true,
Line 119: Line 436:
trim = true,
trim = true,
nilIfEmpty = true,
nilIfEmpty = true,
},
titleImages = {
type = "string",
desc = "Two file names with the <code>File:</code> prefix, separated by a comma. Renders image to the left and right of the navbox title.",
trim = true,
nilIfEmpty = true,
split = true,
},
},
image = {
image = {
type = "wiki-file-name",
type = "wiki-file-name",
desc = "A file name, with the <code>File:</code> prefix.",
desc = "A file name, with the <code>File:</code> prefix. Renders an image in the navbox body.",
trim = true,
trim = true,
nilIfEmpty = true,
nilIfEmpty = true,
Line 139: Line 463:
nilIfEmpty = true,
nilIfEmpty = true,
split = true,
split = true,
},
maxGroupSize = {
type = "number",
nilIfEmpty = true,
default = MAX_RECOMMENDED_PARTITION_SIZE,
desc = "Adds the template to [[:Category:"..CATEGORY_NAVBOXES_ATTENTION.."]] if the number of links in the group exceeds this number. In most cases this value should not be set higher than its default; navboxes with too many links per group can be difficult to read.",
},
},
footer = {
footer = {
Line 145: Line 475:
trim = true,
trim = true,
nilIfEmpty = true,
nilIfEmpty = true,
},
}
},
},
},
},