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 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(entry)
local function renderAttachments(
local wrapper =
root,
mw.html.create('div')
entry
:addClass('feedback-attachments')
)
 
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'
)


local link = trim(entry.link)


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


local image = trim(entry.image)


if image ~= '' then
if image ~= '' then
image = mw.ustring.gsub(
image,
'^File:',
''
)


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


if title then
imageAttachment:tag('span')
local imageWrapper =
:addClass(
wrapper:tag('span')
'feedback-image-link'
:addClass('feedback-attachment-image')
)
:wikitext(
imageWrapper:tag('span')
'[[Media:' ..
:addClass('feedback-attachment-icon')
image ..
'|Picture]]'
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(
local resolvedBy = trim(entry.resolvedby)
root,
local resolvedDate = trim(entry.resolveddate)
entry
)
 
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
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 219: 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 250: 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 289: 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 310: 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 326: 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


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


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


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


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


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


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


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


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


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


return tostring(root)
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 674: Line 1,034:


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


--------------------------------------------------------
--------------------------------------------------------
Line 683: 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 738: 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 848: 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
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;