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.

Module:Feedback: Difference between revisions

From The Petit Planet Wiki
ReisuDesign (talk | contribs)
No edit summary
ReisuDesign (talk | contribs)
No edit summary
 
(12 intermediate revisions by the same user not shown)
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 ~= ''
return value == 'true'
and value ~= '0'
or value == 'yes'
and value ~= 'false'
or value == '1'
and value ~= 'no'
or value == 'resolved'
and value ~= 'off'
end
end




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


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


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


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


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


Line 52: Line 56:
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, 'Bucket extension is not available'
return nil
end
 
local bucket =
mw.ext.bucket('feedback')
 
if not bucket then
return nil, 'Feedback bucket could not be opened'
end
end


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




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


local aDate =
return aDate > bDate
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 results
return entries
end
end




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


if not bucket then
if not bucket then
return nil, err
return {}
end
end


local query =
local query = bucket
bucket.select(
'page',
'user',
'status',
'feedback',
'id',
'createddate',
'link',
'image',
'resolvedby',
'resolveddate'
)
 
if page and page ~= '' then
query.where(
'page',
page
)
end


if status
query.select(
and status ~= ''
'page',
and status ~= 'all'
'user',
then
'status',
 
'feedback',
query.where(
'id',
'status',
'createddate',
status
'link',
)
'image',
 
'resolvedby',
end
'resolveddate'
 
)
local success, results =
pcall(function()
return query.run()
end)


if not success then
query.where(
return nil, tostring(results)
'page',
end
page
 
return sortNewestFirst(
results or {}
)
)
end


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


------------------------------------------------------------
if not success or not results then
-- Save feedback
return {}
------------------------------------------------------------
 
local function saveFeedback(data)
 
local bucket, err =
getBucket()
 
if not bucket then
return false, err
end
 
local success, saveError =
pcall(function()
 
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
})
 
end)
 
if not success then
return false, tostring(saveError)
end
end


return true
return sortNewestFirst(results)
end
end




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


local function renderMetadata(root, entry)
local function renderAttachments(entry)
local wrapper =
mw.html.create('div')
:addClass('feedback-attachments')


local meta =
local link = trim(entry.link)
root:tag('div')
:addClass('feedback-meta')
 
if entry.user and entry.user ~= '' then
 
meta:tag('span')
:addClass('feedback-user')
:wikitext(
formatUser(entry.user)
)


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


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


meta:tag('span')
if image ~= '' then
:addClass('feedback-meta-date')
image = mw.ustring.gsub(
:wikitext(
image,
' · '
'^File:',
.. mw.text.nowiki(
''
entry.createddate
)
)
)


end
local title =
end
mw.title.new('File:' .. image)
 
 
------------------------------------------------------------
-- 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')


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


if image ~= '' then
return wrapper
 
attachments:tag('span')
:addClass('feedback-attachment')
 
attachments:tag('span')
:addClass('feedback-image-icon')
:wikitext('▣')
 
attachments:tag('span')
:addClass('feedback-image-link')
:wikitext(
'[[Media:'
.. image
.. '|Picture]]'
)
 
end
end
end




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


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


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


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


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


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


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


Line 339: Line 216:
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 =
parent:tag('span')
wrapper: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('feedback-resolve-button--compact')
:addClass(
:attr('aria-label', 'Resolve feedback')
'feedback-resolve-button--compact'
: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 =
entry.status == 'resolved'
trim(entry.status):lower() == 'resolved'


local id =
local id = trim(entry.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 402: Line 296:
)
)
end
end
--------------------------------------------------------
-- Main content column
--------------------------------------------------------
local content =
root:tag('div')
:addClass('feedback-content')


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


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


main:tag('span')
main:tag('span')
Line 420: Line 326:
: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(
entry.feedback
feedback ~= ''
and feedback
or 'No feedback provided'
or 'No feedback provided'
)
)
Line 435: Line 346:
--------------------------------------------------------
--------------------------------------------------------


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


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


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


--------------------------------------------------------
--------------------------------------------------------
-- Resolve button
-- Attachments
--------------------------------------------------------
--------------------------------------------------------
 
if not resolved
content:node(
and id ~= ''
    renderAttachments(entry)
then
)
 
renderResolveControl(
main,
id,
false
)
 
end


--------------------------------------------------------
--------------------------------------------------------
-- Attachments
-- Resolution information
--------------------------------------------------------
--------------------------------------------------------


renderAttachments(
renderResolution(
root,
content,
entry
entry
)
)


