MediaWiki:Gadget-GrAnnotations.js: Difference between revisions
No edit summary |
No edit summary |
||
| Line 64: | Line 64: | ||
// ── Configuration ──────────────────────────────────────────────────── | // ── Configuration ──────────────────────────────────────────────────── | ||
var ADMIN_USER = 'GranthaGate'; | var ADMIN_USER = 'GranthaGate'; // MW username for talk-page fallback | ||
var ADMIN_EMAIL = 'admin@grantha.io'; // ← set your email here directly | |||
var CONTENT_SEL = '#mw-content-text'; | var CONTENT_SEL = '#mw-content-text'; | ||
var BM_LS_KEY = 'grantha_bm_' + ( ( window.mw && mw.config.get( 'wgPageName' ) ) || '' ); | var BM_LS_KEY = 'grantha_bm_' + ( ( window.mw && mw.config.get( 'wgPageName' ) ) || '' ); | ||
| Line 148: | Line 149: | ||
' <div class="gra-avatar" id="gra-cmp-avatar">' + esc(userInitial) + '</div>', | ' <div class="gra-avatar" id="gra-cmp-avatar">' + esc(userInitial) + '</div>', | ||
' <div class="gra-composer-name" id="gra-cmp-name">' + esc(currentUser || 'Anonymous') + '</div>', | ' <div class="gra-composer-name" id="gra-cmp-name">' + esc(currentUser || 'Anonymous') + '</div>', | ||
' </div>', | |||
' <div class="gra-quick-chips" id="gra-quick-chips">', | |||
' <button class="gra-chip" data-val="Spelling mistake" type="button">Spelling mistake</button>', | |||
' <button class="gra-chip" data-val="Reference error" type="button">Reference error</button>', | |||
' <button class="gra-chip" data-val="Others" type="button">Others ▾</button>', | |||
' </div>', | ' </div>', | ||
' <textarea class="gra-composer-input" id="gra-cmp-input"', | ' <textarea class="gra-composer-input" id="gra-cmp-input"', | ||
' placeholder=" | ' placeholder="Describe the issue…" rows="3"', | ||
' style="display:none"></textarea>', | |||
' <div class="gra-composer-actions">', | ' <div class="gra-composer-actions">', | ||
' <button class="gra-btn-cancel" id="gra-cmp-cancel">Cancel</button>', | ' <button class="gra-btn-cancel" id="gra-cmp-cancel">Cancel</button>', | ||
| Line 157: | Line 164: | ||
'</div>', | '</div>', | ||
].join('') ); | ].join('') ); | ||
// ── Bookmark composer card ──────────────────────────────────────── | // ── Bookmark composer card ──────────────────────────────────────── | ||
| Line 360: | Line 367: | ||
function closeCommentComposer() { | function closeCommentComposer() { | ||
$cmpComposer.removeClass('gra-composer-visible'); | $cmpComposer.removeClass('gra-composer-visible'); | ||
$cmpInput.val(''); | $( '#gra-quick-chips .gra-chip' ).removeClass('gra-chip-active'); | ||
$cmpInput.hide().val(''); | |||
$cmpSubmit.prop('disabled', true); | $cmpSubmit.prop('disabled', true); | ||
_selRange = null; | _selRange = null; | ||
| Line 424: | Line 432: | ||
function notifyAdmin( anchorId, quote, commentText, ts ) { | function notifyAdmin( anchorId, quote, commentText, ts ) { | ||
/* Send | /* Send notification email directly using MW's EmailUser API, targeting | ||
* | * ADMIN_EMAIL via the ADMIN_USER account — no server-side SMTP config needed. | ||
* ADMIN_EMAIL is set at the top of this file; change it to any address. */ | |||
if ( !window.mw ) return; | |||
* | |||
if ( !window.mw | |||
var articlePath = ( mw.config.get('wgArticlePath') || '/wiki/$1' ) | var articlePath = ( mw.config.get('wgArticlePath') || '/wiki/$1' ) | ||
.replace( '$1', pageTitle ); | .replace( '$1', pageTitle ); | ||
var anchorLink = window.location.origin + articlePath | var anchorLink = window.location.origin + articlePath | ||
+ ( anchorId ? '#' + anchorId : '' ); | + ( anchorId ? '#' + anchorId : '' ); | ||
var pageDisplay = pageTitle.replace( /_/g, ' ' ); | var pageDisplay = pageTitle.replace( /_/g, ' ' ); | ||
var subject = '[Grantha] New comment on "' + pageDisplay + '"'; | var subject = '[Grantha] New comment on "' + pageDisplay + '"'; | ||
var body = ' | var body = 'Page : ' + pageDisplay + '\n' | ||
+ ' | + 'By : ' + ( currentUser || 'Anonymous' ) + '\n' | ||
+ 'Time | + 'Time : ' + ts + '\n' | ||
+ 'Passage | + 'Passage : "' + quote + '"\n\n' | ||
+ 'Comment | + 'Comment :\n' + commentText + '\n\n' | ||
+ ' | + 'Link : ' + anchorLink + '\n'; | ||
/* Primary: send via MW EmailUser API to ADMIN_USER account. | |||
* The admin account must have ADMIN_EMAIL set in Special:Preferences, | |||
* OR you can hardcode the target as any confirmed MW user. */ | |||
new mw.Api().post({ | new mw.Api().post({ | ||
action: | action: 'emailuser', | ||
target: | target: ADMIN_USER, | ||
subject: | subject: subject, | ||
text: | text: body, | ||
token: | token: mw.user.tokens.get( 'csrfToken' ), | ||
}).catch(function(){ | }).catch(function(){ | ||
/* | /* Fallback: post a section to admin talk page for Echo notification */ | ||
if ( !currentUser ) return; | if ( !currentUser ) return; | ||
var wikimsg = '== New comment on [[' + pageDisplay + ']] ==\n' | var wikimsg = '== New comment on [[' + pageDisplay + ']] ==\n' | ||
+ '; By: ' + ( currentUser || 'Anonymous' ) + '\n' | + '; By: ' + ( currentUser || 'Anonymous' ) + '\n' | ||
| Line 471: | Line 467: | ||
+ '; Link: ' + anchorLink + '\n\n' | + '; Link: ' + anchorLink + '\n\n' | ||
+ commentText.slice(0,500) | + commentText.slice(0,500) | ||
+ ( commentText.length > 500 ? '\ | + ( commentText.length > 500 ? '\n...' : '' ) | ||
+ '\n\n[[User:Chandrashekars|Chandrashekars]] ([[User talk:Chandrashekars|talk]]) | + '\n\n[[User:Chandrashekars|Chandrashekars]] ([[User talk:Chandrashekars|talk]]) 18:12, 25 April 2026 (UTC)'; | ||
new mw.Api().postWithEditToken({ | new mw.Api().postWithEditToken({ | ||
action:'edit', title: | action:'edit', title:'User_talk:' + ADMIN_USER, section:'new', | ||
sectiontitle:'Comment on ' + pageDisplay, | sectiontitle:'Comment on ' + pageDisplay, | ||
text:wikimsg, | text:wikimsg, | ||
| Line 720: | Line 716: | ||
// ── Comment composer ────────────────────────────────────────── | // ── Comment composer ────────────────────────────────────────── | ||
/* Quick-select chips: clicking a chip selects it and sets comment text. | |||
* 'Others' chip shows the textarea for free-form input. | |||
* Any other chip sets the text directly and enables submit. */ | |||
$( '#gra-cmp-composer' ).on( 'click', '.gra-chip', function() { | |||
var $chip = $( this ); | |||
var val = $chip.attr('data-val'); | |||
// Toggle active state — allow deselecting | |||
var wasActive = $chip.hasClass('gra-chip-active'); | |||
$( '#gra-quick-chips .gra-chip' ).removeClass('gra-chip-active'); | |||
if ( !wasActive ) $chip.addClass('gra-chip-active'); | |||
if ( val === 'Others' && !wasActive ) { | |||
// Show textarea for free-form input | |||
$cmpInput.show().focus().val(''); | |||
$cmpSubmit.prop('disabled', true); | |||
} else if ( !wasActive ) { | |||
// Pre-fill and hide textarea — chip text is the comment | |||
$cmpInput.hide().val(val); | |||
$cmpSubmit.prop('disabled', !currentUser); | |||
} else { | |||
// Deselected — clear | |||
$cmpInput.hide().val(''); | |||
$cmpSubmit.prop('disabled', true); | |||
} | |||
} ); | |||
$cmpInput.on('input', function(){ | $cmpInput.on('input', function(){ | ||
$cmpSubmit.prop('disabled', !$(this).val().trim() || !currentUser); | $cmpSubmit.prop('disabled', !$(this).val().trim() || !currentUser); | ||