Jump to content
Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
If you make an edit, a temporary account will be created for you.

Editing Module:Feedback

From The Petit Planet Wiki
Warning: You are not logged in. Once you make an edit, a temporary account will be created for you. Learn more. Log in or create an account to continue receiving notifications after this account expires, and to access other features.
The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
Latest revision Your text
Line 2: Line 2:


------------------------------------------------------------
------------------------------------------------------------
-- HELPERS
-- Helpers
------------------------------------------------------------
------------------------------------------------------------


local function isTruthy(value)
local function isTruthy(v)
if value == nil then
if v == nil then
return false
return false
end
end


value = mw.text.trim(tostring(value)):lower()
v = mw.text.trim(tostring(v)):lower()


return value == 'true'
return v ~= ''
or value == 'yes'
and v ~= '0'
or value == '1'
and v ~= 'false'
or value == 'resolved'
and v ~= 'no'
end
and v ~= 'off'
 
 
local function trim(value)
if value == nil then
return ''
end
 
return mw.text.trim(tostring(value))
end
end




local function formatUser(user)
local function formatUser(user)
user = trim(user)
user = mw.text.trim(tostring(user or ''))


if user == '' then
if user == '' or user:lower() == 'anonymous' then
return 'Anonymous'
return 'Anonymous'
end
end
Line 40: Line 32:
user
user
)
)
end
local function currentSubjectPage()
local title = mw.title.getCurrentTitle()
if title.subjectPageTitle then
return title.subjectPageTitle.fullText
end
return title.fullText
end
local function getBucket()
if not (mw.ext and mw.ext.bucket) then
return nil
end
return mw.ext.bucket('feedback')
end
local function sortNewestFirst(entries)
table.sort(
entries,
function(a, b)
local aDate = trim(a.createddate)
local bDate = trim(b.createddate)
return aDate > bDate
end
)
return entries
end
local function queryFeedback(page)
local bucket = getBucket()
if not bucket then
return {}
end
local query = bucket
query.select(
'page',
'user',
'status',
'feedback',
'id',
'createddate',
'link',
'image',
'resolvedby',
'resolveddate'
)
query.where(
'page',
page
)
local success, results = pcall(function()
return query.run()
end)
if not success or not results then
return {}
end
return sortNewestFirst(results)
end
end




------------------------------------------------------------
------------------------------------------------------------
-- ATTACHMENTS
-- Feedback card
------------------------------------------------------------
------------------------------------------------------------


local function renderAttachments(entry)
local function renderCard(entry, showInstruction)
local wrapper =
 
mw.html.create('div')
local resolved =
:addClass('feedback-attachments')
(entry.status == 'resolved')


local link = trim(entry.link)
local user =
entry.user
or 'Anonymous'


if link ~= '' then
local feedback =
local linkWrapper =
entry.feedback
wrapper:tag('span')
or 'No feedback provided'
:addClass('feedback-attachment-link')
linkWrapper:tag('span')
:addClass('feedback-attachment-icon')
linkWrapper:wikitext(
'[' .. link .. ' ' .. mw.text.nowiki(link) .. ']'
)
end


local image = trim(entry.image)
local page =
entry.page


if image ~= '' then
local id =
image = mw.ustring.gsub(
mw.text.trim(
image,
tostring(entry.id or '')
'^File:',
''
)
)


local title =
local resolvedBy =
mw.title.new('File:' .. image)
mw.text.trim(
 
tostring(entry.resolvedby or '')
if title then
local imageWrapper =
wrapper:tag('span')
:addClass('feedback-attachment-image')
imageWrapper:tag('span')
:addClass('feedback-attachment-icon')
imageWrapper:wikitext(
'[' ..
title:fullUrl() ..
' ' ..
mw.text.nowiki(image) ..
']'
)
end
end
 
return wrapper
end
 
 
------------------------------------------------------------
-- RESOLUTION
------------------------------------------------------------
 
local function renderResolution(parent, entry)
local resolvedBy = trim(entry.resolvedby)
local resolvedDate = trim(entry.resolveddate)
 
if resolvedBy == '' and resolvedDate == '' then
return
end
 
local resolution =
parent:tag('div')
:addClass('feedback-resolution')
 
resolution:wikitext('Resolved by ')
 
if resolvedBy ~= '' then
resolution:wikitext(
formatUser(resolvedBy)
)
)
else
resolution:wikitext('Unknown')
end


