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)
Replaced content with "------------------------------------------------------------ -- Resolve control ------------------------------------------------------------ local function renderResolveControl( parent, id, compact, page ) if not id or id == '' then return end local control = parent:tag('span') :addClass('feedback-resolve-button') :attr( 'data-feedback-id', id ) if page and page ~= '' then control:attr( 'data-feedback-page', page ) end if..."
Tag: Replaced
ReisuDesign (talk | contribs)
No edit summary
 
(11 intermediate revisions by the same user not shown)
Line 1: Line 1:
local p = {}
------------------------------------------------------------
------------------------------------------------------------
-- Resolve control
-- HELPERS
------------------------------------------------------------
 
local function isTruthy(value)
if value == nil then
return false
end
 
value = mw.text.trim(tostring(value)):lower()
 
return value == 'true'
or value == 'yes'
or value == '1'
or value == 'resolved'
end
 
 
local function trim(value)
if value == nil then
return ''
end
 
return mw.text.trim(tostring(value))
end
 
 
local function formatUser(user)
user = trim(user)
 
if user == '' then
return 'Anonymous'
end
 
return string.format(
'[[User:%s|%s]]',
user,
user
)
end
 
 
local function currentSubjectPage()
local title = mw.title.getCurrentTitle()
 
if title.subjectPageTitle then
return title.subjectPageTitle.fullText
end
 
return title.fullText
end
 
 
local function getBucket()
if not (mw.ext and mw.ext.bucket) then
return nil
end
 
return mw.ext.bucket('feedback')
end
 
 
local function sortNewestFirst(entries)
table.sort(
entries,
function(a, b)
local aDate = trim(a.createddate)
local bDate = trim(b.createddate)
 
return aDate > bDate
end
)
 
return entries
end
 
 
local function queryFeedback(page)
local bucket = getBucket()
 
if not bucket then
return {}
end
 
local query = bucket
 
query.select(
'page',
'user',
'status',
'feedback',
'id',
'createddate',
'link',
'image',
'resolvedby',
'resolveddate'
)
 
query.where(
'page',
page
)
 
local success, results = pcall(function()
return query.run()
end)
 
if not success or not results then
return {}
end
 
return sortNewestFirst(results)
end
 
 
------------------------------------------------------------
-- ATTACHMENTS
------------------------------------------------------------
 
local function renderAttachments(entry)
local wrapper =
mw.html.create('div')
:addClass('feedback-attachments')
 
local link = trim(entry.link)
 
if link ~= '' then
local linkWrapper =
wrapper:tag('span')
:addClass('feedback-attachment-link')
linkWrapper:tag('span')
:addClass('feedback-attachment-icon')
linkWrapper:wikitext(
'[' .. link .. ' ' .. mw.text.nowiki(link) .. ']'
)
end
 
local image = trim(entry.image)
 
if image ~= '' then
image = mw.ustring.gsub(
image,
'^File:',
''
)
 
local title =
mw.title.new('File:' .. image)
 
if title then
local imageWrapper =
wrapper:tag('span')
:addClass('feedback-attachment-image')
imageWrapper:tag('span')
:addClass('feedback-attachment-icon')
imageWrapper:wikitext(
'[' ..
title:fullUrl() ..
' ' ..
mw.text.nowiki(image) ..
']'
)
end
end
 
return wrapper
end
 
 
------------------------------------------------------------
-- RESOLUTION
------------------------------------------------------------
 
local function renderResolution(parent, entry)
local resolvedBy = trim(entry.resolvedby)
local resolvedDate = trim(entry.resolveddate)
 
if resolvedBy == '' and resolvedDate == '' then
return
end
 
local resolution =
parent:tag('div')
:addClass('feedback-resolution')
 
resolution:wikitext('Resolved by ')
 
if resolvedBy ~= '' then
resolution:wikitext(
formatUser(resolvedBy)
)
else
resolution:wikitext('Unknown')
end
 
if resolvedDate ~= '' then
resolution:wikitext(
' on ' .. mw.text.nowiki(resolvedDate)
)
end
end
 
 
------------------------------------------------------------
-- RESOLVE CONTROL
------------------------------------------------------------
------------------------------------------------------------


