Module:Protection banner
This page documents the Chalo Chatu–only protection banner module. It provides a lightweight, dependency-free implementation for protection notices and padlock indicators. It is declarative: pages call the module (usually via Template:PP-meta) and pass action, level, reason, and dates. The module does not query actual MediaWiki protection state.
Overview
- Designed for Chalo Chatu only
- No external dependencies or WMF modules
- Outputs either a full banner or a small padlock
- Adds simple, predictable protection categories
- Icons and category names configurable in Module:Protection banner/config
How it works
Most pages should not call the module directly. Use the wrapper template:
- Template:PP-meta invokes the module with your parameters
- Module:Protection banner renders the banner/padlock and adds categories
Quick start (recommended wrapper)
Place this in the article or template needing a notice:
{{pp-meta|vandalism|action=edit|level=autoconfirmed|expiry=indef|date=2025-09-29}}
Small padlock indicator:
{{pp-meta|dispute|action=edit|level=sysop|small=yes|link=Chalo Chatu:Protection policy}}
Parameters (via Template:PP-meta)
All parameters are passed through to the module.
- 1 or reason
- Free-text reason shown in the banner line. Example: vandalism, dispute, sock.
- action
- edit (default), move, upload.
- level
- sysop, templateeditor, extendedconfirmed, autoconfirmed, * (unprotected). Determines icon, wording, and category.
- expiry
- indef for indefinite; or free text such as 2026-12-31. Shown as provided.
- date
- Free text such as 2025-09-29. Shown as Protected on 2025-09-29.
- small
- yes to output a small padlock indicator instead of a full banner.
- category
- yes (default) to add a protection category; no to suppress.
- image
- Optional file name override for the padlock icon. Example: Padlock-custom.svg
- link
- Optional page to link the padlock to when small=yes. Example: Chalo Chatu:Protection policy
- pagetype
- Defaults to page. Used in phrasing (This page is semi-protected...). You may set article, template, module, etc.
Levels and default wording
The module maps levels to human-readable phrases.
- edit/sysop
- fully protected
- edit/templateeditor
- template-protected
- edit/extendedconfirmed
- extended confirmed protected
- edit/autoconfirmed
- semi-protected
- *
- unprotected
Move and upload actions use simple default wording (move-protected, upload-protected).
Categories
When category=yes and level is not *, the module adds one category based on action/level. Defaults are set in Module:Protection banner/config:
- Fully protected pages
- Template-protected pages
- Extended-confirmed protected pages
- Semi-protected pages
- Move-protected pages
- Upload-protected pages
- Protected pages (fallback)
Rename or localize categories by editing Module:Protection banner/config.
Icons
Icons are file names stored in Module:Protection banner/config. Replace with your local files as needed:
- Padlock-red.svg, Padlock-orange.svg, Padlock-amber.svg, Padlock-yellow.svg, Padlock-blue.svg, Padlock-grey.svg
You can override per-call with image=.
Examples
Full banner, indefinite semi-protection for vandalism:
{{pp-meta|vandalism|action=edit|level=autoconfirmed|expiry=indef|date=2025-09-29}}
Full banner, extended-confirmed protection with expiry:
{{pp-meta|dispute|action=edit|level=extendedconfirmed|expiry=2026-06-30}}
Small padlock on a template:
{{pp-meta|reason=high-risk template|action=edit|level=templateeditor|small=yes|link=Chalo Chatu:Template protection}}
Suppress category (maintenance only):
{{pp-meta|reason=test|action=edit|level=autoconfirmed|category=no}}
Daughter templates (optional)
You may create simple wrappers that prefill parameters, for example:
Template:PP-vandalism
<includeonly>{{pp-meta|vandalism|action={{{ action|edit}}}|level={{{level|autoconfirmed}}}|expiry={{{expiry|indef}}}|small={{{small|}}}|date={{{date|}}}}}</includeonly> <noinclude>Wrapper for vandalism protection. [[Category:Protection templates]]</noinclude>
Template:PP-dispute
<includeonly>{{pp-meta|dispute|action={{{ action|edit}}}|level={{{level|extendedconfirmed}}}|expiry={{{expiry|}}}|small={{{small|}}}|date={{{date|}}}}}</includeonly> <noinclude>Wrapper for dispute-driven protection. [[Category:Protection templates]]</noinclude>
Configuration
Site-wide configuration lives in Module:Protection banner/config:
- images: per-action, per-level icon filenames
- protectionLevels: phrases used in banner text
- categories: mapping from action/level to category names
Update that file to adjust look, wording, and categories globally.
Limitations
- Declarative only; does not read MediaWiki protection state
- Age and date strings are displayed as provided; no automatic date parsing
- Minimal CSS. For consistent styling, add optional CSS in MediaWiki:Common.css for .cc-mbox and .cc-padlock
See also
-- Module:Protection banner (Chalo Chatu)
-- Lightweight, dependency-free. Declarative protection only.
-- Implements {{pp-meta}}-style usage via p.main().
-- Params (typical):
-- action=edit/move/upload
-- level=autoconfirmed/extendedconfirmed/templateeditor/sysop/* (unprotected)
-- reason=vandalism/dispute/sock/etc (free text)
-- expiry=indef | YYYY-MM-DD | free text (shown as-is)
-- date=YYYY-MM-DD (shown as "Protected on …")
-- small=yes -> padlock indicator; else banner
-- category=yes/no (default yes)
-- section=anchor on talk link (optional)
-- image=File name override (optional)
-- link=target page for padlock click (optional)
local p = {}
-- Basic yes/no parser
local function yesno(v, default)
if v == nil then return default end
if type(v) == 'boolean' then return v end
local s = tostring(v):lower()
if s == 'yes' or s == 'y' or s == 'true' or s == '1' then return true end
if s == 'no' or s == 'n' or s == 'false' or s == '0' then return false end
return default
end
-- HTML builder helpers (simple, safe)
local function esc(s)
s = tostring(s or '')
return s:gsub('&','&'):gsub('<','<'):gsub('>','>')
end
local function tag(name, attrs, content)
local a = {}
if attrs then
for k,v in pairs(attrs) do
if v ~= nil and v ~= '' then
table.insert(a, string.format(' %s="%s"', k, esc(v)))
end
end
end
if content == nil then
return string.format('<%s%s />', name, table.concat(a))
end
return string.format('<%s%s>%s</%s>', name, table.concat(a), content, name)
end
-- Minimal styling (relies on site Common.css, but works standalone)
local function messageBox(imageHtml, textHtml)
local left = tag('div', {style='flex:0 0 auto; padding-right:8px;'}, imageHtml or '')
local right = tag('div', {style='flex:1 1 auto;'}, textHtml or '')
local row = tag('div', {style='display:flex; align-items:flex-start;'}, left .. right)
return tag('div', {
class='cc-mbox cc-mbox-protection',
style='border:1px solid #aaa;background:#f9f9f9;padding:8px;margin:0 0 1em 0;'
}, row)
end
local function padlockIndicator(imageHtml, linkTarget, alt)
local content = imageHtml or ''
if linkTarget and linkTarget ~= '' then
content = string.format('[[%s|%s]]', linkTarget, content)
end
return tag('span', {class='cc-padlock', title=alt or 'Page is protected'}, content)
end
local function fileLink(filename, size, alt, link)
if not filename or filename == '' then return '' end
local core = string.format('[[File:%s|%s|%s]]',
filename,
size or '20px',
(alt and ('alt='..alt)) or ''
)
if link and link ~= '' then
return string.format('[[%s|%s]]', link, core)
end
return core
end
-- Default config (overrideable by Module:Protection banner/config if present)
local function getConfig()
local ok, cfg = pcall(require, 'Module:Protection banner/config')
if ok and type(cfg) == 'table' then return cfg end
-- Built-in defaults for Chalo Chatu
return {
images = {
edit = {
sysop = 'Padlock-red.svg',
templateeditor = 'Padlock-orange.svg',
extendedconfirmed = 'Padlock-amber.svg',
autoconfirmed = 'Padlock-yellow.svg',
['*'] = 'Padlock-grey.svg',
default = 'Padlock-blue.svg'
},
move = { default = 'Padlock-blue.svg' },
upload = { default = 'Padlock-blue.svg' },
default = 'Padlock-blue.svg'
},
padlockIndicatorName = 'pp',
-- Simple phrasing tables
protectionLevels = {
edit = {
sysop='fully protected', templateeditor='template-protected',
extendedconfirmed='extended confirmed protected',
autoconfirmed='semi-protected', ['*']='unprotected', default='protected'
},
move = { default='move-protected' },
upload = { default='upload-protected' }
},
categories = {
-- Built from: action/level -> category name. Fallbacks used below.
['edit/sysop'] = 'Fully protected pages',
['edit/autoconfirmed'] = 'Semi-protected pages',
['edit/extendedconfirmed'] = 'Extended-confirmed protected pages',
['edit/templateeditor'] = 'Template-protected pages',
['move/sysop'] = 'Move-protected pages',
['upload/sysop'] = 'Upload-protected pages',
default = 'Protected pages'
},
talkNsName = 'Talk'
}
end
local function pickImage(cfg, action, level, override)
if override and override ~= '' then return override end
local imgs = cfg.images
if imgs[action] then
return imgs[action][level] or imgs[action].default or imgs.default
end
return imgs.default
end
local function pickLevelName(cfg, action, level)
local t = cfg.protectionLevels[action] or {}
return t[level] or t.default or 'protected'
end
local function buildCategory(cfg, action, level)
local key = (action or 'edit') .. '/' .. (level or '*')
local cat = cfg.categories[key] or cfg.categories.default
return string.format('[[Category:%s]]', cat)
end
local function buildText(action, levelName, reason, expiry, date, pagetype)
local parts = {}
local ptype = pagetype or 'page'
table.insert(parts, string.format("This %s is %s", ptype, levelName))
if reason and reason ~= '' then
table.insert(parts, string.format(" for %s", reason))
end
table.insert(parts, '.')
if expiry and expiry ~= '' and expiry ~= 'indef' then
table.insert(parts, string.format(" Protection expires: %s.", expiry))
elseif expiry == 'indef' then
table.insert(parts, " Protection is indefinite.")
end
if date and date ~= '' then
table.insert(parts, string.format(" Protected on %s.", date))
end
return table.concat(parts)
end
-- Minimal args fetcher (works for template or module invocation)
local function getArgs(frame)
local parent = frame:getParent()
local src = parent or frame
local args = {}
for k,v in pairs(src.args) do
if v ~= '' then args[k] = v end
end
return args
end
local function render(args, cfg)
local action = (args.action or 'edit'):lower()
local level = (args.level or '*'):lower()
local reason = args[1] or args.reason
local expiry = args.expiry
local date = args.date
local small = yesno(args.small, false)
local putCat = yesno(args.category, true)
local image = pickImage(cfg, action, level, args.image)
local link = args.link
local alt = pickLevelName(cfg, action, level)
local padImg = fileLink(image, small and '20px' or '40px', alt, small and link or nil)
local out = {}
if small then
table.insert(out, padlockIndicator(padImg, link, alt))
else
local text = buildText(action, alt, reason, expiry, date, args.pagetype)
local head = tag('div', {style='font-weight:bold;margin-bottom:2px;'}, esc(alt:gsub("^%l", string.upper)))
local body = tag('div', nil, esc(text))
table.insert(out, messageBox(padImg, head .. body))
end
if putCat and level ~= '*' then
table.insert(out, buildCategory(cfg, action, level))
end
return table.concat(out)
end
function p.main(frame)
local cfg = getConfig()
local args = getArgs(frame)
return render(args, cfg)
end
-- For module testing:
function p._render(targs, tcfg) return render(targs or {}, tcfg or getConfig()) end
return p