Jump to content

Editing Module:Feedback

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
------------------------------------------------------------
------------------------------------------------------------


Line 12: Line 12:
value = mw.text.trim(tostring(value)):lower()
value = mw.text.trim(tostring(value)):lower()


return value == 'true'
return value ~= ''
or value == 'yes'
and value ~= '0'
or value == '1'
and value ~= 'false'
or value == 'resolved'
and value ~= 'no'
and value ~= 'off'
end
end




local function trim(value)
local function trim(value)
if value == nil then
return mw.text.trim(
return ''
tostring(value or '')
end
)
 
return mw.text.trim(tostring(value))
end
end


Line 31: Line 30:
user = trim(user)
user = trim(user)


if user == '' then
if user == '' or user:lower() == 'anonymous' then
return 'Anonymous'
return 'Anonymous'
end
end
Line 44: Line 43:


local function currentSubjectPage()
local function currentSubjectPage()
local title = mw.title.getCurrentTitle()
local title =
 
mw.title.getCurrentTitle()
if title.subjectPageTitle then
return title.subjectPageTitle.fullText
end


return title.fullText
return title.subjectPageTitle.fullText
end
end


Line 56: Line 52:
local function getBucket()
local function getBucket()
if not (mw.ext and mw.ext.bucket) then
if not (mw.ext and mw.ext.bucket) then
return nil
return nil, 'Bucket extension is not available'
end
 
local bucket =
mw.ext.bucket('feedback')
 
if not bucket then
return nil, 'Feedback bucket could not be opened'
end
end


return mw.ext.bucket('feedback')
return bucket
end
end




local function sortNewestFirst(entries)
local function sortNewestFirst(results)
if not results then
return {}
end
 
table.sort(
table.sort(
entries,
results,
function(a, b)
function(a, b)
local aDate = trim(a.createddate)
local bDate = trim(b.createddate)


return aDate > bDate
local aDate =
trim(a.createddate)
 
local bDate =
trim(b.createddate)
 
-- Entries without a date go last.
if aDate == '' and bDate ~= '' then
return false
end
 
if aDate ~= '' and bDate == '' then
return true
end
 
if aDate ~= bDate then
return aDate > bDate
end
 
-- Stable-ish fallback.
return trim(a.id) > trim(b.id)
end
end
)
)


return entries
return results
end
end




local function queryFeedback(page)
local function queryFeedback(page, status)
local bucket = getBucket()
local bucket, err =
getBucket()


if not bucket then
if not bucket then
return {}
return nil, err
end
 
local query =
bucket.select(
'page',
'user',
'status',
'feedback',
'id',
'createddate',
'link',
'image',
'resolvedby',
'resolveddate'
)
 
if page and page ~= '' then
query.where(
'page',
page
)
end
 
if status
and status ~= ''
and status ~= 'all'
then
 
query.where(
'status',
status
)
 
end
end


local query = bucket
local success, results =
pcall(function()
return query.run()
end)
 
if not success then
return nil, tostring(results)
end


query.select(
return sortNewestFirst(
'page',
results or {}
'user',
'status',
'feedback',
'id',
'createddate',
'link',
'image',
'resolvedby',
'resolveddate'
)
)
end
------------------------------------------------------------
-- Save feedback
------------------------------------------------------------
local function saveFeedback(data)
local bucket, err =
getBucket()
if not bucket then
return false, err
end


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


local success, results = pcall(function()
end)
return query.run()
end)


if not success or not results then
if not success then
return {}
return false, tostring(saveError)
end
end


return sortNewestFirst(results)
return true
end
end




------------------------------------------------------------
------------------------------------------------------------
-- ATTACHMENTS
-- Shared metadata
------------------------------------------------------------
------------------------------------------------------------


local function renderAttachments(entry)
local function renderMetadata(root, entry)
local wrapper =
 
mw.html.create('div')
local meta =
root:tag('div')
:addClass('feedback-meta')
 
if entry.user and entry.user ~= '' then
 
meta:tag('span')
:addClass('feedback-user')
:wikitext(
formatUser(entry.user)
)
 
end
 
if entry.createddate
and entry.createddate ~= ''
then
 
