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)
Undo revision 24119 by ReisuDesign (talk)
Tag: Undo
Line 1: Line 1:
local p = {}
local p = {}


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


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


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


return mw.text.trim(tostring(value))
return mw.text.trim(
tostring(value or '')
)
 
end
end




local function formatUser(user)
local function formatUser(user)
user = trim(user)


if user == '' then
user =
trim(user)
 
if user == ''
or user:lower() == 'anonymous'
then
return 'Anonymous'
return 'Anonymous'
end
end
Line 40: Line 50:
user
user
)
)
end
end




local function currentSubjectPage()
local function currentSubjectPage()
local title = mw.title.getCurrentTitle()


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


return title.fullText
end
end




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
end


return mw.ext.bucket('feedback')
 
local bucket =
mw.ext.bucket('feedback')
 
 
if not bucket then
return nil, 'Feedback bucket could not be opened'
end
 
 
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)
 
 
if aDate == ''
and bDate ~= ''
then
return false
end
 
 
if aDate ~= ''
and bDate == ''
then
return true
end
 
 
if aDate ~= bDate then
return aDate > bDate
end
 
 
return trim(a.id) > trim(b.id)
 
end
end
)
)


return entries
 
return results
 
end
end




local function queryFeedback(page)
local function queryFeedback(
local bucket = getBucket()
page,
status
)
 
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
 
 
local success, results =
pcall(
function()
return query.run()
end
)
 
 
if not success then
return nil, tostring(results)
end
end


local query = bucket


query.select(
return sortNewestFirst(
'page',
results or {}
'user',
'status',
'feedback',
'id',
'createddate',
'link',
'image',
'resolvedby',
'resolveddate'
)
)


query.where(
end
'page',
 
page
 
)
------------------------------------------------------------
-- Save feedback
------------------------------------------------------------
 
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
 
 
return true
 
end
 
 
------------------------------------------------------------
-- Shared metadata
------------------------------------------------------------
 
local function renderMetadata(
root,
entry
)
 
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


local success, results = pcall(function()
meta:tag('span')
return query.run()
:addClass(
end)
'feedback-meta-date'
)
:wikitext(
' · ' ..
mw.text.nowiki(
entry.createddate
)
)


if not success or not results then
return {}
end
end


return sortNewestFirst(results)
end
end




------------------------------------------------------------
------------------------------------------------------------
-- ATTACHMENTS
-- Attachments
------------------------------------------------------------
------------------------------------------------------------


local function renderAttachments(parent, entry)
local function renderAttachments(
local link = trim(entry.link)
root,
local image = trim(entry.image)
entry
)


if link == '' and image == '' then
local link =
trim(entry.link)
 
 
local image =
trim(entry.image)
 
 
if link == ''
and image == ''
then
return
return
end
end


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


if link ~= '' then
if link ~= '' then
attachments:tag('a')
 
:addClass('feedback-link')
attachments:tag('span')
:attr('href', link)
:addClass(
:attr('target', '_blank')
'feedback-attachment'
:attr('rel', 'noopener noreferrer')
)
:wikitext('View link')
:wikitext(
'🔗 ' ..
'[' ..
link ..
' Link]'
)
 
end
end


if image ~= '' then
if image ~= '' then
if link ~= '' then
attachments:wikitext(' ')
end


attachments:tag('a')
local imageAttachment =
:addClass('feedback-image-link')
attachments:tag('span')
:attr(
:addClass(
'href',
'feedback-attachment'
'./File:' .. image
)
 
 
imageAttachment:tag('span')
:addClass(
'feedback-image-icon'
)
:wikitext('▣')
 
 
imageAttachment:tag('span')
:addClass(
'feedback-image-link'
)
:wikitext(
'[[Media:' ..
image ..
'|Picture]]'
)
)
:wikitext('View image')
 
end
end
end
end




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


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


if resolvedBy == '' and resolvedDate == '' then
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
resolution:wikitext(
resolution:wikitext(
formatUser(resolvedBy)
formatUser(
resolvedBy
)
)
)
else
else
resolution:wikitext('Unknown')
 
resolution:wikitext(
'Unknown'
)
 
end
end


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




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


Line 202: Line 480:
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(
:attr(
'feedback-resolve-button'
'data-feedback-id',
id
)
)
:attr(
 
'data-feedback-page',
 
page or ''
 
control:attr(
'data-feedback-id',
id
)
 
 
if page
and page ~= ''
then
 
control:attr(
'data-feedback-page',
page
)
 
end
 
 
if compact then
 
control
:addClass(
'feedback-resolve-button--compact'
)
)
:attr(
:attr(
Line 233: Line 532:
'Resolve feedback'
'Resolve feedback'
)
)
:attr(
'title',
'Resolve feedback'
)
:wikitext(
'✓'
)
else


