Module:DYK queue formatting check
Appearance
| This module is rated as beta. It is considered ready for widespread use, but as it is still relatively new, it should be applied with some caution to ensure results are as expected. |
| This module depends on the following other modules: |
This module automatically checks for stylistic and formatting errors unique to DYK.
Usage
{{Did you know/Clear/header}}- Transcluded by pages like Template:Did you know/Queue/1 and Template:Did you know/Preparation area 1
{{#invoke:DYK queue formatting check|main}}- Manually for sandboxes
-- This module started out verifying punctuation rules unique to [[WP:DYKMOS]]
-- Then it expanded to include the remaining non-subjective part of
-- [[WP:DYKMOS]] and [[MOS:CQ]] to keep formatting errors from [[WP:MP/E]]
-- If there's a false positive with a hook and the warning is too annoying,
-- the current method is to just disableAll.
-- This module doesn't show up on the main page or prevent promotion,
-- but if a problem arises:
local disableAll = false
-- This module is not [[WP:EXPENSIVE]], but if performance becomes a problem:
local disableTransclusionBasedChecks = false
-- People don't seem to care about [[MOS:SOB]]. [[Special:Diff/1282220073]]
-- could be edited but [[Special:Diff/1281992817]] might be intentional
local disableSeaOfBlueCheck = true
local p = {}
local plainText = require("Module:Plain text")._main
local isDisambiguation = require('Module:Disambiguation').isDisambiguation
local getTargetFromText = require('Module:Redirect').getTargetFromText
local decode = require('Module:DecodeEncode')._decode
-- Checks that look at the destination page of a wikilink
local function check_link(page)
if disableTransclusionBasedChecks then
return
end
local title = mw.title.new(page)
local content = title.content
if not content then
return "[[WP:DYKMOS]]: The hook must not contain redlinks: " .. page
end
local target = getTargetFromText(content)
if target then
return "[[WP:DYKMOS]]/[[WP:MPNOREDIRECT]]: The hook must not " ..
"contain redirects: [[" .. page .. "]] → [[" .. target .. "]]"
end
if isDisambiguation(content) then
return "[[WP:DYKMOS]]: The hook must not contain links to " ..
"disambiguation pages: " .. page
end
end
-- Per-hook checks that may have occasional exceptions
local function check_hook(wikitextLine, expandedLine, decodedLine, hookNum)
if ((expandedLine:find("{{") and not wikitextLine:find("{{%("))
or (expandedLine:find("}}") and not wikitextLine:find("{{%)"))
or (expandedLine:find("%[%[") and not wikitextLine:find("{{!"))
or (expandedLine:find("%]%]") and not wikitextLine:find("!}}"))) then
-- "code" is not ignored because some hooks use the word "codename"
-- Use templates or HTML alternatives from
-- [[Template:Escape template list]] if there's actually code in a hook.
return "Potentially broken wikitext"
end
local byteCount = -1 -- Counting ends at the question mark
if wikitextLine:find("''%([^)]+%)''") then
-- The eleven characters in a ''(pictured)'' tag do not count
byteCount = byteCount - 11
end
local boldLinkFound = false
for wikilink in wikitextLine:gmatch("'''%[%[([^]]+)%]%]") do
-- Text in boldlinks after the first do not count toward the limit
if boldLinkFound then
wikilink = wikilink:gsub("[^|]+|", "", 1)
byteCount = byteCount - #wikilink
end
boldLinkFound = true
end
byteCount = byteCount + #decodedLine
if byteCount > 200 then
return "[[WP:DYK200]]: The hook cannot exceed 200 prose characters. " ..
"It is currently " .. byteCount .. " characters"
elseif byteCount < 1 then
if hookNum == 1 then
return 'Empty first hooks are usually written as "... that ... ' ..
"''(pictured)'' ...\""
else
return 'Empty hooks are usually written as "... that ..."'
end
end
if hookNum > 1 then
if wikitextLine:find("''%([^)]+%)''") then
return "[[WP:DYKIMG]]/[[WP:DYKMOS]]: Extra ''(pictured)'' or " ..
"alternative"
end
elseif hookNum < 0 then
-- First hook might have been skipped by opt-out
else
if not decodedLine:find("%([^)]+%)") then
return "[[WP:DYKIMG]]/[[WP:DYKMOS]]: Missing ''(pictured)'' or " ..
"alternative"
end
-- Parentheses that are not the media marker are
-- allowed if "absolutely unavoidable"
end
if not disableSeaOfBlueCheck then
if (string.find(wikitextLine, "%]%] *%[%[")
or string.find(wikitextLine, "%]%] *'''+ *'''+ *%[%[")) then
return "[[WP:DYKMOS]]/[[MOS:SEAOFBLUE]]: Two non-boldlinks or " ..
"two boldlinks must be kept separate"
end
end
if decodedLine:byte(-1) ~= 46 then -- Not an empty hook in prep area
if not boldLinkFound then
return "[[WP:DYKMOS]]: Every eligible article in the hook should be " ..
"linked and wrapped in bold markup"
end
if decodedLine:byte(-1) ~= 63 then
return "[[WP:DYKMOS]]: A hook should end in a question mark"
end
end
if mw.ustring.find(decodedLine, ("[“”]")) then
return '[[MOS:CURLY]]: Use "straight" quotation marks, not “curly” ones'
end
if mw.ustring.find(decodedLine, "[‘’]") then
return "[[MOS:APOSTROPHE]]: Use straight apostrophes ('), " ..
"not curly apostrophes (‘ or ’)"
end
-- Only humans can check if {{lang}} and {{transl}} are needed i.e. if
-- the "non-English and transliterated text" is in "common English usage"
-- [[WP:DYKMOS]] says that only ''most'' hooks begin with "that"
-- [[MOS:CONTRACTION]] doesn't apply to quotes
end
-- General checks that should be uncontroversial for the entire page
local function general_checks(content)
if content:find("[^[]%[//") or content:find("https?://") then
-- Assuming nobody inserts a [[mw:Help:Links|mailto]] link
return "[[WP:DYKMOS]]: The hook must not contain external links"
end
if content:find("|''[^]]+''%]") then
return "[[WP:DYKMOS]]: Markup should go on the outside of the link " ..
"if possible"
end
if content:find("%(''[^)]+''%)") then
return "[[WP:DYKMOS]]: Note that the italics sit outside the "
.. "parentheses"
end
if content:find("%(disambiguation%)") then
return "[[WP:DYKMOS]]: The hook must not contain links to " ..
"disambiguation pages"
end
local delinkedContent = content:gsub("%]", "")
if (delinkedContent:find("[^']''%?")
or delinkedContent:find("''''%?")
or delinkedContent:find("[ ;?]%?")) then
return "[[WP:DYKMOS]]: There should not be a space before the " ..
"question mark, but if the text directly preceding it is " ..
"italicized, the {{-?}} tag can offset it."
end
if delinkedContent:find("''s ") then
return "[[WP:DYKMOS]]: Keep the bold / italic (or bold italic) text and " ..
"the apostrophe distinct using {{`s}}/{{'s}} respectively"
end
if (delinkedContent:find("''''{{`") -- Use {{'}} with bold italic
or delinkedContent:find("[^']''{{`") -- Use {{'}} with italic
or delinkedContent:find("[^']'''{{'")) then -- Use {{`}} with bold
-- Adapted from [[Template:Quotation mark templates]]
return "[[WP:DYKMOS]]: {{`}} (or {{`s}}) is for adjacent bold " ..
"markup; {{'}} (or {{'s}}) is for adjacent italic (or bold italic)"
end
if (delinkedContent:find("[^'] *{{'s}}")) then
return "[[WP:DYKMOS]]: {{'s}} is only for italics"
end
if content:find("\n%*%.%.%.") then
return "[[WP:DYKMOS]]: The three dots should be preceded by a space"
end
if content:find("\n%* %.%.%.that") then
return "[[WP:DYKMOS]]: The three dots should be followed by a space"
end
end
-- Checks to use for queues and complete prep areas
local function check_queue(content)
if content:find("example%-serious%.jpg") then
return "Image is still \"example-serious.jpg\""
end
if content:find("Caption goes here") then
return "Caption is still \"Caption goes here\""
end
for article in content:gmatch("\n%* *%{%{DYK[^\n|]+|([^\n|]+)|") do
article = article:lower()
if article == "example" or article == "articlename" then
return "[[WP:DYKPBI]]: DYKmake or DYKnom article is still " ..
'"Example"/"ArticleName"'
end
end
end
-- Run all checks
-- Useful for the debug console:
-- =p._check("\n* ... that '''''[[Main Page]]''''' test ''(pictured)'' test?\n")
function p._check(content)
local generalProblem = general_checks(content)
if generalProblem then
return generalProblem
end
local hookNum = 1
local frame = mw.getCurrentFrame()
-- Counting starts from after the space following the three dots
for wikitextLine in content:gmatch("\n%* *%.%.%. *([^\n]*)") do
if false then -- "Skip one hook" feature was removed as under discussion
-- Disable missing ''(pictured)'' check
-- as first hook might have been skipped
hookNum = -1
else
local expandedLine = frame:preprocess(wikitextLine)
expandedLine = plainText(expandedLine, false)
local decodedLine = decode(expandedLine)
local hookProblem = check_hook(wikitextLine, expandedLine,
decodedLine, hookNum)
if hookProblem then
return hookProblem .. ": " .. decodedLine
end
for page in wikitextLine:gmatch("%[%[([^]|]+)|?[^]]*%]%]") do
local linkProblem = check_link(page)
if linkProblem then
return linkProblem
end
end
if hookNum > 0 then
hookNum = hookNum + 1
end
end
end
-- No empty hooks ("... that ...") = queue or completed prep area
if not content:find("\n%* %.%.%. that[^\n]*%.%.%.\n") then
local queueProblem = check_queue(content)
if queueProblem then
return queueProblem
end
end
end
function p.main(frame)
if disableAll then
return
end
local message = p._check(mw.title.getCurrentTitle().content)
if message then
return frame:expandTemplate{title = "Ombox", args = {type = "style",
text = '<p style="color:var(--color-error,#bf3c2c);font-weight:' ..
'bold">' .. message .. "</p>" .. frame:expandTemplate{
title = "Navbar",
args = {
"Module:DYK queue formatting check",
text = "This warning module:"
}
}
}}
end
end
return p