Line 6: Line 216:
parent,
parent,
id,
id,
compact,
page,
page
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(
:attr(
Line 21: Line 234:
id
id
)
)
:attr(
'data-feedback-page',
page or ''
)
:attr(
'role',
'button'
)
:attr(
'tabindex',
'0'
)
:attr(
'aria-label',
'Resolve feedback'
)
if compact then
control
:addClass(
'feedback-resolve-button--compact'
)
:attr(
'title',
'Resolve feedback'
)
:wikitext('✓')
else
control:wikitext('Resolve')
end
end
------------------------------------------------------------
-- FULL FEEDBACK CARD
------------------------------------------------------------
local function renderFullCard(entry)
local resolved =
trim(entry.status):lower() == 'resolved'
local id = trim(entry.id)
local page = trim(entry.page)
local feedback = trim(entry.feedback)
local user = trim(entry.user)
local createddate = trim(entry.createddate)
local root =
mw.html.create('div')
:addClass('feedback-box')
:addClass(
resolved
and 'feedback-box--resolved'
or 'feedback-box--unresolved'
)
if id ~= '' then
root:attr(
'data-feedback-id',
id
)
end
--------------------------------------------------------
-- Main content column
--------------------------------------------------------
local content =
root:tag('div')
:addClass('feedback-content')
--------------------------------------------------------
-- Main line
--------------------------------------------------------
local main =
content:tag('div')
:addClass('feedback-main-line')
--------------------------------------------------------
-- Status
--------------------------------------------------------
main:tag('span')
:addClass('feedback-status')
:addClass(
resolved
and 'feedback-status--resolved'
or 'feedback-status--unresolved'
)
:wikitext(
resolved
and 'Resolved'
or 'Unresolved'
)
--------------------------------------------------------
-- Feedback text
--------------------------------------------------------
main:tag('span')
:addClass('feedback-text')
:wikitext(
feedback ~= ''
and feedback
or 'No feedback provided'
)
--------------------------------------------------------
-- Metadata
--------------------------------------------------------
local meta =
main:tag('span')
:addClass('feedback-inline-meta')
if user ~= '' then
meta:wikitext(
' · ' .. formatUser(user)
)
end
if createddate ~= '' then
meta:wikitext(
' · ' .. mw.text.nowiki(createddate)
)
end
--------------------------------------------------------
-- Attachments
--------------------------------------------------------
content:node(
    renderAttachments(entry)
)
--------------------------------------------------------
-- Resolution information
--------------------------------------------------------
renderResolution(
content,
entry
)
--------------------------------------------------------
-- Missing ID warning
--------------------------------------------------------
if id == '' then
content:tag('div')
:addClass('feedback-missing-id')
:wikitext(
'This feedback entry is missing an ID.'
)
end
--------------------------------------------------------
-- Resolve button
--
-- IMPORTANT:
-- This is outside .feedback-content so it
-- remains on the right.
--------------------------------------------------------


if page and page ~= '' then
if not resolved then
control:attr(
renderResolveControl(
'data-feedback-page',
root,
page
id,
page,
false
)
)
end
end


if compact then
return tostring(root)
end
 
 
------------------------------------------------------------
-- COMPACT CARD
------------------------------------------------------------
 
local function renderCompactCard(entry)
return renderFullCard(entry)
end
 
 
------------------------------------------------------------
-- SAVE FEEDBACK
------------------------------------------------------------
 
local function saveFeedback(data)
local bucket = getBucket()
 
if not bucket then
return false, 'Bucket extension is not available'
end
 
local success, err = 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(err)
end
 
return true
end
 
 
------------------------------------------------------------
-- 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()
 
if title
and title ~= ''
and not title:find('^Template:')
then
pageTitle = title
end
end
 
return p._main(
args,
pageTitle
)
end
 
 
function p._main(args, pageTitle)
local feedback =
trim(args.feedback)
 
if feedback == '' then
feedback = 'No feedback provided'
end
 
local user =
trim(args.user)
 
if user == '' then
user = 'Anonymous'
end
 
local resolved =
isTruthy(args.resolved)
 
