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.

Editing Module:Feedback

From The Petit Planet Wiki
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 = {}
------------------------------------------------------------
------------------------------------------------------------
-- HELPERS
-- Resolve control
------------------------------------------------------------
 
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 216: Line 6:
parent,
parent,
id,
id,
page,
compact,
compact
page
)
)
if trim(id) == '' then
 
if not id or id == '' then
return
return
end
end
local wrapper =
parent:tag('div')
:addClass('feedback-resolve')


local control =
local control =
wrapper:tag('span')
parent:tag('span')
:addClass('feedback-resolve-button')
:addClass('feedback-resolve-button')
:attr(
:attr(
'data-feedback-id',
'data-feedback-id',
id
id
)
:attr(
'data-feedback-page',
page or ''
)
:attr(
'role',
'button'
)
:attr(
'tabindex',
'0'
)
:attr(
'aria-label',
'Resolve feedback'
)
)


if compact then
if page and page ~= '' then
control
control:attr(
:addClass(
'data-feedback-page',
'feedback-resolve-button--compact'
page
)
: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
end


--------------------------------------------------------
if compact then
-- Main content column
--------------------------------------------------------


local content =
control
root:tag('div')
:addClass('feedback-resolve-button--compact')
:addClass('feedback-content')
:attr('aria-label', 'Resolve feedback')
 
:attr('title', 'Resolve feedback')
--------------------------------------------------------
:wikitext('✓')
-- 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 not resolved then
renderResolveControl(
root,
id,
page,
false
)
end
 
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
else
category =
'[[Category:Unresolved Feedback]]'
end


if id == '' then
control
category =
:attr('role', 'button')
category ..
:attr('tabindex', '0')
'[[Category:Feedbacks Missing ID]]'
:attr('aria-label', 'Resolve feedback')
end
:wikitext('Resolve')
 
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
end
return tostring(table)
end
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
elseif parent
and parent.args
then
args = parent.args
else
args = {}
end
return p._view(
args,
frame
)
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
--------------------------------------------------------
-- All
--------------------------------------------------------
return renderTable(entries)
end
p.table = p.view
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;