if resolvedDate ~= '' then
local resolvedDate =
resolution:wikitext(
mw.text.trim(
' on ' .. mw.text.nowiki(resolvedDate)
tostring(entry.resolveddate or '')
)
)
end
end
------------------------------------------------------------
-- RESOLVE CONTROL
------------------------------------------------------------
local function renderResolveControl(
parent,
id,
page,
compact
)
if trim(id) == '' then
return
end
local wrapper =
parent:tag('div')
:addClass('feedback-resolve')
local control =
wrapper:tag('span')
:addClass('feedback-resolve-button')
:attr(
'data-feedback-id',
id
)
:attr(
'data-feedback-page',
page or ''
)
:attr(
'role',
'button'
)
:attr(
'tabindex',
'0'
)
:attr(
'aria-label',
'Resolve feedback'
)
if compact then
control
:addClass(
'feedback-resolve-button--compact'
)
:attr(
'title',
'Resolve feedback'
)
:wikitext('✓')
else
control:wikitext('Resolve')
end
end
------------------------------------------------------------
-- FULL FEEDBACK CARD
------------------------------------------------------------
local function renderFullCard(entry)
local resolved =
trim(entry.status):lower() == 'resolved'
local id = trim(entry.id)
local page = trim(entry.page)
local feedback = trim(entry.feedback)
local user = trim(entry.user)
local createddate = trim(entry.createddate)


local root =
local root =
Line 289: Line 78:
or 'feedback-box--unresolved'
or 'feedback-box--unresolved'
)
)
if id ~= '' then
root:attr(
'data-feedback-id',
id
)
end


--------------------------------------------------------
--------------------------------------------------------
-- Main content column
-- Meta
--------------------------------------------------------
--------------------------------------------------------


local content =
local meta =
root:tag('div')
root:tag('div')
:addClass('feedback-content')
:addClass('feedback-meta')
 
if page and page ~= '' then
 
meta:tag('span')
:addClass('feedback-page')
:wikitext(
string.format(
'[[%s]]',
page
)
)
 
meta:tag('span')
:addClass('feedback-separator')
:wikitext(' • ')


--------------------------------------------------------
end
-- Main line
--------------------------------------------------------


local main =
meta:tag('span')
content:tag('div')
:addClass('feedback-user')
:addClass('feedback-main-line')
:wikitext(
formatUser(user)
)


--------------------------------------------------------
meta:tag('span')
-- Status
:addClass('feedback-separator')
--------------------------------------------------------
:wikitext(' • ')