meta:tag('span')
:addClass('feedback-meta-date')
:wikitext(
' · '
.. mw.text.nowiki(
entry.createddate
)
)
 
end
end
 
 
------------------------------------------------------------
-- Attachments
------------------------------------------------------------
 
local function renderAttachments(root, entry)
 
local link =
trim(entry.link)
 
local image =
trim(entry.image)
 
if link == ''
and image == ''
then
return
end
 
local attachments =
root:tag('div')
:addClass('feedback-attachments')
:addClass('feedback-attachments')


local link = trim(entry.link)
if link ~= '' then
 
attachments:tag('span')
:addClass('feedback-attachment')
:wikitext(
'🔗 '
.. '['
.. link
.. ' Link]'
)


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


local image = trim(entry.image)
if image ~= '' then


if image ~= '' then
attachments:tag('span')
image = mw.ustring.gsub(
:addClass('feedback-attachment')
image,
'^File:',
''
)


local title =
attachments:tag('span')
mw.title.new('File:' .. image)
:addClass('feedback-image-icon')
:wikitext('▣')


if title then
attachments:tag('span')
local imageWrapper =
:addClass('feedback-image-link')
wrapper:tag('span')
:wikitext(
:addClass('feedback-attachment-image')
'[[Media:'
.. image
imageWrapper:tag('span')
.. '|Picture]]'
:addClass('feedback-attachment-icon')
imageWrapper:wikitext(
'[' ..
title:fullUrl() ..
' ' ..
mw.text.nowiki(image) ..
']'
)
)
end
 
end
end
return wrapper
end
end




------------------------------------------------------------
------------------------------------------------------------
-- RESOLUTION
-- Resolution information
------------------------------------------------------------
------------------------------------------------------------


local function renderResolution(parent, entry)
local function renderResolution(root, entry)
local resolvedBy = trim(entry.resolvedby)
 
local resolvedDate = trim(entry.resolveddate)
local resolvedBy =
trim(entry.resolvedby)


if resolvedBy == '' and resolvedDate == '' then
local resolvedDate =
trim(entry.resolveddate)
 
if resolvedBy == ''
and resolvedDate == ''
then
return
return
end
end


local resolution =
local resolution =
parent:tag('div')
root:tag('div')
:addClass('feedback-resolution')
:addClass('feedback-resolution')


resolution:wikitext('Resolved by ')
resolution:wikitext('↳ Resolved by ')


if resolvedBy ~= '' then
if resolvedBy ~= '' then
Line 203: Line 325:
if resolvedDate ~= '' then
if resolvedDate ~= '' then
resolution:wikitext(
resolution:wikitext(
' on ' .. mw.text.nowiki(resolvedDate)
' on '
.. mw.text.nowiki(resolvedDate)
)
)
end
end
Line 210: Line 333:


------------------------------------------------------------
------------------------------------------------------------
-- RESOLVE CONTROL
-- Resolve control
------------------------------------------------------------
------------------------------------------------------------


Line 216: Line 339:
parent,
parent,
id,
id,
page,
compact
compact
)
)
if trim(id) == '' then
 
if not id or id == '' then
return
return
end
end
local wrapper =
parent:tag('div')
:addClass('feedback-resolve')


local control =
local control =
wrapper:tag('span')
parent:tag('span')
:addClass('feedback-resolve-button')
: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
if compact then
control
control
:addClass(
:addClass('feedback-resolve-button--compact')
'feedback-resolve-button--compact'
:attr('aria-label', 'Resolve feedback')
)
:attr('title', 'Resolve feedback')
:attr(
'title',
'Resolve feedback'
)
:wikitext('✓')
:wikitext('✓')
else
else
control:wikitext('Resolve')
 
control
:attr('role', 'button')
:attr('tabindex', '0')
:attr('aria-label', 'Resolve feedback')
:wikitext('Resolve')
 
end
end
control:attr(
'data-feedback-id',
id
)
end
end




------------------------------------------------------------
------------------------------------------------------------
-- FULL FEEDBACK CARD
-- Full feedback card
------------------------------------------------------------
------------------------------------------------------------


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


local id = trim(entry.id)
local id =
local page = trim(entry.page)
trim(entry.id)
local feedback = trim(entry.feedback)
local user = trim(entry.user)
local createddate = trim(entry.createddate)


