|
|
| (19 intermediate revisions by the same user not shown) |
| Line 1: |
Line 1: |
| local p = {}
| |
|
| |
|
| local function isTruthy(v)
| |
| if v == nil then
| |
| return false
| |
| end
| |
| v = mw.text.trim(tostring(v)):lower()
| |
| return v ~= '' and v ~= '0' and v ~= 'false' and v ~= 'no' and v ~= 'off'
| |
| end
| |
|
| |
| local function formatUser(user)
| |
| user = mw.text.trim(tostring(user or ''))
| |
| if user == '' or user:lower() == 'anonymous' then
| |
| return 'Anonymous'
| |
| end
| |
| return string.format('[[User:%s|%s]]', user, user)
| |
| end
| |
|
| |
| -- Renders the individual feedback visual card
| |
| local function renderCard(user, feedback, resolved, pageName)
| |
| local root = mw.html.create('div')
| |
| :addClass('feedback-box')
| |
| :addClass(resolved and 'feedback-box--resolved' or 'feedback-box--unresolved')
| |
|
| |
| local meta = root:tag('div')
| |
| :addClass('feedback-meta')
| |
|
| |
| if pageName and pageName ~= '' then
| |
| meta:tag('span')
| |
| :addClass('feedback-page')
| |
| :wikitext(string.format("'''[[%s]]'''", pageName))
| |
| meta:tag('span')
| |
| :addClass('feedback-separator')
| |
| :wikitext(' • ')
| |
| end
| |
|
| |
| meta:tag('span')
| |
| :addClass('feedback-user')
| |
| :wikitext(formatUser(user))
| |
|
| |
| meta:tag('span')
| |
| :addClass('feedback-separator')
| |
| :wikitext(' • ')
| |
|
| |
| meta:tag('span')
| |
| :addClass('feedback-status')
| |
| :addClass(resolved and 'feedback-status--resolved' or 'feedback-status--unresolved')
| |
| :wikitext(resolved and 'Resolved' or 'Unresolved')
| |
|
| |
| root:tag('div')
| |
| :addClass('feedback-body')
| |
| :wikitext(feedback)
| |
|
| |
| if not resolved and (not pageName or pageName == '') then
| |
| root:tag('div')
| |
| :addClass('feedback-instruction')
| |
| :wikitext('If this feedback has been resolved, edit this page and set <code>|resolved=true</code>.')
| |
| end
| |
|
| |
| return tostring(root)
| |
| end
| |
|
| |
| -- =========================================================================
| |
| -- 1. ARTICLE INGESTION & DISPLAY: {{#invoke:Feedback|main|...}}
| |
| -- =========================================================================
| |
| function p.main(frame)
| |
| local parent = frame:getParent()
| |
| local args = parent and parent.args or frame.args
| |
| return p._main(args)
| |
| end
| |
|
| |
| function p._main(args)
| |
| local feedback = args.feedback or "No feedback provided"
| |
| local user = args.user or "Anonymous"
| |
| local resolved = isTruthy(args.resolved)
| |
| local statusStr = resolved and 'resolved' or 'unresolved'
| |
| local currentPage = mw.title.getCurrentTitle().fullText
| |
|
| |
| -- Ingest to Bucket
| |
| if mw.ext and mw.ext.bucket and mw.ext.bucket.put then
| |
| mw.ext.bucket.put('feedback', {
| |
| page = currentPage,
| |
| user = user,
| |
| status = statusStr,
| |
| feedback = feedback
| |
| })
| |
| elseif mw.ext and mw.ext.bucket then
| |
| pcall(function()
| |
| mw.ext.bucket('feedback').put({
| |
| page = currentPage,
| |
| user = user,
| |
| status = statusStr,
| |
| feedback = feedback
| |
| })
| |
| end)
| |
| end
| |
|
| |
| local card = renderCard(user, feedback, resolved, nil)
| |
| local category = resolved and '[[Category:Resolved Feedback]]' or '[[Category:Unresolved Feedback]]'
| |
| return card .. category
| |
| end
| |
|
| |
| -- =========================================================================
| |
| -- 2. DASHBOARD QUERY WITH TABBER: {{#invoke:Feedback|dashboard}}
| |
| -- =========================================================================
| |
| function p.dashboard(frame)
| |
| local rows = nil
| |
|
| |
| -- Method 1: mw.ext.bucket:select() syntax (common Scribunto Bucket API)
| |
| if not rows and mw.ext and mw.ext.bucket and type(mw.ext.bucket) == 'function' then
| |
| pcall(function()
| |
| rows = mw.ext.bucket('feedback'):select('page', 'user', 'status', 'feedback'):run()
| |
| end)
| |
| end
| |
|
| |
| -- Method 2: mw.ext.bucket.query() or mw.ext.bucket.select() direct table syntax
| |
| if not rows and mw.ext and mw.ext.bucket then
| |
| if type(mw.ext.bucket.query) == 'function' then
| |
| pcall(function()
| |
| rows = mw.ext.bucket.query('feedback', {
| |
| fields = { 'page', 'user', 'status', 'feedback' }
| |
| })
| |
| end)
| |
| elseif type(mw.ext.bucket.select) == 'function' then
| |
| pcall(function()
| |
| rows = mw.ext.bucket.select('feedback', { 'page', 'user', 'status', 'feedback' })
| |
| end)
| |
| end
| |
| end
| |
|
| |
| -- Method 3: Global mw.bucket syntax
| |
| if not rows and mw.bucket then
| |
| pcall(function()
| |
| if type(mw.bucket) == 'function' then
| |
| rows = mw.bucket('feedback'):select('page', 'user', 'status', 'feedback'):run()
| |
| elseif type(mw.bucket.query) == 'function' then
| |
| rows = mw.bucket.query('feedback', {
| |
| fields = { 'page', 'user', 'status', 'feedback' }
| |
| })
| |
| end
| |
| end)
| |
| end
| |
|
| |
| rows = rows or {}
| |
|
| |
| local allCards = {}
| |
| local unresolvedCards = {}
| |
| local resolvedCards = {}
| |
|
| |
| for _, row in ipairs(rows) do
| |
| -- Support both string keys and positional keys
| |
| local pageName = row.page or row[1] or ''
| |
| local user = row.user or row[2] or 'Anonymous'
| |
| local status = tostring(row.status or row[3] or 'unresolved'):lower()
| |
| local feedbackText = row.feedback or row[4] or ''
| |
| local isResolved = status == 'resolved' or status == 'true' or status == '1'
| |
|
| |
| local card = renderCard(user, feedbackText, isResolved, pageName)
| |
|
| |
| table.insert(allCards, card)
| |
| if isResolved then
| |
| table.insert(resolvedCards, card)
| |
| else
| |
| table.insert(unresolvedCards, card)
| |
| end
| |
| end
| |
|
| |
| local function getListHtml(cards, emptyMsg)
| |
| if #cards == 0 then
| |
| return string.format('<div class="feedback-empty" style="padding: 12px; color: #72777d; font-style: italic;">%s</div>', emptyMsg)
| |
| end
| |
| return table.concat(cards, '\n\n')
| |
| end
| |
|
| |
| -- Construct the raw wikitext for the <tabber> extension
| |
| local tabberWikitext = string.format(
| |
| '<tabber>\n' ..
| |
| 'Unresolved (%d)=\n%s\n' ..
| |
| '|-|\n' ..
| |
| 'Resolved (%d)=\n%s\n' ..
| |
| '|-|\n' ..
| |
| 'All (%d)=\n%s\n' ..
| |
| '</tabber>',
| |
| #unresolvedCards, getListHtml(unresolvedCards, 'No unresolved feedback found.'),
| |
| #resolvedCards, getListHtml(resolvedCards, 'No resolved feedback found.'),
| |
| #allCards, getListHtml(allCards, 'No feedback records found.')
| |
| )
| |
|
| |
| return frame:preprocess(tabberWikitext)
| |
| end
| |
|
| |
| return p
| |