main:tag('span')
meta:tag('span')
:addClass('feedback-status')
:addClass('feedback-status')
:addClass(
:addClass(
Line 331: Line 128:


--------------------------------------------------------
--------------------------------------------------------
-- Feedback text
-- Feedback body
--------------------------------------------------------
--------------------------------------------------------


main:tag('span')
root:tag('div')
:addClass('feedback-text')
:addClass('feedback-body')
:wikitext(
:wikitext(feedback)
feedback ~= ''
and feedback
or 'No feedback provided'
)


--------------------------------------------------------
--------------------------------------------------------
-- Metadata
-- Resolution information
--------------------------------------------------------
--------------------------------------------------------


local meta =
if resolved and resolvedBy ~= '' then
main:tag('span')
 
:addClass('feedback-inline-meta')
local resolution =
root:tag('div')
:addClass('feedback-resolution')


if user ~= '' then
resolution:wikitext(
meta:wikitext(
'Resolved by '
' · ' .. formatUser(user)
.. formatUser(resolvedBy)
)
)
end


if createddate ~= '' then
if resolvedDate ~= '' then
meta:wikitext(
 
' · ' .. mw.text.nowiki(createddate)
resolution:wikitext(
)
' on '
.. mw.text.nowiki(resolvedDate)
)
 
end
end
end


--------------------------------------------------------
--------------------------------------------------------
-- Attachments
-- Resolve instruction
--------------------------------------------------------
--------------------------------------------------------
content:node(
    renderAttachments(entry)
)


--------------------------------------------------------
if showInstruction and not resolved then
-- Resolution information
--------------------------------------------------------


renderResolution(
local instruction =
content,
root:tag('div')
entry
:addClass('feedback-instruction')
)


--------------------------------------------------------
if id ~= '' then
-- Missing ID warning
--------------------------------------------------------


if id == '' then
instruction:wikitext(
content:tag('div')
'If this feedback has been resolved, click the button below. '
:addClass('feedback-missing-id')
:wikitext(
'This feedback entry is missing an ID.'
)
)
end


--------------------------------------------------------
instruction:tag('span')
-- Resolve button
:addClass('feedback-resolve-button')
--
:attr('role', 'button')
-- IMPORTANT:
:attr('tabindex', '0')
-- This is outside .feedback-content so it
:attr('data-feedback-id', id)
-- remains on the right.
:wikitext('Resolve')
--------------------------------------------------------
 
else
 
instruction:wikitext(
'Add an ID to this feedback to enable the Resolve button.'
)


if not resolved then
end
renderResolveControl(
root,
id,
page,
false
)
end
end


Line 413: Line 197:


------------------------------------------------------------
------------------------------------------------------------
-- COMPACT CARD
-- Save feedback
------------------------------------------------------------
------------------------------------------------------------


local function renderCompactCard(entry)
local function saveFeedback(data)
return renderFullCard(entry)
end


if not (mw.ext and mw.ext.bucket) then
return false, 'Bucket extension is not available'
end


------------------------------------------------------------
local bucket =
-- SAVE FEEDBACK
mw.ext.bucket('feedback')
------------------------------------------------------------
 
local function saveFeedback(data)
local bucket = getBucket()


if not bucket then
if not bucket then
return false, 'Bucket extension is not available'
return false, 'Feedback bucket could not be opened'
end
end


local success, err = pcall(function()
local success, err =
bucket.put({
pcall(function()
page = data.page,
 
user = data.user,
bucket.put({
status = data.status,
page = data.page,
feedback = data.feedback,
user = data.user,
id = data.id,
status = data.status,
createddate = data.createddate,
feedback = data.feedback,
link = data.link,
id = data.id,
image = data.image,
resolvedby = data.resolvedby,
resolvedby = data.resolvedby,
resolveddate = data.resolveddate
resolveddate = data.resolveddate
})
})
 
end)
end)


if not success then
if not success then
Line 456: Line 237:


------------------------------------------------------------
------------------------------------------------------------
-- MAIN
-- Main
--
-- Used by Template:Feedback
------------------------------------------------------------
------------------------------------------------------------


function p.main(frame)
function p.main(frame)
local parent = frame:getParent()
 
local parent =
frame:getParent()


local args =
local args =
Line 471: Line 252:


if parent and parent.getTitle then
if parent and parent.getTitle then
local title = parent:getTitle()
 
local title =
parent:getTitle()


if title
if title
Line 477: Line 260:
and not title:find('^Template:')
and not title:find('^Template:')
then
then
pageTitle = title
pageTitle = title
end
end
end
end
Line 489: Line 274:


function p._main(args, pageTitle)
function p._main(args, pageTitle)
local feedback =
local feedback =
trim(args.feedback)
args.feedback
or 'No feedback provided'
 
local user =
args.user
or 'Anonymous'


if feedback == '' then
local id =
feedback = 'No feedback provided'
args.id
end
or ''


local user =
local resolvedBy =
trim(args.user)
args.resolvedby
or ''


if user == '' then
local resolvedDate =
user = 'Anonymous'
args.resolveddate
end
or ''


local resolved =
local resolved =
Line 512: Line 304:


local currentPage =
local currentPage =
trim(pageTitle)
pageTitle
 
or mw.title.getCurrentTitle().fullText
if currentPage == '' then
currentPage =
mw.title.getCurrentTitle().fullText
end
 
local id =
trim(args.id)
 
local createddate =
trim(args.createddate)
 
local link =
trim(args.link)
 
local image =
trim(args.image)
 
local resolvedby =
trim(args.resolvedby)
 
local resolveddate =
trim(args.resolveddate)


--------------------------------------------------------
--------------------------------------------------------
Line 548: Line 318:
feedback = feedback,
feedback = feedback,
id = id,
id = id,
createddate = createddate,
resolvedby = resolvedBy,
link = link,
resolveddate = resolvedDate
image = image,
resolvedby = resolvedby,
resolveddate = resolveddate
})
})


if not saved then
if not saved then
return
 
'<strong style="color:red;">Feedback bucket error: '
return '<strong style="color:red;">Feedback bucket error: '
.. mw.text.nowiki(
.. mw.text.nowiki(
saveError or 'Unknown error'
saveError or 'Unknown error'
)
)
.. '</strong>'
.. '</strong>'
end
end