local root =
local root =
Line 296: Line 402:
)
)
end
end
--------------------------------------------------------
-- Main content column
--------------------------------------------------------
local content =
root:tag('div')
:addClass('feedback-content')


--------------------------------------------------------
--------------------------------------------------------
Line 310: Line 408:


local main =
local main =
content:tag('div')
root:tag('div')
:addClass('feedback-main-line')
:addClass('feedback-main-line')
--------------------------------------------------------
-- Status
--------------------------------------------------------


main:tag('span')
main:tag('span')
Line 326: Line 420:
:wikitext(
:wikitext(
resolved
resolved
and 'Resolved'
and 'RESOLVED'
or 'Unresolved'
or 'UNRESOLVED'
)
)
--------------------------------------------------------
-- Feedback text
--------------------------------------------------------


main:tag('span')
main:tag('span')
:addClass('feedback-text')
:addClass('feedback-text')
:wikitext(
:wikitext(
feedback ~= ''
entry.feedback
and feedback
or 'No feedback provided'
or 'No feedback provided'
)
)
Line 346: Line 435:
--------------------------------------------------------
--------------------------------------------------------


local meta =
local metadata =
main:tag('span')
main:tag('span')
:addClass('feedback-inline-meta')
:addClass('feedback-inline-meta')


if user ~= '' then
if entry.user
meta:wikitext(
and entry.user ~= ''
' · ' .. formatUser(user)
then
 
metadata:wikitext(
' · '
.. formatUser(entry.user)
)
 
end
 
if entry.createddate
and entry.createddate ~= ''
then
 
metadata:wikitext(
' '
.. mw.text.nowiki(
entry.createddate
)
)
)
end
end


if createddate ~= '' then
--------------------------------------------------------
meta:wikitext(
-- Resolve button
' · ' .. mw.text.nowiki(createddate)
--------------------------------------------------------
 
if not resolved
and id ~= ''
then
 
renderResolveControl(
main,
id,
false
)
)
end
end


Line 365: Line 482:
-- Attachments
-- Attachments
--------------------------------------------------------
--------------------------------------------------------
 
content:node(
renderAttachments(
    renderAttachments(entry)
root,
entry
)
)


--------------------------------------------------------
--------------------------------------------------------
-- Resolution information
-- Resolution
--------------------------------------------------------
--------------------------------------------------------


renderResolution(
if resolved then
content,
renderResolution(
entry
root,
)
entry
)
end


--------------------------------------------------------
--------------------------------------------------------
Line 383: Line 503:
--------------------------------------------------------
--------------------------------------------------------


if id == '' then
if not resolved
content:tag('div')
and id == ''
then
 
root:tag('div')
:addClass('feedback-missing-id')
:addClass('feedback-missing-id')
:wikitext(
:wikitext(
'This feedback entry is missing an ID.'
'Add an ID to enable the Resolve button.'
)
)
end


--------------------------------------------------------
-- Resolve button
--
-- IMPORTANT:
-- This is outside .feedback-content so it
-- remains on the right.
--------------------------------------------------------
if not resolved then
renderResolveControl(
root,
id,
page,
false
)
end
end


Line 413: Line 520:


------------------------------------------------------------
------------------------------------------------------------
-- COMPACT CARD
-- Compact article card
------------------------------------------------------------
------------------------------------------------------------


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


local resolved =
entry.status == 'resolved'


------------------------------------------------------------
local id =
-- SAVE FEEDBACK
trim(entry.id)
------------------------------------------------------------


local function saveFeedback(data)
local root =
local bucket = getBucket()
mw.html.create('div')
:addClass('feedback-page-card')
:addClass(
resolved
and 'feedback-page-card--resolved'
or 'feedback-page-card--unresolved'
)


if not bucket then
if id ~= '' then
return false, 'Bucket extension is not available'
root:attr(
'data-feedback-id',
id
)
end
end


local success, err = pcall(function()
local content =
bucket.put({
root:tag('div')
page = data.page,
:addClass('feedback-page-card-content')
user = data.user,
status = data.status,
feedback = data.feedback,
id = data.id,
createddate = data.createddate,
link = data.link,
image = data.image,
resolvedby = data.resolvedby,
resolveddate = data.resolveddate
})
end)


