Module:Category description
Jump to navigation
Jump to search
Lua
CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules
The module Category description implements the logic for the {{Category description}} template family, a flexible and universal system for building category pages.
Usage
[edit]{{#invoke:Category description|categoryDescription}}
Parameters
[edit]See {{Category description/core}}.
Code
-- =============================================================================
-- Routines for the automatic generation of complete category description pages
-- with navigation, using Wikidata
-- =============================================================================
require("strict")
local arguments = require "Module:Arguments"
local autocat = require "Module:Autocat"
local navbox = require "Module:Navbox"
local wdLabel = require "Module:Wikidata label"
local wdStatements = require "Module:Wikidata statements"
local specialRules = require "Module:Navigation by Wikidata/special rules"
local p = {}
-- -----------------------------------------------------------------------------
-- Escape magic characters for a regular expression
-- -----------------------------------------------------------------------------
local function regExEscape(pattern)
return string.gsub(pattern, "([%(%)%.%%%+%-%*%?%[%^%$])", "%%%1")
end
-- -----------------------------------------------------------------------------
-- Convert a pattern into a Lua-style regular expression
-- -----------------------------------------------------------------------------
local function makeRegEx(pattern, variable)
-- Change underlines into spaces
local regex = string.gsub(pattern, "_", " ")
-- Escape any magic characters in the pattern
regex = regExEscape(regex)
-- Change <<...>> into regex placeholders
-- We use non-greedy matching ("..-" instead of ".*"), see for example
-- the category "Seasons in Ebenthal in Kärnten" with two "in"
regex = string.gsub(regex, "<<" .. variable .. ">>", "(..-)")
regex = string.gsub(regex, "<<[^>]+>>", "..-")
-- Add beginning and end marks
regex = "^" .. regex .. "$"
return regex
end
-- -----------------------------------------------------------------------------
-- Find out the pattern variables and their content for the current page
-- -----------------------------------------------------------------------------
local function getVariableList(pagename, pattern)
local variableList = {} -- sorted
local variableTable = {} -- unsorted but indexed by variable name
for placeholder in string.gmatch(pattern, "<<([^>]+)>>") do
local variable = {}
-- Split the placeholder at each ":" to separate modifiers
for part in string.gmatch(placeholder, "[^:]+") do
if not variable.name then
variable.name = part
elseif part == "hyphen" then
variable.hyphen = true
elseif part == "the" then
variable.the = true
end
end
-- Determine variable content in current page name and assigned Wikidata item id
variable.content = assert(
string.match(pagename, makeRegEx(pattern, placeholder)),
"“" .. pagename .. "” does not match “" .. pattern .. "”"
)
-- Remove hyphens if necessary
if variable.hyphen then
variable.content = string.gsub(variable.content, "-", " ")
end
-- Optionally remove the "the" prefix from the category name, but only
-- if the unmodified category name does not exist; this keeps names like
-- "The Bahamas" unchanged
variable.item = mw.wikibase.getEntityIdForTitle('Category:' .. variable.content)
if not variable.item and variable.the and string.find(variable.content, "the ") == 1 then
variable.content = string.sub(variable.content, 5)
variable.item = mw.wikibase.getEntityIdForTitle('Category:' .. variable.content)
end
-- Find out which wikidata entity matches the variable content
if not variable.item then
error("Page “Category:" .. variable.content .. "” not found")
end
variable.item = wdStatements.getOneItemId(variable.item, "P301") or variable.item
table.insert(variableList, variable)
variableTable[variable.name] = variable
end
return variableList, variableTable
end
-- -----------------------------------------------------------------------------
-- Fill in variable contents in a pattern, potentially keeping one placeholder
-- -----------------------------------------------------------------------------
local function fillVariables(pattern, variableTable, keep, replacement)
local result = {pattern = pattern}
for placeholder in string.gmatch(pattern, "<<([^>]+)>>") do
local variable
local modifiers = {}
-- Split the placeholder at each ":" to separate modifiers
for part in string.gmatch(placeholder, "[^:]+") do
if not variable then
variable = assert(variableTable[part],
"Unknown placeholder “" .. placeholder .. "”")
elseif part == "label" then
modifiers.label = "yes"
elseif part == "hyphen" then
modifiers.hyphen = "yes"
elseif part == "the" then
modifiers.the = "yes"
end
end
if variable.name == keep then
-- This becomes the complete argument list for Navigation_by/...
result.item = variable.item
-- Remove modifiers from placeholder
result.pattern = string.gsub(result.pattern, placeholder, replacement)
result.hyphen = modifiers.hyphen
result.the = modifiers.the
result.style = "block"
else
-- Fill in the actual value for placeholder
local value, lang
if modifiers.label then
value, lang = mw.wikibase.getLabelWithLang(variable.item)
if not value then
value = variable.item
lang = "en"
end
else
value = variable.content
lang = "en"
end
if not modifiers.label or lang == "en" then
if modifiers.hyphen then
value = value:gsub(" ", "-")
end
if modifiers.the and specialRules[variable.item] and specialRules[variable.item].the then
value = "the " .. value
end
end
result.pattern = string.gsub(result.pattern, "<<" .. placeholder .. ">>", value)
end
end
return result
end
-- -----------------------------------------------------------------------------
-- Determine navigation data for a page and a pattern
-- -----------------------------------------------------------------------------
local function getTitleAndNavigationBlocks(args, variableList, variableTable)
local frame = mw.getCurrentFrame()
local title = args.title or ""
local blocks = ""
for index, variable in ipairs(variableList) do
-- Compile title line
if args[variable.name .. ":title"] ~= "no" then
if title ~= "" then
title = title .. " - "
end
title = title .. wdLabel._getLabel(variable.item, nil, "wikipedia", "ucfirst")
end
-- Build navigation blocks
local navigationArgs = fillVariables(args.pattern, variableTable, variable.name, args[variable.name] or variable.name)
-- Level "siblings"
navigationArgs.level = "siblings"
if args[variable.name .. ":parent:pattern"] then
local parentArgs = fillVariables(args[variable.name .. ":parent:pattern"], variableTable, variable.name, variable.name)
navigationArgs["title:pattern"] = parentArgs.pattern
navigationArgs["title:hyphen"] = parentArgs.hyphen
navigationArgs["title:the"] = parentArgs.the
end
navigationArgs.redlinks = args[variable.name .. ":redlinks"]
if args[variable.name .. ":autocat"] then
navigationArgs["autocat"] = args[variable.name .. ":autocat"]
navigationArgs["autocat:parent"] = args[variable.name .. ":autocat:parent"]
if args[variable.name .. ":autocat:candidates"] then
navigationArgs["autocat:candidates"] = fillVariables(args[variable.name .. ":autocat:candidates"], variableTable).pattern
end
end
local block = frame:expandTemplate{
title = "Navigation by/" .. variable.name,
args = navigationArgs
}
blocks = blocks .. block .. "\n"
navigationArgs["title:pattern"] = nil
navigationArgs["title:hyphen"] = nil
navigationArgs["title:the"] = nil
navigationArgs["autocat"] = nil
navigationArgs["autocat:parent"] = nil
navigationArgs["autocat:candidates"] = nil
-- Level "children"
if args[variable.name .. ":children"] ~= "no" then
navigationArgs.level = "children"
if args[variable.name .. ":children:pattern"] then
local childrenArgs = fillVariables(args[variable.name .. ":children:pattern"], variableTable, variable.name, variable.name)
navigationArgs["title:pattern"] = navigationArgs.pattern
navigationArgs["title:hyphen"] = navigationArgs.hyphen
navigationArgs["title:the"] = navigationArgs.the
navigationArgs.pattern = childrenArgs.pattern
navigationArgs.hyphen = childrenArgs.hyphen
navigationArgs.the = childrenArgs.the
end
navigationArgs.redlinks = args[variable.name .. ":children:redlinks"] or args[variable.name .. ":redlinks"]
local block = frame:expandTemplate{
title = "Navigation by/" .. variable.name,
args = navigationArgs
}
blocks = blocks .. block .. "\n"
end
end
return title, blocks
end
-- -----------------------------------------------------------------------------
-- Build a box with description and navigation blocks
-- Arguments: name, title, description, remarks, pattern, pagename
-- -----------------------------------------------------------------------------
function p._categoryDescription(args)
if not args.pattern then
error("Missing “pattern” parameter")
end
local categories = ""
local boxargs = {
name = args.name,
title = args.title,
titlestyle = "font-size: 114%;",
above = args.description,
abovestyle = "font-size: 114%;",
listclass = "hlist",
below = args.remarks,
belowstyle = "font-size: 114%;"
}
if args.lang then
boxargs.lang = args.lang
boxargs.dir = mw.language.new(args.lang):getDir()
end
if args.pagename then
local variableList, variableTable = getVariableList(args.pagename, args.pattern)
if args.autocat and mw.title.getCurrentTitle().text == args.pagename then
categories = autocat.autoCat(fillVariables(args.autocat, variableTable).pattern)
end
if boxargs.above then
boxargs.above = fillVariables(boxargs.above, variableTable).pattern
end
if boxargs.below then
boxargs.below = fillVariables(boxargs.below, variableTable).pattern
end
local blocks
boxargs.title, blocks = getTitleAndNavigationBlocks(args, variableList, variableTable)
local count = 0
for line in string.gmatch(blocks, "[^\n]+") do
if string.sub(line, 1, 1) == ";" then
line = string.gsub(line, "^; *", "")
count = count + 1
boxargs["group" .. tostring(count)] = line
boxargs["list" .. tostring(count)] = ""
else
if count == 0 then
count = count + 1
boxargs["list" .. tostring(count)] = ""
end
boxargs["list" .. tostring(count)] = (
boxargs["list" .. tostring(count)] .. line .. "\n"
)
end
end
end
return categories .. navbox._navbox(boxargs)
end
-- -----------------------------------------------------------------------------
-- Function wrapper for usage with #invoke
-- -----------------------------------------------------------------------------
function p.categoryDescription(frame)
return p._categoryDescription(arguments.getArgs(frame))
end
-- =============================================================================
return p