--------------------------------------------------------
--------------------------------------------------------
-- Render
-- Render card
--------------------------------------------------------
--------------------------------------------------------


local card =
local card =
renderFullCard({
renderCard({
page = currentPage,
page = nil,
user = user,
user = user,
status = status,
status = status,
feedback = feedback,
feedback = feedback,
id = id,
id = id,
createddate = createddate,
resolvedby = resolvedBy,
link = link,
resolveddate = resolvedDate
image = image,
}, true)
resolvedby = resolvedby,
resolveddate = resolveddate
})


--------------------------------------------------------
--------------------------------------------------------
Line 586: Line 351:
--------------------------------------------------------
--------------------------------------------------------


local category
local categories = {}


if resolved then
if resolved then
category =
 
table.insert(
categories,
'[[Category:Resolved Feedback]]'
'[[Category:Resolved Feedback]]'
)
else
else
category =
 
table.insert(
categories,
'[[Category:Unresolved Feedback]]'
'[[Category:Unresolved Feedback]]'
)
end
end


if id == '' then
if mw.text.trim(
category =
tostring(id)
category ..
) == '' then
 
table.insert(
categories,
'[[Category:Feedbacks Missing ID]]'
'[[Category:Feedbacks Missing ID]]'
)
end
end


return card .. category
return card
.. table.concat(categories)
end
end




------------------------------------------------------------
------------------------------------------------------------
-- RENDER FEEDBACK LIST
-- Render feedback list
------------------------------------------------------------
------------------------------------------------------------


local function renderFeedbackList(results)
local function renderFeedbackList(results)
if not results or #results == 0 then
if not results or #results == 0 then
return '<em>No feedback entries found.</em>'
return '<em>No feedback entries found.</em>'
Line 618: Line 398:


for _, row in ipairs(results) do
for _, row in ipairs(results) do
table.insert(
table.insert(
list,
list,
renderFullCard(row)
renderCard(
row,
false
)
)
)
end
end


Line 632: Line 417:


------------------------------------------------------------
------------------------------------------------------------
-- PAGE FEEDBACK
-- View feedback
------------------------------------------------------------
------------------------------------------------------------


function p.page(frame)
function p.view(frame)
local page =
 
currentSubjectPage()
local parent =
frame:getParent()
 
local args
 
if frame
and frame.args
and frame.args.status
then
 
args =
frame.args
 
elseif parent
and parent.args
and parent.args.status
then


local entries =
args =
queryFeedback(page)
parent.args


local unresolved = {}
elseif frame
local resolved = {}
and frame.args
then


for _, entry in ipairs(entries) do
args =
if trim(entry.status):lower()
frame.args
== 'resolved'
then
table.insert(
resolved,
entry
)
else
table.insert(
unresolved,
entry
)
end
end


local root =
elseif parent
mw.html.create('section')
and parent.args
:attr(
then
'id',
'feedback-section'
)
:addClass('feedback-page')


--------------------------------------------------------
args =
-- Heading
parent.args
--------------------------------------------------------


root:tag('h2')
else
:addClass('feedback-page-title')
:wikitext('Feedbacks')


--------------------------------------------------------
args = {}
-- Filters
--------------------------------------------------------


local filters =
end
root:tag('div')
:addClass('feedback-filters')


local function addFilter(
return p._view(
label,
args,
value,
frame
active
)
)
return filters:tag('button')
end
:attr(
 
'type',
 
'button'
function p._view(args, frame)
)
 
:attr(
if not (mw.ext and mw.ext.bucket) then
'data-feedback-filter',
return '<em>Bucket extension is not available.</em>'
value
)
:addClass('feedback-filter')
:addClass(
active
and 'feedback-filter--active'
or nil
)
:wikitext(label)
end
end


addFilter(
frame =
'Unresolved',
frame
'unresolved',
or mw.getCurrentFrame()
true
)


addFilter(
local rawStatus =
'Resolved',
args.status
'resolved',
or args[1]
false
)


addFilter(
local filterStatus =
'All',
rawStatus
'all',
and mw.text.trim(rawStatus):lower()
false
or nil
)


--------------------------------------------------------
--------------------------------------------------------
-- Unresolved
-- Filtered views
--------------------------------------------------------
--------------------------------------------------------