if not success then
local firstLine =
return false, tostring(err)
content:tag('div')
end
:addClass('feedback-page-card-line')


return true
firstLine:tag('span')
end
:addClass('feedback-status')
:addClass(
resolved
and 'feedback-status--resolved'
or 'feedback-status--unresolved'
)
:wikitext(
resolved
and 'RESOLVED'
or 'UNRESOLVED'
)


firstLine:tag('span')
:addClass('feedback-page-card-text')
:wikitext(
entry.feedback
or 'No feedback provided'
)


------------------------------------------------------------
local meta =
-- MAIN
content:tag('div')
--
:addClass('feedback-page-card-meta')
-- Used by Template:Feedback
------------------------------------------------------------


function p.main(frame)
if entry.user
local parent = frame:getParent()
and entry.user ~= ''
then


local args =
meta:wikitext(
parent and parent.args
formatUser(entry.user)
or frame.args
)
 
local pageTitle = nil


if parent and parent.getTitle then
local title = parent:getTitle()
if title
and title ~= ''
and not title:find('^Template:')
then
pageTitle = title
end
end
end


return p._main(
if entry.createddate
args,
and entry.createddate ~= ''
pageTitle
then
)
end


meta:wikitext(
' · '
.. mw.text.nowiki(
entry.createddate
)
)


function p._main(args, pageTitle)
local feedback =
trim(args.feedback)
if feedback == '' then
feedback = 'No feedback provided'
end
end


local user =
if resolved then
trim(args.user)


if user == '' then
local resolvedBy =
user = 'Anonymous'
trim(entry.resolvedby)
end


local resolved =
local resolvedDate =
isTruthy(args.resolved)
trim(entry.resolveddate)


local status =
if resolvedBy ~= ''
resolved
or resolvedDate ~= ''
and 'resolved'
then
or 'unresolved'


local currentPage =
local resolution =
trim(pageTitle)
content:tag('div')
:addClass(
'feedback-page-card-resolution'
)


if currentPage == '' then
resolution:wikitext(
currentPage =
'↳ Resolved by '
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)
 
--------------------------------------------------------
-- Save to Bucket
--------------------------------------------------------
 
local saved, saveError =
saveFeedback({
page = currentPage,
user = user,
status = status,
feedback = feedback,
id = id,
createddate = createddate,
link = link,
image = image,
resolvedby = resolvedby,
resolveddate = resolveddate
})
 
if not saved then
return
'<strong style="color:red;">Feedback bucket error: '
.. mw.text.nowiki(
saveError or 'Unknown error'
)
)
.. '</strong>'
end


--------------------------------------------------------
if resolvedBy ~= '' then
-- Render
resolution:wikitext(
--------------------------------------------------------
formatUser(resolvedBy)
)
else
resolution:wikitext(
'Unknown'
)
end


local card =
if resolvedDate ~= '' then
renderFullCard({
resolution:wikitext(
page = currentPage,
' · '
user = user,
.. mw.text.nowiki(
status = status,
resolvedDate
feedback = feedback,
)
id = id,
)
createddate = createddate,
end
link = link,
end
image = image,
resolvedby = resolvedby,
resolveddate = resolveddate
})
 
--------------------------------------------------------
-- Categories
--------------------------------------------------------
 
local category


if resolved then
category =
'[[Category:Resolved Feedback]]'
else
else
category =
'[[Category:Unresolved Feedback]]'
end


if id == '' then
if id ~= '' then
category =
category ..
'[[Category:Feedbacks Missing ID]]'
end


return card .. category
renderResolveControl(
end
root,
id,
true
)


end


------------------------------------------------------------
-- RENDER FEEDBACK LIST
------------------------------------------------------------
local function renderFeedbackList(results)
if not results or #results == 0 then
return '<em>No feedback entries found.</em>'
end
end


local list = {}
renderAttachments(
content,
entry
)


for _, row in ipairs(results) do
return tostring(root)
table.insert(
list,
renderFullCard(row)
)
end
 
return table.concat(
list,
'\n'
)
end
end