--------------------------------------------------------
--------------------------------------------------------
-- Resolution
-- Missing ID warning
--------------------------------------------------------
--------------------------------------------------------


if resolved then
if id == '' then
renderResolution(
content:tag('div')
root,
:addClass('feedback-missing-id')
entry
:wikitext(
)
'This feedback entry is missing an ID.'
)
end
end


--------------------------------------------------------
--------------------------------------------------------
-- Missing ID warning
-- Resolve button
--
-- IMPORTANT:
-- This is outside .feedback-content so it
-- remains on the right.
--------------------------------------------------------
--------------------------------------------------------


if not resolved
if not resolved then
and id == ''
renderResolveControl(
then
root,
 
id,
root:tag('div')
page,
:addClass('feedback-missing-id')
false
:wikitext(
)
'Add an ID to enable the Resolve button.'
)
 
end
end


Line 520: Line 413:


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


local function renderCompactCard(entry)
local function renderCompactCard(entry)
return renderFullCard(entry)
end
------------------------------------------------------------
-- SAVE FEEDBACK
------------------------------------------------------------


local resolved =
local function saveFeedback(data)
entry.status == 'resolved'
local bucket = getBucket()


local id =
if not bucket then
trim(entry.id)
return false, 'Bucket extension is not available'
end


local root =
local success, err = pcall(function()
mw.html.create('div')
bucket.put({
:addClass('feedback-page-card')
page = data.page,
:addClass(
user = data.user,
resolved
status = data.status,
and 'feedback-page-card--resolved'
feedback = data.feedback,
or 'feedback-page-card--unresolved'
id = data.id,
)
createddate = data.createddate,
link = data.link,
image = data.image,
resolvedby = data.resolvedby,
resolveddate = data.resolveddate
})
end)


if id ~= '' then
if not success then
root:attr(
return false, tostring(err)
'data-feedback-id',
id
)
end
end


local content =
return true
root:tag('div')
end
:addClass('feedback-page-card-content')
 
 
------------------------------------------------------------
-- MAIN
--
-- Used by Template:Feedback
------------------------------------------------------------
 
function p.main(frame)
local parent = frame:getParent()
 
local args =
parent and parent.args
or frame.args
 
local pageTitle = nil
 
if parent and parent.getTitle then
local title = parent:getTitle()


local firstLine =
if title
content:tag('div')
and title ~= ''
:addClass('feedback-page-card-line')
and not title:find('^Template:')
then
pageTitle = title
end
end