local unresolvedList =
if filterStatus == 'resolved'
root:tag('div')
or filterStatus == 'unresolved'
:addClass(
then
'feedback-page-list'
)
:addClass(
'feedback-page-list--unresolved'
)
:addClass(
'feedback-page-list--visible'
)


if #unresolved == 0 then
local query =
unresolvedList:tag('div')
mw.ext.bucket('feedback')
:addClass('feedback-empty')
:wikitext(
'No unresolved feedback.'
)
else
for _, entry in ipairs(unresolved) do
unresolvedList:wikitext(
renderCompactCard(entry)
)
end
end


--------------------------------------------------------
query.select(
-- Resolved
'page',
--------------------------------------------------------
'user',
'status',
'feedback',
'id',
'resolvedby',
'resolveddate'
)


local resolvedList =
query.where(
root:tag('div')
'status',
:addClass(
filterStatus
'feedback-page-list'
)
)
:addClass(
'feedback-page-list--resolved'
)


if #resolved == 0 then
return renderFeedbackList(
resolvedList:tag('div')
query.run()
:addClass('feedback-empty')
)
:wikitext(
'No resolved feedback.'
)
else
for _, entry in ipairs(resolved) do
resolvedList:wikitext(
renderCompactCard(entry)
)
end
end
end


Line 787: Line 524:
--------------------------------------------------------
--------------------------------------------------------


local allList =
if filterStatus == 'all' then
root:tag('div')
:addClass(
'feedback-page-list'
)
:addClass(
'feedback-page-list--all'
)


if #entries == 0 then
local query =
allList:tag('div')
mw.ext.bucket('feedback')
:addClass('feedback-empty')
:wikitext(
'No feedback.'
)
else
for _, entry in ipairs(entries) do
allList:wikitext(
renderCompactCard(entry)
)
end
end


return tostring(root)
query.select(
end
'page',
'user',
'status',
'feedback',
'id',
'resolvedby',
'resolveddate'
)


 
return renderFeedbackList(
------------------------------------------------------------
query.run()
-- COUNTS
------------------------------------------------------------
 
function p.counts(frame)
local entries =
queryFeedback(
currentSubjectPage()
)
)
end


local unresolved = 0
--------------------------------------------------------
local resolved = 0
-- Default tabbed view
--------------------------------------------------------


for _, entry in ipairs(entries) do
local qAll =
if trim(entry.status):lower()
mw.ext.bucket('feedback')
== 'resolved'
then
resolved = resolved + 1
else
unresolved = unresolved + 1
end
end


return tostring(
qAll.select(
mw.html.create('span')
'page',
:addClass('feedback-count-data')
'user',
:attr(
'status',
'data-unresolved',
'feedback',
unresolved
'id',
)
'resolvedby',
:attr(
'resolveddate'
'data-resolved',
resolved
)
)
)
end


local allEntries =
qAll.run()


------------------------------------------------------------
local allList = {}
-- TABLE / DASHBOARD
local unresolvedList = {}
------------------------------------------------------------
local resolvedList = {}


local function renderTable(entries)
if allEntries and #allEntries > 0 then
if not entries or #entries == 0 then
return '<em>No feedback entries found.</em>'
end


local table =
for _, row in ipairs(allEntries) do
mw.html.create('table')
:addClass('feedback-table')


local head =
local card =
table:tag('thead')
renderCard(
:tag('tr')
row,
false
)


head:tag('th')
table.insert(
:wikitext('Status')
allList,
card
)


head:tag('th')
if row.status == 'resolved' then
:wikitext('Page')


head:tag('th')
table.insert(
:wikitext('Feedback')
resolvedList,
card
)


head:tag('th')
else
:wikitext('User')


head:tag('th')
table.insert(
:wikitext('Date')
unresolvedList,
card
)


local body =
end
table:tag('tbody')
end
end


for _, entry in ipairs(entries) do
--------------------------------------------------------
local row =
-- All content
body:tag('tr')
--------------------------------------------------------


local resolved =
local allContent
trim(entry.status):lower()
== 'resolved'


row:tag('td')
if #allList > 0 then
:addClass(
resolved
and 'feedback-table-status--resolved'
or 'feedback-table-status--unresolved'
)
:wikitext(
resolved
and 'Resolved'
or 'Unresolved'
)


