MediaWiki:Gadget-verify.js: Difference between revisions
From PickiPedia: A knowledge base of bluegrass, old time, and other traditional and public domain music
Jump to navigationJump to search
Recreate with javascript content model (via create-page on MediaWiki MCP Server) |
Make source URL optional - text source is sufficient (via update-page on MediaWiki MCP Server) |
||
| Line 67: | Line 67: | ||
$form.append( | $form.append( | ||
$('<label>').attr('for', 'verify-source').text('Source URL:'), | $('<label>').attr('for', 'verify-source').text('Source (URL or description):'), | ||
$('<br>'), | $('<br>'), | ||
$('<input>') | $('<input>') | ||
.attr({ | .attr({ | ||
type: ' | type: 'text', | ||
id: 'verify-source', | id: 'verify-source', | ||
name: 'source', | name: 'source', | ||
placeholder: 'https://...' | placeholder: 'https://... or "I was there" or "per setlist.fm"' | ||
}) | }) | ||
.val(existingSource | .val(existingSource || '') | ||
.css({ width: '100%', marginBottom: '1em', padding: '6px', boxSizing: 'border-box' }) | .css({ width: '100%', marginBottom: '1em', padding: '6px', boxSizing: 'border-box' }) | ||
); | ); | ||
if (isBotProposal && existingSource | if (isBotProposal && existingSource) { | ||
$form.find('label[for="verify-source"]').after( | $form.find('label[for="verify-source"]').after( | ||
$('<div>').css({ fontSize: '0.9em', color: '#666', marginBottom: '0.5em' }) | $('<div>').css({ fontSize: '0.9em', color: '#666', marginBottom: '0.5em' }) | ||
| Line 95: | Line 95: | ||
name: 'note', | name: 'note', | ||
rows: 3, | rows: 3, | ||
placeholder: ' | placeholder: 'Additional context about your verification...' | ||
}) | }) | ||
.css({ width: '100%', marginBottom: '1em', padding: '6px', boxSizing: 'border-box' }) | .css({ width: '100%', marginBottom: '1em', padding: '6px', boxSizing: 'border-box' }) | ||
| Line 165: | Line 165: | ||
function submitVerification($claim, $form, claimText, $overlay, $dialog) { | function submitVerification($claim, $form, claimText, $overlay, $dialog) { | ||
var | var source = $form.find('#verify-source').val().trim(); | ||
var note = $form.find('#verify-note').val().trim(); | var note = $form.find('#verify-note').val().trim(); | ||
var pageName = mw.config.get('wgPageName'); | var pageName = mw.config.get('wgPageName'); | ||
| Line 171: | Line 171: | ||
var timestamp = new Date().toISOString().split('T')[0]; | var timestamp = new Date().toISOString().split('T')[0]; | ||
if (! | // Require at least a source OR a note | ||
mw.notify('Please provide a source | if (!source && !note) { | ||
mw.notify('Please provide a source or a note explaining your verification', { type: 'error' }); | |||
return; | return; | ||
} | } | ||
| Line 200: | Line 201: | ||
); | ); | ||
var replacement = claimText + '{{source|' + | // Build replacement - use {{source|...}} for URLs, {{verified|...}} for text | ||
var replacement; | |||
if (source && source.match(/^https?:\/\//)) { | |||
replacement = claimText + '{{source|' + source + '}}'; | |||
} else if (source) { | |||
replacement = claimText + '{{verified|' + source + '}}'; | |||
} else { | |||
replacement = claimText + '{{verified|by ' + userName + '}}'; | |||
} | |||
var newContent = content.replace(claimPattern, replacement); | var newContent = content.replace(claimPattern, replacement); | ||
| Line 219: | Line 229: | ||
}).then(function() { | }).then(function() { | ||
// Add to talk page | // Add to talk page | ||
return addTalkPageEntry(api, pageName, claimText, | return addTalkPageEntry(api, pageName, claimText, source, note, userName, timestamp); | ||
}).then(function() { | }).then(function() { | ||
mw.notify('Verification recorded!', { type: 'success' }); | mw.notify('Verification recorded!', { type: 'success' }); | ||
| Line 234: | Line 244: | ||
} | } | ||
function addTalkPageEntry(api, pageName, claimText, | function addTalkPageEntry(api, pageName, claimText, source, note, userName, timestamp) { | ||
var talkPage = 'Talk:' + pageName.replace(/^Talk:/, '').replace(/_/g, ' '); | var talkPage = 'Talk:' + pageName.replace(/^Talk:/, '').replace(/_/g, ' '); | ||
var entry = '\n== Verification by ' + userName + ' (' + timestamp + ') ==\n'; | var entry = '\n== Verification by ' + userName + ' (' + timestamp + ') ==\n'; | ||
entry += "'''Claim:''' " + claimText + '\n\n'; | entry += "'''Claim:''' " + claimText + '\n\n'; | ||
entry += "'''Source:''' " + | if (source) { | ||
entry += "'''Source:''' " + source + '\n'; | |||
} | |||
if (note) { | if (note) { | ||
entry += "\n'''Note:''' " + note + '\n'; | entry += "\n'''Note:''' " + note + '\n'; | ||
Revision as of 02:36, 1 January 2026
/**
* Gadget-verify.js - Verification gadget for Pickipedia claims
*
* Adds "Verify" buttons to {{claim}} and {{bot_proposes}} templates,
* allowing logged-in users to verify content and record attestations.
*/
(function() {
'use strict';
// Only run for logged-in users
if (mw.config.get('wgUserName') === null) {
return;
}
// Wait for page to be ready
mw.hook('wikipage.content').add(function($content) {
// Find all claim and bot_proposes elements
var $claims = $content.find('.claim-unverified, .bot-proposal');
$claims.each(function() {
var $claim = $(this);
// Don't add button twice
if ($claim.find('.verify-btn').length > 0) {
return;
}
var $btn = $('<button>')
.addClass('verify-btn')
.text('✓ Verify')
.css({
'margin-left': '0.5em',
'font-size': '0.8em',
'padding': '1px 6px',
'cursor': 'pointer',
'background': '#f8f9fa',
'border': '1px solid #a2a9b1',
'border-radius': '3px',
'vertical-align': 'middle'
})
.on('click', function(e) {
e.preventDefault();
e.stopPropagation();
openVerifyDialog($claim);
});
$claim.append(' ').append($btn);
});
});
function openVerifyDialog($claim) {
var claimText = $claim.clone().children('.verify-btn').remove().end().text().trim();
var existingSource = $claim.data('source') || '';
var isBotProposal = $claim.hasClass('bot-proposal');
// Create dialog content
var $dialog = $('<div>').addClass('verify-dialog');
var $form = $('<form>').on('submit', function(e) {
e.preventDefault();
submitVerification($claim, $form, claimText, $overlay, $dialog);
});
$form.append(
$('<p>').css('margin-top', '0').html('<strong>Verifying:</strong> ' + mw.html.escape(claimText))
);
$form.append(
$('<label>').attr('for', 'verify-source').text('Source (URL or description):'),
$('<br>'),
$('<input>')
.attr({
type: 'text',
id: 'verify-source',
name: 'source',
placeholder: 'https://... or "I was there" or "per setlist.fm"'
})
.val(existingSource || '')
.css({ width: '100%', marginBottom: '1em', padding: '6px', boxSizing: 'border-box' })
);
if (isBotProposal && existingSource) {
$form.find('label[for="verify-source"]').after(
$('<div>').css({ fontSize: '0.9em', color: '#666', marginBottom: '0.5em' })
.text('Bot suggested source: ' + existingSource)
);
}
$form.append(
$('<label>').attr('for', 'verify-note').text('Note (optional):'),
$('<br>'),
$('<textarea>')
.attr({
id: 'verify-note',
name: 'note',
rows: 3,
placeholder: 'Additional context about your verification...'
})
.css({ width: '100%', marginBottom: '1em', padding: '6px', boxSizing: 'border-box' })
);
$form.append(
$('<button>')
.attr('type', 'submit')
.text('Confirm Verification')
.css({
padding: '8px 16px',
background: '#36c',
color: 'white',
border: 'none',
borderRadius: '3px',
cursor: 'pointer',
marginRight: '8px'
}),
$('<button>')
.attr('type', 'button')
.text('Cancel')
.css({
padding: '8px 16px',
background: '#f8f9fa',
border: '1px solid #a2a9b1',
borderRadius: '3px',
cursor: 'pointer'
})
.on('click', function() {
$overlay.remove();
$dialog.remove();
})
);
$dialog.append($form);
// Style the dialog
$dialog.css({
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
background: 'white',
padding: '20px',
borderRadius: '8px',
boxShadow: '0 4px 20px rgba(0,0,0,0.3)',
zIndex: 10000,
minWidth: '400px',
maxWidth: '90vw'
});
// Add overlay
var $overlay = $('<div>').css({
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'rgba(0,0,0,0.5)',
zIndex: 9999
}).on('click', function() {
$overlay.remove();
$dialog.remove();
});
$('body').append($overlay).append($dialog);
$dialog.find('#verify-source').focus();
}
function submitVerification($claim, $form, claimText, $overlay, $dialog) {
var source = $form.find('#verify-source').val().trim();
var note = $form.find('#verify-note').val().trim();
var pageName = mw.config.get('wgPageName');
var userName = mw.config.get('wgUserName');
var timestamp = new Date().toISOString().split('T')[0];
// Require at least a source OR a note
if (!source && !note) {
mw.notify('Please provide a source or a note explaining your verification', { type: 'error' });
return;
}
// Disable form while processing
$form.find('button').prop('disabled', true);
$form.find('button[type="submit"]').text('Verifying...');
var api = new mw.Api();
api.get({
action: 'query',
titles: pageName,
prop: 'revisions',
rvprop: 'content',
rvslots: 'main',
formatversion: 2
}).then(function(data) {
var page = data.query.pages[0];
var content = page.revisions[0].slots.main.content;
// Build pattern to find the claim/bot_proposes template
var searchText = claimText.substring(0, 40).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
var claimPattern = new RegExp(
'\\{\\{(claim|bot_proposes)\\|[^}]*?' + searchText + '[^}]*?\\}\\}',
'i'
);
// Build replacement - use {{source|...}} for URLs, {{verified|...}} for text
var replacement;
if (source && source.match(/^https?:\/\//)) {
replacement = claimText + '{{source|' + source + '}}';
} else if (source) {
replacement = claimText + '{{verified|' + source + '}}';
} else {
replacement = claimText + '{{verified|by ' + userName + '}}';
}
var newContent = content.replace(claimPattern, replacement);
if (newContent === content) {
mw.notify('Could not find the claim in page source. It may have been modified.', { type: 'error' });
$form.find('button').prop('disabled', false);
$form.find('button[type="submit"]').text('Confirm Verification');
return;
}
var editSummary = 'Verified: "' + claimText.substring(0, 50) + (claimText.length > 50 ? '...' : '') + '"';
return api.postWithToken('csrf', {
action: 'edit',
title: pageName,
text: newContent,
summary: editSummary
}).then(function() {
// Add to talk page
return addTalkPageEntry(api, pageName, claimText, source, note, userName, timestamp);
}).then(function() {
mw.notify('Verification recorded!', { type: 'success' });
$overlay.remove();
$dialog.remove();
// Reload to show changes
location.reload();
});
}).catch(function(err) {
mw.notify('Error: ' + err, { type: 'error' });
$form.find('button').prop('disabled', false);
$form.find('button[type="submit"]').text('Confirm Verification');
});
}
function addTalkPageEntry(api, pageName, claimText, source, note, userName, timestamp) {
var talkPage = 'Talk:' + pageName.replace(/^Talk:/, '').replace(/_/g, ' ');
var entry = '\n== Verification by ' + userName + ' (' + timestamp + ') ==\n';
entry += "'''Claim:''' " + claimText + '\n\n';
if (source) {
entry += "'''Source:''' " + source + '\n';
}
if (note) {
entry += "\n'''Note:''' " + note + '\n';
}
return api.postWithToken('csrf', {
action: 'edit',
title: talkPage,
appendtext: entry,
summary: 'Recorded verification of claim'
});
}
})();