if compact then
control
control
:addClass(
:attr(
'feedback-resolve-button--compact'
'role',
'button'
)
:attr(
'tabindex',
'0'
)
)
:attr(
:attr(
'title',
'aria-label',
'Resolve feedback'
'Resolve feedback'
)
)
:wikitext('✓')
:wikitext(
else
'Resolve'
control:wikitext('Resolve')
)
 
end
end
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 page =
trim(entry.page)


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 =
mw.html.create('div')
mw.html.create('div')
:addClass('feedback-box')
:addClass(
'feedback-box'
)
:addClass(
:addClass(
resolved
resolved
Line 272: Line 594:
or 'feedback-box--unresolved'
or 'feedback-box--unresolved'
)
)


if id ~= '' then
if id ~= '' then
root:attr(
root:attr(
'data-feedback-id',
'data-feedback-id',
id
id
)
)
end
end


--------------------------------------------------------
-- Main content column
--------------------------------------------------------
local content =
root:tag('div')
:addClass('feedback-content')


--------------------------------------------------------
--------------------------------------------------------
Line 293: Line 611:


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')
:addClass('feedback-status')
:addClass(
'feedback-status'
)
:addClass(
:addClass(
resolved
resolved
Line 309: Line 628:
: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'
)
)


--------------------------------------------------------
--------------------------------------------------------
-- Metadata
-- Inline metadata
--------------------------------------------------------
--------------------------------------------------------


local meta =
local metadata =
main:tag('span')
main:tag('span')
:addClass('feedback-inline-meta')
:addClass(
'feedback-inline-meta'
)
 
 
if entry.user
and entry.user ~= ''
then
 
metadata:wikitext(
' · ' ..
formatUser(
entry.user
)
)
 
end
 


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


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


--------------------------------------------------------
--------------------------------------------------------
Line 350: Line 705:


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


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


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


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


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


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


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


return tostring(root)
end
end




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


local function renderCompactCard(entry)
local function renderCompactCard(
return renderFullCard(entry)
entry
end
)
 
local resolved =
entry.status == 'resolved'
 
 
local id =
trim(entry.id)
 
 
local page =
trim(entry.page)




------------------------------------------------------------
local root =
-- SAVE FEEDBACK
mw.html.create('div')
------------------------------------------------------------
:addClass(
'feedback-page-card'
)
:addClass(
resolved
and 'feedback-page-card--resolved'
or 'feedback-page-card--unresolved'
)


local function saveFeedback(data)
local bucket = getBucket()


if not bucket then
if id ~= '' then
return false, 'Bucket extension is not available'
end