------------------------------------------------------------
------------------------------------------------------------
-- PAGE FEEDBACK
-- Page feedback section
------------------------------------------------------------
------------------------------------------------------------


function p.page(frame)
function p.page(frame)
local page =
local page =
currentSubjectPage()
currentSubjectPage()


local entries =
local allEntries, err =
queryFeedback(page)
queryFeedback(page)
if not allEntries then
return ''
end
if #allEntries == 0 then
return ''
end


local unresolved = {}
local unresolved = {}
local resolved = {}
local resolved = {}


for _, entry in ipairs(entries) do
for _, row in ipairs(allEntries) do
if trim(entry.status):lower()
 
== 'resolved'
if row.status == 'resolved' then
then
 
table.insert(
table.insert(
resolved,
resolved,
entry
row
)
)
else
else
table.insert(
table.insert(
unresolved,
unresolved,
entry
row
)
)
end
end
end
end
Line 663: Line 711:
local root =
local root =
mw.html.create('section')
mw.html.create('section')
:addClass('feedback-page-section')
:attr('id', 'feedback-section')
:attr(
'data-unresolved-count',
#unresolved
)
:attr(
:attr(
'id',
'data-resolved-count',
'feedback-section'
#resolved
)
)
:addClass('feedback-page')


--------------------------------------------------------
--------------------------------------------------------
Line 674: Line 727:


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


--------------------------------------------------------
--------------------------------------------------------
-- Filters
-- Filter controls
--------------------------------------------------------
--------------------------------------------------------


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


local function addFilter(
local unresolvedFilter =
label,
filters:tag('span')
value,
:addClass('feedback-page-filter')
active
:addClass(
)
'feedback-page-filter--active'
return filters:tag('button')
)
:attr(
:attr(
'type',
'role',
'button'
'button'
)
:attr(
'tabindex',
'0'
)
)
:attr(
:attr(
'data-feedback-filter',
'data-feedback-filter',
value
'unresolved'
)
)
:addClass('feedback-filter')
:addClass(
active
and 'feedback-filter--active'
or nil
)
:wikitext(label)
end


addFilter(
unresolvedFilter:wikitext(
'Unresolved',
'Unresolved '
'unresolved',
true
)
)


addFilter(
unresolvedFilter:tag('span')
'Resolved',
:addClass('feedback-page-filter-count')
'resolved',
:wikitext(
false
tostring(#unresolved)
)
)
 
if #resolved > 0 then
 
local resolvedFilter =
filters:tag('span')
:addClass('feedback-page-filter')
:attr(
'role',
'button'
)
:attr(
'tabindex',
'0'
)
:attr(
'data-feedback-filter',
'resolved'
)
 
resolvedFilter:wikitext(
'Resolved '
)
 
resolvedFilter:tag('span')
:addClass('feedback-page-filter-count')
:wikitext(
tostring(#resolved)
)


addFilter(
end
'All',
'all',
false
)


--------------------------------------------------------
--------------------------------------------------------
-- Unresolved
-- Unresolved list
--------------------------------------------------------
--------------------------------------------------------


Line 738: Line 809:
'feedback-page-list--unresolved'
'feedback-page-list--unresolved'
)
)
:addClass(
'feedback-page-list--visible'
)


if #unresolved == 0 then
if #unresolved > 0 then
unresolvedList:tag('div')
 
:addClass('feedback-empty')
for _, row in ipairs(unresolved) do
:wikitext(
unresolvedList:node(
'No unresolved feedback.'
renderCompactCard(row)
)
else
for _, entry in ipairs(unresolved) do
unresolvedList:wikitext(
renderCompactCard(entry)
)
)
end
end
end


--------------------------------------------------------
else
-- Resolved
--------------------------------------------------------


local resolvedList =
unresolvedList:tag('div')
root:tag('div')
:addClass(
:addClass(
'feedback-page-list'
'feedback-page-empty'
)
)
:addClass(
:wikitext(
'feedback-page-list--resolved'
'No unresolved feedback.'
)
)


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


--------------------------------------------------------
--------------------------------------------------------
-- All
-- Resolved list
--------------------------------------------------------
--------------------------------------------------------


local allList =
if #resolved > 0 then
root:tag('div')
 
