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 1: | Line 1: | ||
local p = {} | local p = {} | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- 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 == ' | return value ~= '' | ||
and value ~= '0' | |||
and value ~= 'false' | |||
and value ~= 'no' | |||
and value ~= 'off' | |||
end | end | ||
local function trim(value) | local function trim(value) | ||
return mw.text.trim(tostring(value)) | return mw.text.trim( | ||
tostring(value or '') | |||
) | |||
end | end | ||
local function formatUser(user) | local function formatUser(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() | |||
return title.subjectPageTitle.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 | ||
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( | local function sortNewestFirst(results) | ||
if not results then | |||
return {} | |||
end | |||
table.sort( | table.sort( | ||
results, | |||
function(a, b) | function(a, b) | ||
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 | |||
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 | ||
return sortNewestFirst( | |||
results or {} | |||
) | ) | ||
end | |||
' | |||
) | ------------------------------------------------------------ | ||
-- 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 | |||
meta:tag('span') | |||
:addClass( | |||
'feedback-meta-date' | |||
) | |||
:wikitext( | |||
' · ' .. | |||
mw.text.nowiki( | |||
entry.createddate | |||
) | |||
) | |||
end | end | ||
end | end | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Attachments | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
local function renderAttachments(entry) | local function renderAttachments( | ||
local | root, | ||
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' | |||
) | |||
if link ~= '' then | if link ~= '' then | ||
attachments:tag('span') | |||
:addClass( | |||
'feedback-attachment' | |||
) | |||
:wikitext( | |||
'🔗 ' .. | |||
'[' .. | |||
link .. | |||
' Link]' | |||
) | |||
end | end | ||
if image ~= '' then | if image ~= '' then | ||
local | local imageAttachment = | ||
attachments:tag('span') | |||
:addClass( | |||
'feedback-attachment' | |||
) | |||
imageAttachment:tag('span') | |||
:addClass( | |||
'feedback-image-icon' | |||
) | |||
:wikitext('▣') | |||
imageAttachment:tag('span') | |||
:addClass( | |||
'feedback-image-link' | |||
) | |||
:wikitext( | |||
'[[Media:' .. | |||
image .. | |||
'|Picture]]' | |||
'[ | |||
']' | |||
) | ) | ||
end | end | ||
end | end | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Resolution information | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
local function renderResolution( | local function renderResolution( | ||
local resolvedBy = trim(entry.resolvedby) | root, | ||
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 = | ||
root:tag('div') | |||
:addClass('feedback-resolution') | :addClass( | ||
'feedback-resolution' | |||
) | |||
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 | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
| Line 219: | Line 480: | ||
compact | compact | ||
) | ) | ||
if | |||
if not id | |||
or id == '' | |||
then | |||
return | return | ||
end | end | ||
local control = | local control = | ||
parent:tag('span') | |||
:addClass('feedback-resolve-button' | :addClass( | ||
'feedback-resolve-button' | |||
) | ) | ||
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 | |||
control | control | ||
: | :attr( | ||
' | 'role', | ||
'button' | |||
) | |||
:attr( | |||
'tabindex', | |||
'0' | |||
) | ) | ||
:attr( | :attr( | ||
' | 'aria-label', | ||
'Resolve feedback' | 'Resolve feedback' | ||
) | ) | ||
:wikitext( | :wikitext( | ||
'Resolve' | |||
) | |||
end | end | ||
end | end | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Full feedback card | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
local function renderFullCard(entry) | local function renderFullCard( | ||
entry | |||
) | |||
local resolved = | local resolved = | ||
entry.status == 'resolved' | |||
local id = | |||
trim(entry.id) | |||
local page = | |||
trim(entry.page) | |||
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 | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
| Line 310: | Line 611: | ||
local main = | local main = | ||
root:tag('div') | |||
:addClass('feedback-main-line') | :addClass( | ||
'feedback-main-line' | |||
) | |||
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 ' | and 'RESOLVED' | ||
or ' | or 'UNRESOLVED' | ||
) | ) | ||
main:tag('span') | main:tag('span') | ||
:addClass('feedback-text') | :addClass( | ||
'feedback-text' | |||
) | |||
:wikitext( | :wikitext( | ||
entry.feedback | |||
or 'No feedback provided' | or 'No feedback provided' | ||
) | ) | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Inline metadata | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
local | 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(user) | formatUser( | ||
entry.user | |||
) | |||
) | ) | ||
end | end | ||
if createddate ~= '' then | |||
if entry.createddate | |||
' | and entry.createddate ~= '' | ||
then | |||
metadata:wikitext( | |||
' ' .. | |||
mw.text.nowiki( | |||
entry.createddate | |||
) | |||
) | ) | ||
end | end | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Resolve button | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
if not resolved | |||
and id ~= '' | |||
then | |||
renderResolveControl( | |||
main, | |||
id, | |||
page, | |||
false | |||
) | |||
end | |||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Attachments | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
renderAttachments( | |||
root, | |||
entry | entry | ||
) | ) | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Resolution | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
if | if resolved then | ||
renderResolution( | |||
root, | |||
entry | |||
) | |||
end | end | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Missing ID | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
if not resolved then | if not resolved | ||
and id == '' | |||
then | |||
root:tag('div') | |||
:addClass( | |||
'feedback-missing-id' | |||
) | |||
:wikitext( | |||
'Add an ID to enable the Resolve button.' | |||
) | |||
end | end | ||
return tostring(root) | return tostring(root) | ||
end | end | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Compact article card | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
local function renderCompactCard(entry) | local function renderCompactCard( | ||
entry | |||
) | |||
local resolved = | |||
entry.status == 'resolved' | |||
local id = | |||
trim(entry.id) | |||
local page = | |||
trim(entry.page) | |||
-- | local root = | ||
mw.html.create('div') | |||
-------- | :addClass( | ||
'feedback-page-card' | |||
) | |||
:addClass( | |||
resolved | |||
and 'feedback-page-card--resolved' | |||
or 'feedback-page-card--unresolved' | |||
) | |||
if | if id ~= '' then | ||
root:attr( | |||
'data-feedback-id', | |||
id | |||
) | |||
id | |||
end | end | ||
local content = | |||
root:tag('div') | |||
:addClass( | |||
'feedback-page-card-content' | |||
) | |||
local firstLine = | |||
content:tag('div') | |||
:addClass( | |||
'feedback-page-card-line' | |||
) | |||
firstLine:tag('span') | |||
:addClass( | |||
'feedback-status' | |||
) | |||
:addClass( | |||
resolved | |||
and 'feedback-status--resolved' | |||
or 'feedback-status--unresolved' | |||
) | |||
:wikitext( | |||
resolved | |||
and 'RESOLVED' | |||
or 'UNRESOLVED' | |||
) | |||
firstLine:tag('span') | |||
:addClass( | |||
'feedback-page-card-text' | |||
) | |||
:wikitext( | |||
entry.feedback | |||
or 'No feedback provided' | |||
) | |||
-------------------------------------------------------- | |||
-- Metadata | |||
-------------------------------------------------------- | |||
local meta = | |||
content:tag('div') | |||
:addClass( | |||
'feedback-page-card-meta' | |||
) | |||
if | if entry.user | ||
and entry.user ~= '' | |||
then | |||
meta:wikitext( | |||
formatUser( | |||
entry.user | |||
) | |||
) | |||
end | end | ||
if entry.createddate | |||
and entry.createddate ~= '' | |||
then | |||
meta:wikitext( | |||
' · ' .. | |||
mw.text.nowiki( | |||
entry.createddate | |||
) | |||
) | |||
end | end | ||
local | -------------------------------------------------------- | ||
-- Resolution | |||
-------------------------------------------------------- | |||
if resolved then | |||
local resolvedBy = | |||
trim(entry.resolvedby) | |||
local resolvedDate = | |||
trim(entry.resolveddate) | |||
if resolvedBy ~= '' | |||
or resolvedDate ~= '' | |||
then | |||
local resolution = | |||
content:tag('div') | |||
:addClass( | |||
'feedback-page-card-resolution' | |||
) | |||
resolution:wikitext( | |||
'↳ Resolved by ' | |||
) | ) | ||
if resolvedBy ~= '' then | |||
resolution:wikitext( | |||
formatUser( | |||
resolvedBy | |||
) | |||
) | |||
else | |||
resolution:wikitext( | |||
'Unknown' | |||
) | |||
end | |||
if resolvedDate ~= '' then | |||
resolution:wikitext( | |||
' · ' .. | |||
mw.text.nowiki( | |||
resolvedDate | |||
) | |||
) | |||
end | |||
end | |||
else | else | ||
if id ~= '' then | |||
renderResolveControl( | |||
root, | |||
id, | |||
page, | |||
true | |||
) | |||
end | |||
end | end | ||
-------------------------------------------------------- | |||
-- Attachments | |||
-------------------------------------------------------- | |||
renderAttachments( | |||
content, | |||
entry | |||
) | ) | ||
return tostring(root) | |||
end | end | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Page feedback section | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
function p.page(frame) | function p.page(frame) | ||
local page = | local page = | ||
currentSubjectPage() | currentSubjectPage() | ||
local | |||
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 _, | |||
if | for _, row in ipairs(allEntries) do | ||
if row.status == 'resolved' then | |||
table.insert( | table.insert( | ||
resolved, | resolved, | ||
row | |||
) | ) | ||
else | else | ||
table.insert( | table.insert( | ||
unresolved, | unresolved, | ||
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' | ||
) | ) | ||
: | :attr( | ||
'data-unresolved-count', | |||
#unresolved | |||
) | |||
:attr( | |||
'data-resolved-count', | |||
#resolved | |||
) | |||
-------------------------------------------------------- | -------------------------------------------------------- | ||
| Line 674: | Line 1,034: | ||
root:tag('h2') | root:tag('h2') | ||
:addClass('feedback-page- | :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 | |||
local unresolvedFilter = | |||
filters:tag('span') | |||
:addClass( | |||
'feedback-page-filter' | |||
) | |||
:addClass( | |||
'feedback-page-filter--active' | |||
) | |||
:attr( | :attr( | ||
' | 'role', | ||
'button' | 'button' | ||
) | |||
:attr( | |||
'tabindex', | |||
'0' | |||
) | ) | ||
:attr( | :attr( | ||
'data-feedback-filter', | 'data-feedback-filter', | ||
'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( | ||
'feedback-page-filter-count' | |||
) | |||
:wikitext( | |||
tostring( | |||
#resolved | |||
) | |||
) | ) | ||
end | end | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- Unresolved | -- Unresolved list | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
| Line 738: | Line 1,142: | ||
'feedback-page-list--unresolved' | 'feedback-page-list--unresolved' | ||
) | ) | ||
: | |||
if #unresolved > 0 then | |||
for _, row in ipairs(unresolved) do | |||
unresolvedList:node( | |||
renderCompactCard( | |||
row | |||
) | |||
) | ) | ||
end | |||
else | |||
unresolvedList:tag('div') | unresolvedList:tag('div') | ||
:addClass('feedback-empty') | :addClass( | ||
'feedback-page-empty' | |||
) | |||
:wikitext( | :wikitext( | ||
'No unresolved feedback.' | 'No unresolved feedback.' | ||
) | ) | ||
end | end | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- Resolved | -- Resolved list | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
local resolvedList = | if #resolved > 0 then | ||
local resolvedList = | |||
root:tag('div') | |||
:addClass( | |||
'feedback-page-list' | |||
) | |||
:addClass( | |||
'feedback-page-list--resolved' | |||
) | |||
for _, row in ipairs(resolved) do | |||
resolvedList:node( | |||
renderCompactCard( | |||
: | row | ||
) | |||
) | ) | ||
end | end | ||
end | end | ||
return tostring(root) | return tostring(root) | ||
end | end | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Feedback counts | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
function p.counts(frame) | function p.counts(frame) | ||
local page = | |||
currentSubjectPage() | |||
local entries = | local entries = | ||
queryFeedback( | queryFeedback( | ||
page | |||
) | ) | ||
if not entries then | |||
return '' | |||
end | |||
local unresolved = 0 | local unresolved = 0 | ||
local resolved = 0 | local resolved = 0 | ||
for _, | |||
if | for _, row in ipairs(entries) do | ||
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 | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
-- | -- Main / individual feedback | ||
------------------------------------------------------------ | ------------------------------------------------------------ | ||
local | function p.main(frame) | ||
return '< | 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' | |||
) | |||
header:tag('th') | |||
:wikitext( | |||
'Feedback' | |||
) | |||
if includeResolved then | |||
header:tag('th') | |||
:wikitext( | |||
'Resolved by' | |||
) | |||
header:tag('th') | |||
:wikitext( | |||
'Resolved date' | |||
) | |||
end | |||
if #entries == 0 then | |||
local row = | local row = | ||
tableRoot:tag('tr') | |||
local colspan = | |||
includeResolved | |||
and '7' | |||
or '4' | |||
row:tag('td') | row:tag('td') | ||
:attr( | |||
'colspan', | |||
colspan | |||
) | |||
:addClass( | :addClass( | ||
'feedback-table-empty' | |||
) | ) | ||
:wikitext( | :wikitext( | ||
'No feedback entries found.' | |||
) | ) | ||
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 | '[[' .. | ||
entry.page .. | |||
']]' | |||
) | ) | ||
row:tag('td') | row:tag('td') | ||
:addClass('feedback-table- | :addClass( | ||
'feedback-table-user' | |||
) | |||
:wikitext( | :wikitext( | ||
entry. | formatUser( | ||
entry.user | |||
) | |||
) | ) | ||
row:tag('td') | row:tag('td') | ||
:addClass( | |||
'feedback-table-date' | |||
) | |||
:wikitext( | :wikitext( | ||
mw.text.nowiki( | |||
entry.createddate | |||
or '—' | |||
) | |||
) | ) | ||
row:tag('td') | row:tag('td') | ||
:addClass( | |||
'feedback-table-feedback' | |||
) | |||
:wikitext( | :wikitext( | ||
entry. | 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( | |||
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 = | |||
and | parent | ||
and parent.args | |||
or frame.args | |||
args | |||
local status = | |||
trim( | |||
args.status or '' | |||
):lower() | |||
-------------------------------------------------------- | |||
-- Specific table | |||
-------------------------------------------------------- | |||
if status == 'resolved' | |||
or status == 'unresolved' | |||
then | then | ||
local entries = | |||
queryFeedback( | |||
nil, | |||
status | |||
) | |||
if not entries then | |||
return '' | |||
end | |||
end | |||
return renderTable( | |||
entries, | |||
false | |||
) | |||
end | end | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
-- | -- Dashboard | ||
-------------------------------------------------------- | -------------------------------------------------------- | ||
local | local unresolved = | ||
queryFeedback( | |||
nil, | |||
'unresolved' | |||
) | |||
local all = | |||
queryFeedback( | |||
nil, | |||
'all' | |||
) | ) | ||
if not unresolved then | |||
unresolved = {} | |||
end | end | ||
if not | if not all then | ||
all = {} | |||
end | end | ||
--- | local root = | ||
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' | |||
) | |||
root:node( | |||
renderTable( | |||
all, | |||
true | |||
) | ) | ||
) | |||
return tostring( | |||
root | |||
) | |||
end | end | ||
------------------------------------------------------------ | |||
-- Aliases | |||
------------------------------------------------------------ | |||
p.table = p.view | p.table = p.view | ||
return p | return p | ||