local status =
resolved
and 'resolved'
or 'unresolved'
 
local currentPage =
trim(pageTitle)
 
if currentPage == '' then
currentPage =
mw.title.getCurrentTitle().fullText
end
 
local id =
trim(args.id)
 
local createddate =
trim(args.createddate)
 
local link =
trim(args.link)
 
local image =
trim(args.image)
 
local resolvedby =
trim(args.resolvedby)
 
local resolveddate =
trim(args.resolveddate)
 
--------------------------------------------------------
-- Save to Bucket
--------------------------------------------------------
 
local saved, saveError =
saveFeedback({
page = currentPage,
user = user,
status = status,
feedback = feedback,
id = id,
createddate = createddate,
link = link,
image = image,
resolvedby = resolvedby,
resolveddate = resolveddate
})
 
if not saved then
return
'<strong style="color:red;">Feedback bucket error: '
.. mw.text.nowiki(
saveError or 'Unknown error'
)
.. '</strong>'
end
 
--------------------------------------------------------
-- Render
--------------------------------------------------------
 
local card =
renderFullCard({
page = currentPage,
user = user,
status = status,
feedback = feedback,
id = id,
createddate = createddate,
link = link,
image = image,
resolvedby = resolvedby,
resolveddate = resolveddate
})
 
--------------------------------------------------------
-- Categories
--------------------------------------------------------
 
local category
 
if resolved then
category =
'[[Category:Resolved Feedback]]'
else
category =
'[[Category:Unresolved Feedback]]'
end
 
if id == '' then
category =
category ..
'[[Category:Feedbacks Missing ID]]'
end
 
return card .. category
end
 
 
------------------------------------------------------------
-- RENDER FEEDBACK LIST
------------------------------------------------------------
 
local function renderFeedbackList(results)
if not results or #results == 0 then
return '<em>No feedback entries found.</em>'
end
 
local list = {}
 
for _, row in ipairs(results) do
table.insert(
list,
renderFullCard(row)
)
end
 
return table.concat(
list,
'\n'
)
end
 
 
------------------------------------------------------------
-- PAGE FEEDBACK
------------------------------------------------------------
 
function p.page(frame)
local page =
currentSubjectPage()
 
local entries =
queryFeedback(page)
 
local unresolved = {}
local resolved = {}
 
for _, entry in ipairs(entries) do
if trim(entry.status):lower()
== 'resolved'
then
table.insert(
resolved,
entry
)
else
table.insert(
unresolved,
entry
)
end
end
 
local root =
mw.html.create('section')
:attr(
'id',
'feedback-section'
)
:addClass('feedback-page')
 
--------------------------------------------------------
-- Heading
--------------------------------------------------------
 
root:tag('h2')
:addClass('feedback-page-title')
:wikitext('Feedbacks')
 
--------------------------------------------------------
-- Filters
--------------------------------------------------------
 
local filters =
root:tag('div')
:addClass('feedback-filters')
 
local function addFilter(
label,
value,
active
)
return filters:tag('button')
:attr(
'type',
'button'
)
:attr(
'data-feedback-filter',
value
)
:addClass('feedback-filter')
:addClass(
active
and 'feedback-filter--active'
or nil
)
:wikitext(label)
end
 
addFilter(
'Unresolved',
'unresolved',
true
)
 
addFilter(
'Resolved',
'resolved',
false
)
 
addFilter(
'All',
'all',
false
)
 
--------------------------------------------------------
-- Unresolved
--------------------------------------------------------
 
local unresolvedList =
root:tag('div')
:addClass(
'feedback-page-list'
)
:addClass(
'feedback-page-list--unresolved'
)
:addClass(
'feedback-page-list--visible'
)
 
if #unresolved == 0 then
unresolvedList:tag('div')
:addClass('feedback-empty')
:wikitext(
'No unresolved feedback.'
)
else
for _, entry in ipairs(unresolved) do
unresolvedList:wikitext(
renderCompactCard(entry)
)
end
end
 
--------------------------------------------------------
-- Resolved
--------------------------------------------------------
 
local resolvedList =
root:tag('div')
:addClass(
'feedback-page-list'
)
: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
 