:addClass(
local resolvedList =
'feedback-page-list'
root:tag('div')
)
:addClass(
:addClass(
'feedback-page-list'
'feedback-page-list--all'
)
)
:addClass(
'feedback-page-list--resolved'
)


if #entries == 0 then
for _, row in ipairs(resolved) do
allList:tag('div')
resolvedList:node(
:addClass('feedback-empty')
renderCompactCard(row)
:wikitext(
'No feedback.'
)
else
for _, entry in ipairs(entries) do
allList:wikitext(
renderCompactCard(entry)
)
)
end
end
end
end


Line 815: Line 858:


------------------------------------------------------------
------------------------------------------------------------
-- COUNTS
-- Feedback counts
------------------------------------------------------------
------------------------------------------------------------


function p.counts(frame)
function p.counts(frame)
local page =
currentSubjectPage()
local entries =
local entries =
queryFeedback(
queryFeedback(page)
currentSubjectPage()
 
)
if not entries then
return ''
end


local unresolved = 0
local unresolved = 0
local resolved = 0
local resolved = 0


for _, entry in ipairs(entries) do
for _, row in ipairs(entries) do
if trim(entry.status):lower()
 
== 'resolved'
if row.status == 'resolved' then
then
resolved = resolved + 1
resolved = resolved + 1
else
else
unresolved = unresolved + 1
unresolved = unresolved + 1
end
end
end
end


Line 853: Line 902:


------------------------------------------------------------
------------------------------------------------------------
-- TABLE / DASHBOARD
-- Main / individual feedback
------------------------------------------------------------
------------------------------------------------------------


local function renderTable(entries)
function p.main(frame)
if not entries or #entries == 0 then
 
return '<em>No feedback entries found.</em>'
local parent =
frame:getParent()
 
local args =
parent and parent.args
or frame.args
 
local feedback =
args.feedback
or 'No feedback provided'
 
local user =
args.user
or 'Anonymous'
 
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)
 
local resolved =
isTruthy(args.resolved)
 
local status =
resolved
and 'resolved'
or 'unresolved'
 
local page =
currentSubjectPage()
 
local saved, saveError =
saveFeedback({
page = page,
user = user,
status = status,
feedback = feedback,
id = id,
createddate = createdDate,
link = link,
image = image,
resolvedby = resolvedBy,
resolveddate = resolvedDate
})
 
if not saved then
 
return
'<strong style="color:red;">'
.. 'Feedback bucket error: '
.. mw.text.nowiki(
saveError or 'Unknown error'
)
.. '</strong>'
 
end
end


local table =
local card =
renderFullCard({
page = page,
user = user,
status = status,
feedback = feedback,
id = id,
createddate = createdDate,
link = link,
image = image,
resolvedby = resolvedBy,
resolveddate = resolvedDate
})
 
local categories = {}
 
if resolved then
 
table.insert(
categories,
'[[Category:Resolved Feedback]]'
)
 
else
 
table.insert(
categories,
'[[Category:Unresolved Feedback]]'
)
 
end
 
if id == '' then
 
table.insert(
categories,
'[[Category:Feedbacks Missing ID]]'
)
 
end
 
return card
.. table.concat(categories)
end
 
 
------------------------------------------------------------
-- Feedback table
------------------------------------------------------------
 
local function renderTable(entries, includeResolved)
 
local tableRoot =
mw.html.create('table')
mw.html.create('table')
:addClass('feedback-table')
:addClass('feedback-table')


local head =
local header =
table:tag('thead')
tableRoot:tag('tr')
:tag('tr')


head:tag('th')
if includeResolved then
:wikitext('Status')


head:tag('th')
header:tag('th')
:wikitext('Status')
 
end
 
header:tag('th')
:wikitext('Page')
:wikitext('Page')


head:tag('th')
header:tag('th')
:wikitext('Username')
 
header:tag('th')
:wikitext('Date')
 
header:tag('th')
:wikitext('Feedback')
:wikitext('Feedback')


head:tag('th')
if includeResolved then
:wikitext('User')


head:tag('th')
header:tag('th')
:wikitext('Date')
:wikitext('Resolved by')
 
header:tag('th')
:wikitext('Resolved date')
 
end


local body =
if #entries == 0 then
table:tag('tbody')


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