firstLine:tag('span')
return p._main(
:addClass('feedback-status')
args,
:addClass(
pageTitle
resolved
)
and 'feedback-status--resolved'
end
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 =
function p._main(args, pageTitle)
content:tag('div')
local feedback =
:addClass('feedback-page-card-meta')
trim(args.feedback)


if entry.user
if feedback == '' then
and entry.user ~= ''
feedback = 'No feedback provided'
then
end


meta:wikitext(
local user =
formatUser(entry.user)
trim(args.user)
)


if user == '' then
user = 'Anonymous'
end
end


if entry.createddate
local resolved =
and entry.createddate ~= ''
isTruthy(args.resolved)
then
 
local status =
resolved
and 'resolved'
or 'unresolved'


meta:wikitext(
local currentPage =
' · '
trim(pageTitle)
.. mw.text.nowiki(
entry.createddate
)
)


if currentPage == '' then
currentPage =
mw.title.getCurrentTitle().fullText
end
end


if resolved then
local id =
trim(args.id)
 
local createddate =
trim(args.createddate)
 
local link =
trim(args.link)


local resolvedBy =
local image =
trim(entry.resolvedby)
trim(args.image)


local resolvedDate =
local resolvedby =
trim(entry.resolveddate)
trim(args.resolvedby)


if resolvedBy ~= ''
local resolveddate =
or resolvedDate ~= ''
trim(args.resolveddate)
then


local resolution =
--------------------------------------------------------
content:tag('div')
-- Save to Bucket
:addClass(
--------------------------------------------------------
'feedback-page-card-resolution'
)


resolution:wikitext(
local saved, saveError =
'↳ Resolved by '
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
--------------------------------------------------------
resolution:wikitext(
-- Render
formatUser(resolvedBy)
--------------------------------------------------------
)
else
resolution:wikitext(
'Unknown'
)
end


if resolvedDate ~= '' then
local card =
resolution:wikitext(
renderFullCard({
' · '
page = currentPage,
.. mw.text.nowiki(
user = user,
resolvedDate
status = status,
)
feedback = feedback,
)
id = id,
end
createddate = createddate,
end
link = link,
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
category =
category ..
'[[Category:Feedbacks Missing ID]]'
end
return card .. category
end


if id ~= '' then


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


end
local list = {}


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


renderAttachments(
return table.concat(
content,
list,
entry
'\n'
)
)
return tostring(root)
end
end




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


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


local allEntries, err =
local entries =
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 _, row in ipairs(allEntries) do
for _, entry in ipairs(entries) do
 
if trim(entry.status):lower()
if row.status == 'resolved' then
== 'resolved'
 
then
table.insert(
table.insert(
resolved,
resolved,
row
entry
)
)
else
else
table.insert(
table.insert(
unresolved,
unresolved,
row
entry
)
)
end
end
end
end
Line 711: Line 663:
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(
'data-resolved-count',
'id',
#resolved
'feedback-section'
)
)
:addClass('feedback-page')


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


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


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


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


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


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


unresolvedFilter:tag('span')
addFilter(
:addClass('feedback-page-filter-count')
'Resolved',
:wikitext(
'resolved',
tostring(#unresolved)
false
)
)


if #resolved > 0 then
addFilter(
 
'All',
local resolvedFilter =
'all',
filters:tag('span')
false
: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)
)
 
end


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


Line 808: Line 737:
:addClass(
:addClass(
'feedback-page-list--unresolved'
'feedback-page-list--unresolved'
)
:addClass(
'feedback-page-list--visible'
)
)


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


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


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


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


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


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


Line 858: Line 815:


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


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


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


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


Line 902: Line 853:


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


function p.main(frame)
local function renderTable(entries)
 
if not entries or #entries == 0 then
local parent =
return '<em>No feedback entries found.</em>'
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 card =
local table =
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 header =
local head =
tableRoot:tag('tr')
table:tag('thead')
:tag('tr')


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


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


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


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


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


header:tag('th')
local body =
:wikitext('Resolved date')
table:tag('tbody')
 
end
 
if #entries == 0 then


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


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


row:tag('td')
row:tag('td')
:attr(
'colspan',
colspan
)
:addClass(
:addClass(
'feedback-table-empty'
resolved
and 'feedback-table-status--resolved'
or 'feedback-table-status--unresolved'
)
)
:wikitext(
:wikitext(
'No feedback entries found.'
resolved
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-user')
:addClass('feedback-table-text')
:wikitext(
:wikitext(
formatUser(entry.user)
entry.feedback or ''
)
)


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


row:tag('td')
row:tag('td')
:addClass('feedback-table-feedback')
:wikitext(
:wikitext(
entry.feedback
entry.createddate or ''
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(tableRoot)
return tostring(table)
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
and frame.args
and frame.args.status
then
args = frame.args


local status =
elseif parent
trim(
and parent.args
args.status
and parent.args.status
or ''
then
):lower()
args = parent.args


--------------------------------------------------------
elseif frame
-- Specific table
and frame.args
--------------------------------------------------------
then
args = frame.args


if status == 'resolved'
elseif parent
or status == 'unresolved'
and parent.args
then
then
args = parent.args
else
args = {}
end
return p._view(
args,
frame
)
end


local entries =
queryFeedback(
nil,
status
)


if not entries then
function p._view(args, frame)
return ''
local bucket =
end
getBucket()


return renderTable(
if not bucket then
entries,
return
false
'<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


--------------------------------------------------------
--------------------------------------------------------
-- Dashboard
-- Query
--------------------------------------------------------
--------------------------------------------------------


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


local all =
if filterStatus == 'resolved'
queryFeedback(
or filterStatus == 'unresolved'
nil,
then
'all'
query.where(
'status',
filterStatus
)
)
end


if not unresolved then
local success, entries =
unresolved = {}
pcall(function()
end
return query.run()
end)


if not all then
if not success or not entries then
all = {}
return
'<em>Could not load feedback.</em>'
end
end


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


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


root:node(
if filterStatus == 'resolved'
renderTable(
or filterStatus == 'unresolved'
unresolved,
then
false
return renderFeedbackList(
entries
)
)
)
end


root:tag('h2')
--------------------------------------------------------
:addClass('feedback-dashboard-heading')
-- All
:wikitext('All Feedback')
--------------------------------------------------------


root:node(
return renderTable(entries)
renderTable(
all,
true
)
)
 
return tostring(root)
end
end


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


p.table = p.view
p.table = p.view


return p
return p