local success, err = pcall(function()
root:attr(
bucket.put({
'data-feedback-id',
page = data.page,
id
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(err)
end
end


return true
end


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


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


function p.main(frame)
local firstLine =
local parent = frame:getParent()
content:tag('div')
:addClass(
'feedback-page-card-line'
)


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


local pageTitle = nil
firstLine:tag('span')
:addClass(
'feedback-status'
)
:addClass(
resolved
and 'feedback-status--resolved'
or 'feedback-status--unresolved'
)
:wikitext(
resolved
and 'RESOLVED'
or 'UNRESOLVED'
)


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


if title
firstLine:tag('span')
and title ~= ''
:addClass(
and not title:find('^Template:')
'feedback-page-card-text'
then
)
pageTitle = title
:wikitext(
end
entry.feedback
end
or 'No feedback provided'
)
 


return p._main(
--------------------------------------------------------
args,
-- Metadata
pageTitle
--------------------------------------------------------
)
end


local meta =
content:tag('div')
:addClass(
'feedback-page-card-meta'
)


function p._main(args, pageTitle)
local feedback =
trim(args.feedback)


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


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


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


local resolved =
isTruthy(args.resolved)


local status =
if entry.createddate
resolved
and entry.createddate ~= ''
and 'resolved'
then
or 'unresolved'


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


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


local id =
trim(args.id)


local createddate =
--------------------------------------------------------
trim(args.createddate)
-- Resolution
--------------------------------------------------------
 
if resolved then
 
local resolvedBy =
trim(entry.resolvedby)


local link =
trim(args.link)


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


local resolvedby =
trim(args.resolvedby)


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


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


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
resolution:wikitext(
return
'↳ Resolved by '
'<strong style="color:red;">Feedback bucket error: '
.. mw.text.nowiki(
saveError or 'Unknown error'
)
)
.. '</strong>'
end


--------------------------------------------------------
-- Render
--------------------------------------------------------


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


--------------------------------------------------------
end
-- Categories
--------------------------------------------------------


local category
end


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,
page,
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 = {}


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


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




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


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


local entries =
 
queryFeedback(page)
local allEntries =
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
 
if trim(entry.status):lower()
for _, row in ipairs(allEntries) do
== 'resolved'
 
then
if row.status == 'resolved' then
 
table.insert(
table.insert(
resolved,
resolved,
entry
row
)
)
else
else
table.insert(
table.insert(
unresolved,
unresolved,
entry
row
)
)
end
end
end
end


local root =
local root =
mw.html.create('section')
mw.html.create('section')
:addClass(
'feedback-page-section'
)
:attr(
:attr(
'id',
'id',
'feedback-section'
'feedback-section'
)
)
:addClass('feedback-page')
:attr(
'data-unresolved-count',
#unresolved
)
:attr(
'data-resolved-count',
#resolved
)
 


--------------------------------------------------------
--------------------------------------------------------
Line 658: Line 1,034:


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


--------------------------------------------------------
--------------------------------------------------------
Line 667: Line 1,048:
local filters =
local filters =
root:tag('div')
root:tag('div')
:addClass('feedback-filters')
:addClass(
'feedback-page-filters'
)


local function addFilter(
 
label,
local unresolvedFilter =
value,
filters:tag('span')
active
:addClass(
)
'feedback-page-filter'
return filters:tag('button')
)
:addClass(
'feedback-page-filter--active'
)
:attr(
:attr(
'type',
'role',
'button'
'button'
)
:attr(
'tabindex',
'0'
)
)
:attr(
:attr(
'data-feedback-filter',
'data-feedback-filter',
value
'unresolved'
)
)
:addClass('feedback-filter')
 
 
unresolvedFilter:wikitext(
'Unresolved '
)
 
 
unresolvedFilter:tag('span')
:addClass(
'feedback-page-filter-count'
)
:wikitext(
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(
:addClass(
active
'feedback-page-filter-count'
and 'feedback-filter--active'
)
or nil
:wikitext(
tostring(
#resolved
)
)
)
:wikitext(label)
 
end
end


addFilter(
'Unresolved',
'unresolved',
true
)
addFilter(
'Resolved',
'resolved',
false
)
addFilter(
'All',
'all',
false
)


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


Line 722: Line 1,142:
'feedback-page-list--unresolved'
'feedback-page-list--unresolved'
)
)
:addClass(
 
'feedback-page-list--visible'
 
if #unresolved > 0 then
 
for _, row in ipairs(unresolved) do
 
unresolvedList:node(
renderCompactCard(
row
)
)
)


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


--------------------------------------------------------
--------------------------------------------------------
-- Resolved
-- Resolved list
--------------------------------------------------------
--------------------------------------------------------


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


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


--------------------------------------------------------
for _, row in ipairs(resolved) do
-- All
--------------------------------------------------------


local allList =
resolvedList:node(
root:tag('div')
renderCompactCard(
:addClass(
row
'feedback-page-list'
)
)
:addClass(
'feedback-page-list--all'
)
)


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


return tostring(root)
return tostring(root)
end
end




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


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


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


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


return tostring(
return tostring(
mw.html.create('span')
mw.html.create('span')
:addClass('feedback-count-data')
:addClass(
'feedback-count-data'
)
:attr(
:attr(
'data-unresolved',
'data-unresolved',
Line 832: Line 1,253:
resolved
resolved
)
)
)
)
end
end




------------------------------------------------------------
------------------------------------------------------------
-- 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 header =
tableRoot:tag('tr')
 
 
if includeResolved then
 
header:tag('th')
:wikitext(
'Status'
)
 
end
 
 
header:tag('th')
:wikitext(
'Page'
)
 
 
header:tag('th')
:wikitext(
'Username'
)
 
 
header:tag('th')
:wikitext(
'Date'
)
 


local head =
header:tag('th')
table:tag('thead')
:wikitext(
:tag('tr')
'Feedback'
)


head:tag('th')
:wikitext('Status')


head:tag('th')
if includeResolved then
:wikitext('Page')
 
header:tag('th')
:wikitext(
'Resolved by'
)
 


head:tag('th')
header:tag('th')
:wikitext('Feedback')
:wikitext(
'Resolved date'
)


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


head:tag('th')
:wikitext('Date')


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 colspan =
includeResolved
and '7'
or '4'


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


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


if frame
local args =
and frame.args
parent
and frame.args.status
and parent.args
then
or frame.args
args = frame.args
 
 
local status =
trim(
args.status or ''
):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
args = {}
end


return p._view(
if not entries then
args,
return ''
frame
end
)
end




function p._view(args, frame)
return renderTable(
local bucket =
entries,
getBucket()
false
)


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(
'page',
'user',
'status',
'feedback',
'id',
'createddate',
'link',
'image',
'resolvedby',
'resolveddate'
)


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


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


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


entries =
sortNewestFirst(entries)


--------------------------------------------------------
local root =
-- Specific status
mw.html.create('div')
--------------------------------------------------------
:addClass(
'feedback-dashboard'
)
 
 
root:tag('h2')
:addClass(
'feedback-dashboard-heading'
)
:wikitext(
'Unresolved Feedback'
)
 
 
root:node(
renderTable(
unresolved,
false
)
)
 
 
root:tag('h2')
:addClass(
'feedback-dashboard-heading'
)
:wikitext(
'All Feedback'
)
 


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


--------------------------------------------------------
return tostring(
-- All
root
--------------------------------------------------------
)


return renderTable(entries)
end
end


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


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


return p
return p