local resolved =
local colspan =
trim(entry.status):lower()
includeResolved
== 'resolved'
and '7'
or '4'


row:tag('td')
row:tag('td')
:attr(
'colspan',
colspan
)
:addClass(
:addClass(
resolved
'feedback-table-empty'
and 'feedback-table-status--resolved'
or 'feedback-table-status--unresolved'
)
)
:wikitext(
:wikitext(
resolved
'No feedback entries found.'
and 'Resolved'
or 'Unresolved'
)
)
return tostring(tableRoot)
end
for _, entry in ipairs(entries) do
local row =
tableRoot:tag('tr')
:addClass(
entry.status == 'resolved'
and 'feedback-table-row--resolved'
or 'feedback-table-row--unresolved'
)
if includeResolved then
row:tag('td')
:addClass('feedback-table-status')
:wikitext(
entry.status == 'resolved'
and 'Resolved'
or 'Unresolved'
)
end


row:tag('td')
row:tag('td')
:addClass('feedback-table-page')
:addClass('feedback-table-page')
:wikitext(
:wikitext(
entry.page or ''
'[['
.. entry.page
.. ']]'
)
)


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


row:tag('td')
row:tag('td')
:addClass('feedback-table-date')
:wikitext(
:wikitext(
formatUser(entry.user)
mw.text.nowiki(
entry.createddate
or '—'
)
)
)


row:tag('td')
row:tag('td')
:addClass('feedback-table-feedback')
:wikitext(
:wikitext(
entry.createddate or ''
entry.feedback
or ''
)
)
if includeResolved then
row:tag('td')
:addClass(
'feedback-table-resolved-by'
)
:wikitext(
entry.resolvedby
and formatUser(
entry.resolvedby
)
or '—'
)
row:tag('td')
:addClass(
'feedback-table-resolved-date'
)
:wikitext(
entry.resolveddate
and mw.text.nowiki(
entry.resolveddate
)
or '—'
)
end
end
end


return tostring(table)
return tostring(tableRoot)
end
end


------------------------------------------------------------
-- Global feedback page
------------------------------------------------------------


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


local args
local args =
parent and parent.args
or frame.args


if frame
local status =
and frame.args
trim(
and frame.args.status
args.status
then
or ''
args = frame.args
):lower()


elseif parent
--------------------------------------------------------
and parent.args
-- Specific table
and parent.args.status
--------------------------------------------------------
then
args = parent.args


elseif frame
if status == 'resolved'
and frame.args
or status == 'unresolved'
then
then
args = frame.args


elseif parent
local entries =
and parent.args
queryFeedback(
then
nil,
args = parent.args
status
)


else
if not entries then
args = {}
return ''
end
end


return p._view(
return renderTable(
args,
entries,
frame
false
)
)
end
 
 
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
-- Dashboard
--------------------------------------------------------
--------------------------------------------------------


local query =
local unresolved =
bucket
queryFeedback(
nil,
'unresolved'
)


query.select(
local all =
'page',
queryFeedback(
'user',
nil,
'status',
'all'
'feedback',
)
'id',
'createddate',
'link',
'image',
'resolvedby',
'resolveddate'
)


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


local success, entries =
if not all then
pcall(function()
all = {}
return query.run()
end)
 
if not success or not entries then
return
'<em>Could not load feedback.</em>'
end
end


entries =
local root =
sortNewestFirst(entries)
mw.html.create('div')
:addClass('feedback-dashboard')


--------------------------------------------------------
root:tag('h2')
-- Specific status
:addClass('feedback-dashboard-heading')
--------------------------------------------------------
:wikitext('Unresolved Feedback')


if filterStatus == 'resolved'
root:node(
or filterStatus == 'unresolved'
renderTable(
then
unresolved,
return renderFeedbackList(
false
entries
)
)
end
)
 
root:tag('h2')
:addClass('feedback-dashboard-heading')
:wikitext('All Feedback')


--------------------------------------------------------
root:node(
-- All
renderTable(
--------------------------------------------------------
all,
true
)
)


return renderTable(entries)
return tostring(root)
end
end


------------------------------------------------------------
-- Aliases
------------------------------------------------------------


p.table = p.view
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;