--------------------------------------------------------
-- All
--------------------------------------------------------
 
local allList =
root:tag('div')
:addClass(
'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
 
return tostring(root)
end
 
 
------------------------------------------------------------
-- COUNTS
------------------------------------------------------------
 
function p.counts(frame)
local entries =
queryFeedback(
currentSubjectPage()
)
 
local unresolved = 0
local resolved = 0
 
for _, entry in ipairs(entries) do
if trim(entry.status):lower()
== 'resolved'
then
resolved = resolved + 1
else
unresolved = unresolved + 1
end
end
 
return tostring(
mw.html.create('span')
:addClass('feedback-count-data')
:attr(
'data-unresolved',
unresolved
)
:attr(
'data-resolved',
resolved
)
)
end
 
 
------------------------------------------------------------
-- TABLE / DASHBOARD
------------------------------------------------------------
 
local function renderTable(entries)
if not entries or #entries == 0 then
return '<em>No feedback entries found.</em>'
end
 
local table =
mw.html.create('table')
:addClass('feedback-table')
 
local head =
table:tag('thead')
:tag('tr')
 
head:tag('th')
:wikitext('Status')
 
head:tag('th')
:wikitext('Page')
 
head:tag('th')
:wikitext('Feedback')
 
head:tag('th')
:wikitext('User')
 
head:tag('th')
:wikitext('Date')
 
local body =
table:tag('tbody')
 
for _, entry in ipairs(entries) do
local row =
body:tag('tr')
 
local resolved =
trim(entry.status):lower()
== 'resolved'
 
row:tag('td')
:addClass(
resolved
and 'feedback-table-status--resolved'
or 'feedback-table-status--unresolved'
)
:wikitext(
resolved
and 'Resolved'
or 'Unresolved'
)
 
row:tag('td')
:addClass('feedback-table-page')
:wikitext(
entry.page or ''
)
 
row:tag('td')
:addClass('feedback-table-text')
:wikitext(
entry.feedback or ''
)
 
row:tag('td')
:wikitext(
formatUser(entry.user)
)
 
row:tag('td')
:wikitext(
entry.createddate or ''
)
end
 
return tostring(table)
end
 
 
function p.view(frame)
local parent =
frame:getParent()
 
local args
 
if frame
and frame.args
and frame.args.status
then
args = frame.args
 
elseif parent
and parent.args
and parent.args.status
then
args = parent.args
 
elseif frame
and frame.args
then
args = frame.args


control
elseif parent
:addClass('feedback-resolve-button--compact')
and parent.args
:attr('aria-label', 'Resolve feedback')
then
:attr('title', 'Resolve feedback')
args = parent.args
:wikitext('✓')


else
else
args = {}
end


control
return p._view(
:attr('role', 'button')
args,
:attr('tabindex', '0')
frame
:attr('aria-label', 'Resolve feedback')
)
:wikitext('Resolve')
end
 
 
function p._view(args, frame)
local bucket =
getBucket()
 
if not bucket then
return
'<em>Bucket extension is not available.</em>'
end
 
local rawStatus =
args.status
or args[1]
 
local filterStatus =
rawStatus
and trim(rawStatus):lower()
or nil
 
--------------------------------------------------------
-- Query
--------------------------------------------------------
 
local query =
bucket
 
query.select(
'page',
'user',
'status',
'feedback',
'id',
'createddate',
'link',
'image',
'resolvedby',
'resolveddate'
)
 
if filterStatus == 'resolved'
or filterStatus == 'unresolved'
then
query.where(
'status',
filterStatus
)
end
 
local success, entries =
pcall(function()
return query.run()
end)
 
if not success or not entries then
return
'<em>Could not load feedback.</em>'
end
 
entries =
sortNewestFirst(entries)
 
--------------------------------------------------------
-- Specific status
--------------------------------------------------------


if filterStatus == 'resolved'
or filterStatus == 'unresolved'
then
return renderFeedbackList(
entries
)
end
end
--------------------------------------------------------
-- All
--------------------------------------------------------
return renderTable(entries)
end
end
p.table = p.view
return p