Editing Module:Feedback
Appearance
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 2: | Line 2: | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Helpers | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
local function isTruthy( | local function isTruthy(v) | ||
if | if v == nil then | ||
return false | return false | ||
end | end | ||
v = mw.text.trim(tostring(v)):lower() | |||
return | return v ~= '' | ||
and v ~= '0' | |||
and v ~= 'false' | |||
and v ~= 'no' | |||
and v ~= 'off' | |||
end | end | ||
local function formatUser(user) | local function formatUser(user) | ||
user = trim(user) | user = mw.text.trim(tostring(user or '')) | ||
if user == '' then | if user == '' or user:lower() == 'anonymous' then | ||
return 'Anonymous' | return 'Anonymous' | ||
end | end | ||
| Line 40: | Line 32: | ||
user | user | ||
) | ) | ||
end | end | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Feedback card | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
local function | local function renderCard(entry, showInstruction) | ||
local | local resolved = (entry.status == 'resolved') | ||
local user = entry.user or 'Anonymous' | |||
local feedback = entry.feedback or 'No feedback provided' | |||
local page = entry.page | |||
local | local root = mw.html.create('div') | ||
:addClass('feedback-box') | |||
:addClass( | |||
resolved | |||
and 'feedback-box--resolved' | |||
or 'feedback-box--unresolved' | |||
) | ) | ||
-------------------------------------------------------- | |||
-- Metadata | |||
-------------------------------------------------------- | |||
local meta = root:tag('div') | |||
:addClass('feedback-meta') | |||
if page and page ~= '' then | |||
meta:tag('span') | |||
:addClass('feedback-page') | |||
:wikitext( | |||
string.format('[[%s]]', page) | |||
:addClass('feedback- | |||
) | ) | ||
meta:tag('span') | |||
:addClass('feedback-separator') | |||
:addClass('feedback- | :wikitext(' • ') | ||
: | |||
end | end | ||
meta:tag('span') | |||
:addClass('feedback-user') | |||
:wikitext(formatUser(user)) | |||
meta:tag('span') | |||
:addClass('feedback-separator') | |||
:wikitext(' • ') | |||
meta:tag('span') | |||
:addClass('feedback-status') | :addClass('feedback-status') | ||
:addClass( | :addClass( | ||
| Line 329: | Line 101: | ||
or 'Unresolved' | or 'Unresolved' | ||
) | ) | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
| Line 334: | Line 107: | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
root:tag('div') | |||
:addClass('feedback- | :addClass('feedback-body') | ||
:wikitext( | :wikitext(feedback) | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Resolution instruction | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
if showInstruction and not resolved then | |||
root:tag('div') | |||
:addClass('feedback-instruction') | |||
:addClass('feedback- | |||
:wikitext( | :wikitext( | ||
' | 'If this feedback has been resolved, edit this page ' | ||
.. 'and set <code>|resolved=true</code>.' | |||
) | ) | ||
end | end | ||
return tostring(root) | return tostring(root) | ||
| Line 413: | Line 133: | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Save feedback | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
local function | local function saveFeedback(data) | ||
if not (mw.ext and mw.ext.bucket) then | |||
return false, 'Bucket extension is not available' | |||
end | |||
local bucket = mw.ext.bucket('feedback') | |||
local bucket = | |||
if not bucket then | if not bucket then | ||
return false, ' | return false, 'Feedback bucket could not be opened' | ||
end | end | ||
local success, err = pcall(function() | local success, err = pcall(function() | ||
bucket.put({ | bucket.put({ | ||
page = data.page, | page = data.page, | ||
user = data.user, | user = data.user, | ||
status = data.status, | status = data.status, | ||
feedback = data.feedback | feedback = data.feedback | ||
}) | }) | ||
end) | end) | ||
if not success then | if not success then | ||
return false, tostring(err) | return false, tostring(err) | ||
end | end | ||
return true | return true | ||
| Line 456: | Line 172: | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Main | ||
-- | -- | ||
-- Used by Template:Feedback | -- Used by Template:Feedback | ||
-- | |||
-- Example: | |||
-- | |||
-- {{Feedback | |||
-- |feedback=Something could be improved here. | |||
-- |user=Rei | |||
-- |resolved=false | |||
-- }} | |||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
function p.main(frame) | function p.main(frame) | ||
local parent = frame:getParent() | local parent = frame:getParent() | ||
| Line 467: | Line 192: | ||
parent and parent.args | parent and parent.args | ||
or frame.args | or frame.args | ||
-------------------------------------------------------- | |||
-- Get page title | |||
-------------------------------------------------------- | |||
local pageTitle = nil | local pageTitle = nil | ||
if parent and parent.getTitle then | if parent and parent.getTitle then | ||
local title = parent:getTitle() | local title = parent:getTitle() | ||
| Line 479: | Line 210: | ||
pageTitle = title | pageTitle = title | ||
end | end | ||
end | end | ||
return p._main( | |||
return p._main(args, pageTitle) | |||
end | end | ||
function p._main(args, pageTitle) | function p._main(args, pageTitle) | ||
local feedback = | local feedback = | ||
args.feedback | |||
or 'No feedback provided' | |||
local user = | local user = | ||
args.user | |||
or 'Anonymous' | |||
local resolved = | local resolved = | ||
isTruthy(args.resolved) | isTruthy(args.resolved) | ||
local status = | local status = | ||
| Line 510: | Line 239: | ||
and 'resolved' | and 'resolved' | ||
or 'unresolved' | or 'unresolved' | ||
local currentPage = | local currentPage = | ||
pageTitle | |||
or mw.title.getCurrentTitle().fullText | |||
-------------------------------------------------------- | |||
-- Save to Bucket | |||
-------------------------------------------------------- | |||
local | local saved, saveError = saveFeedback({ | ||
page = currentPage, | |||
user = user, | |||
status = status, | |||
feedback = feedback | |||
}) | |||
if not saved then | |||
return '<strong style="color:red;">Feedback bucket error: ' | |||
.. mw.text.nowiki(saveError or 'Unknown error') | |||
.. '</strong>' | |||
end | |||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Render feedback card | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
local | local card = renderCard({ | ||
page = nil, | |||
user = user, | |||
status = status, | |||
feedback = feedback | |||
}, true) | |||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Categorize feedback | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
| Line 589: | Line 291: | ||
if resolved then | if resolved then | ||
category = | category = | ||
'[[Category:Resolved Feedback]]' | '[[Category:Resolved Feedback]]' | ||
else | else | ||
category = | category = | ||
'[[Category:Unresolved Feedback]]' | '[[Category:Unresolved Feedback]]' | ||
end | end | ||
return card .. category | return card .. category | ||
end | end | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Render feedback list | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
local function renderFeedbackList(results) | local function renderFeedbackList(results) | ||
if not results or #results == 0 then | if not results or #results == 0 then | ||
return '<em>No feedback entries found.</em>' | return '<em>No feedback entries found.</em>' | ||
end | end | ||
local list = {} | local list = {} | ||
for _, row in ipairs(results) do | for _, row in ipairs(results) do | ||
table.insert( | table.insert( | ||
list, | list, | ||
renderCard(row, false) | |||
) | ) | ||
end | end | ||
return table.concat( | |||
return table.concat(list, '\n') | |||
end | end | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- View feedback | ||
-- | |||
-- Examples: | |||
-- | |||
-- {{#invoke:Feedback|view}} | |||
-- | |||
-- {{#invoke:Feedback|view|status=all}} | |||
-- | |||
-- {{#invoke:Feedback|view|status=resolved}} | |||
-- | |||
-- {{#invoke:Feedback|view|status=unresolved}} | |||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
function p. | function p.view(frame) | ||
local | |||
local parent = frame:getParent() | |||
local | 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 | end | ||
return p._view(args, frame) | |||
end | |||
function p._view(args, frame) | |||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Check Bucket | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
if not (mw.ext and mw.ext.bucket) then | |||
return | |||
'<em>Bucket extension is not available.</em>' | |||
end | end | ||
frame = frame or mw.getCurrentFrame() | |||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Determine requested status | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
local | local rawStatus = | ||
args.status | |||
or args[1] | |||
local filterStatus = | |||
rawStatus | |||
and mw.text.trim(rawStatus):lower() | |||
or nil | |||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Specific status | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
local | if filterStatus == 'resolved' | ||
or filterStatus == 'unresolved' | |||
then | |||
) | local query = | ||
mw.ext.bucket('feedback') | |||
) | |||
query.select( | |||
'page', | |||
'user', | |||
'status', | |||
'feedback' | |||
) | |||
query.where( | |||
'status', | |||
filterStatus | |||
) | |||
return renderFeedbackList( | |||
query.run() | |||
) | |||
end | end | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- All | -- All feedback | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
if filterStatus == 'all' then | |||
local query = | |||
mw.ext.bucket('feedback') | |||
query.select( | |||
'page', | |||
'user', | |||
'status', | |||
'feedback' | |||
) | |||
return renderFeedbackList( | |||
query.run() | |||
) | ) | ||
end | |||
-------------------------------------------------------- | |||
-- Tabber mode | |||
-- | |||
-- Default: | |||
-- | |||
-- All | |||
-- Unresolved | |||
-- Resolved | |||
-------------------------------------------------------- | |||
local qAll = | |||
mw.ext.bucket('feedback') | |||
qAll.select( | |||
'page', | |||
'user', | |||
'status', | |||
'feedback' | |||
) | |||
local | local allEntries = | ||
qAll.run() | |||
local allList = {} | |||
local unresolvedList = {} | |||
local resolvedList = {} | |||
-------------------------------------------------------- | |||
-- Split entries by status | |||
-------------------------------------------------------- | |||
if allEntries and #allEntries > 0 then | |||
for _, row in ipairs(allEntries) do | |||
local card = | |||
renderCard(row, false) | |||
table.insert( | |||
allList, | |||
card | |||
) | ) | ||
if row.status == 'resolved' then | |||
table.insert( | |||
resolvedList, | |||
card | |||
) | |||
else | |||
table.insert( | |||
unresolvedList, | |||
card | |||
) | |||
end | |||
end | |||
end | |||
end | |||
-------------------------------------------------------- | |||
-- Build tab contents | |||
-------------------------------------------------------- | |||
local allContent | |||
if #allList > 0 then | |||
allContent = | |||
table.concat(allList, '\n') | |||
else | else | ||
allContent = | |||
'<em>No feedback entries found.</em>' | |||
end | end | ||
local unresolvedContent | |||
if #unresolvedList > 0 then | |||
unresolvedContent = | |||
table.concat(unresolvedList, '\n') | |||
else | |||
unresolvedContent = | |||
'<em>No unresolved feedback entries found.</em>' | |||
end | end | ||
local | local resolvedContent | ||
if #resolvedList > 0 then | |||
resolvedContent = | |||
table.concat(resolvedList, '\n') | |||
else | |||
resolvedContent = | |||
'<em>No resolved feedback entries found.</em>' | |||
end | end | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Tabber | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
local tabberBody = string.format( | |||
'All=\n%s\n|-|Unresolved=\n%s\n|-|Resolved=\n%s', | |||
allContent, | |||
unresolvedContent, | |||
resolvedContent | |||
) | |||
return frame:callParserFunction( | |||
'#tag', | |||
{ | |||
'tabber', | |||
tabberBody | |||
} | |||
) | |||
end | end | ||
return p | return p | ||