row:tag('td')
allContent =
:addClass('feedback-table-page')
table.concat(
:wikitext(
allList,
entry.page or ''
'\n'
)
)


row:tag('td')
else
:addClass('feedback-table-text')
:wikitext(
entry.feedback or ''
)


row:tag('td')
allContent =
:wikitext(
'<em>No feedback entries found.</em>'
formatUser(entry.user)
)


row:tag('td')
:wikitext(
entry.createddate or ''
)
end
end


return tostring(table)
--------------------------------------------------------
end
-- Unresolved content
--------------------------------------------------------


local unresolvedContent


function p.view(frame)
if #unresolvedList > 0 then
local parent =
frame:getParent()


local args
unresolvedContent =
 
table.concat(
if frame
unresolvedList,
and frame.args
'\n'
and frame.args.status
)
then
args = frame.args
 
elseif parent
and parent.args
and parent.args.status
then
args = parent.args
 
elseif frame
and frame.args
then
args = frame.args
 
elseif parent
and parent.args
then
args = parent.args


else
else
args = {}
end
return p._view(
args,
frame
)
end


unresolvedContent =
'<em>No unresolved feedback entries found.</em>'


function p._view(args, frame)
local bucket =
getBucket()
if not bucket then
return
'<em>Bucket extension is not available.</em>'
end
end
local rawStatus =
args.status
or args[1]
local filterStatus =
rawStatus
and trim(rawStatus):lower()
or nil


--------------------------------------------------------
--------------------------------------------------------
-- Query
-- Resolved content
--------------------------------------------------------
--------------------------------------------------------


local query =
local resolvedContent
bucket
 
if #resolvedList > 0 then


query.select(
resolvedContent =
'page',
table.concat(
'user',
resolvedList,
'status',
'\n'
'feedback',
)
'id',
'createddate',
'link',
'image',
'resolvedby',
'resolveddate'
)


if filterStatus == 'resolved'
else
or filterStatus == 'unresolved'
then
query.where(
'status',
filterStatus
)
end


local success, entries =
resolvedContent =
pcall(function()
'<em>No resolved feedback entries found.</em>'
return query.run()
end)


if not success or not entries then
return
'<em>Could not load feedback.</em>'
end
end
entries =
sortNewestFirst(entries)


--------------------------------------------------------
--------------------------------------------------------
-- Specific status
-- Tabber
--------------------------------------------------------
--------------------------------------------------------


if filterStatus == 'resolved'
local tabberBody =
or filterStatus == 'unresolved'
string.format(
then
'All=\n%s\n|-|Unresolved=\n%s\n|-|Resolved=\n%s',
return renderFeedbackList(
allContent,
entries
unresolvedContent,
resolvedContent
)
)
end


--------------------------------------------------------
return frame:callParserFunction(
-- All
'#tag',
--------------------------------------------------------
{
 
'tabber',
return renderTable(entries)
tabberBody
}
)
end
end
p.table = p.view


return p
return p
Please note that all contributions to The Petit Planet Wiki are considered to be released under the Creative Commons Attribution-ShareAlike (see Petit Planet Wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)
Preview page with this template
Below are commonly used wikitext from MediaWiki:Edittools. Click on any to insert them in the edit box.
File Pages==Summary====Licensing=={{Fairuse}}{{File|}}
Dynamic Page List¦²«»²{¦}²«»«/»«!----»
Array in DPL¹^2{}^2^2{¹}^2
Parser Functions{{#if:||}}
{{#ifeq:|||}}
{{#expr:}}
{{#switch:||#default=}}
{{#replace:||}}
{{#titleparts:}}
{{#tag:|}}
Markup{{}}|[][[]][[Category:]]#REDIRECT [[]] [[Category:Redirect Pages]]<code></code><includeonly></includeonly><noinclude></noinclude><nowiki></nowiki><!----><br>§
Notices{{Stub|}}{{Stub Dialogue}}{{Upcoming}}{{Under Construction}}{{Placeholder|}}
Magic Words{{!}}{{formatnum:}}{{lc:}}{{uc:}}{{PAGENAME}}{{FULLPAGENAME}}{{ROOTPAGENAME}}{{BASEPAGENAME}}{{SUBPAGENAME}}{{DISPLAYTITLE:|noreplace}}__TOC____NOTOC____EXPECTUNUSEDCATEGORY____HIDDENCAT__
HTML Entities&mdash;&nbsp;&comma;&shy;