2026-02-27 08:06:22 +01:00
/ * *
* WooCow – My Account frontend JavaScript
* /
( function ( $ ) {
'use strict' ;
if ( ! $ ( '#woocow-account' ) . length ) return ;
const ajax = ( action , data ) =>
$ . post ( woocowAcct . ajax _url , { action , nonce : woocowAcct . nonce , ... data } ) ;
const notice = ( msg , type = 'success' ) => {
const $n = $ ( '#woocow-acct-notices' ) ;
$n . html ( ` <div class="woocommerce- ${ type === 'success' ? 'message' : 'error' } "> ${ msg } </div> ` ) ;
setTimeout ( ( ) => $n . find ( '> div' ) . fadeOut ( 400 , function ( ) { $ ( this ) . remove ( ) ; } ) , 5000 ) ;
} ;
function esc ( str ) {
return String ( str ) . replace ( /[&<>"']/g , m => ( {
'&' : '&' , '<' : '<' , '>' : '>' , '"' : '"' , "'" : '''
} ) [ m ] ) ;
}
function formatMB ( bytes ) {
if ( ! bytes ) return '0 MB' ;
const mb = bytes / 1024 / 1024 ;
return mb >= 1024 ? ( mb / 1024 ) . toFixed ( 1 ) + ' GB' : mb . toFixed ( 0 ) + ' MB' ;
}
// ── Load Mailboxes ────────────────────────────────────────────────────────
$ ( document ) . on ( 'click' , '.woocow-load-mailboxes' , function ( ) {
const $panel = $ ( this ) . closest ( '.woocow-domain-panel' ) ;
const sid = $panel . data ( 'server-id' ) ;
const domain = $panel . data ( 'domain' ) ;
const $wrap = $panel . find ( '.woocow-mailboxes-wrap' ) ;
const $list = $panel . find ( '.woocow-mailboxes-list' ) ;
$ ( this ) . prop ( 'disabled' , true ) . text ( 'Loading…' ) ;
$wrap . show ( ) ;
$list . html ( '<p class="woocow-loading">Fetching mailboxes…</p>' ) ;
ajax ( 'woocow_acct_mailboxes' , { server _id : sid , domain } ) . done ( res => {
$ ( this ) . prop ( 'disabled' , false ) . text ( 'Refresh' ) ;
if ( ! res . success ) {
$list . html ( ` <p class="woocow-error"> ${ esc ( res . data ) } </p> ` ) ;
return ;
}
const boxes = res . data . mailboxes || [ ] ;
const webmail = res . data . webmail _url ;
if ( ! boxes . length ) {
$list . html ( '<p class="woocow-muted">No mailboxes yet. Create one below.</p>' ) ;
return ;
}
let html = '' ;
boxes . forEach ( m => {
feat: domains, transports, logs, quarantine, spam filter, i18n + UX fixes
Features added:
- Admin > Domains: add domains to Mailcow servers, auto-generate DKIM,
display full DNS record set (MX, SPF, DMARC, DKIM, autoconfig CNAMEs)
with one-click copy per record
- Admin > Transports: manage sender-dependent relay hosts (add/delete)
- Admin > Logs: view Postfix, Dovecot, Rspamd, Ratelimit, API and other
server logs in a dark scrollable panel
- My Account: per-domain Quarantine panel — view score, sender, subject,
date; permanently delete quarantined messages
- My Account: per-mailbox Spam Filter slider (1–15 threshold) saved via API
- My Account: Aliases & Forwarders (alias creation doubles as forwarder
to any external address)
UX fixes:
- Quota 0 now displays ∞ (unlimited) in both admin and account views
- Admin mailbox action buttons replaced with Dashicon icon buttons
(lock, chart-bar, trash) with title tooltips
i18n:
- load_plugin_textdomain registered on init hook
- All user-facing PHP strings wrapped in __() / esc_html__()
- Translated strings array passed to account JS via wp_localize_script
- woocow-es_ES.po/.mo — Spanish translation
- woocow-ro_RO.po/.mo — Romanian translation (with correct plural forms)
- English remains the fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 08:38:52 +01:00
const unlimited = ( m . quota === 0 || m . quota === '0' ) ;
const pct = unlimited ? 0 : parseFloat ( m . percent _in _use || 0 ) ;
2026-02-27 08:06:22 +01:00
const used = formatMB ( m . quota _used ) ;
feat: domains, transports, logs, quarantine, spam filter, i18n + UX fixes
Features added:
- Admin > Domains: add domains to Mailcow servers, auto-generate DKIM,
display full DNS record set (MX, SPF, DMARC, DKIM, autoconfig CNAMEs)
with one-click copy per record
- Admin > Transports: manage sender-dependent relay hosts (add/delete)
- Admin > Logs: view Postfix, Dovecot, Rspamd, Ratelimit, API and other
server logs in a dark scrollable panel
- My Account: per-domain Quarantine panel — view score, sender, subject,
date; permanently delete quarantined messages
- My Account: per-mailbox Spam Filter slider (1–15 threshold) saved via API
- My Account: Aliases & Forwarders (alias creation doubles as forwarder
to any external address)
UX fixes:
- Quota 0 now displays ∞ (unlimited) in both admin and account views
- Admin mailbox action buttons replaced with Dashicon icon buttons
(lock, chart-bar, trash) with title tooltips
i18n:
- load_plugin_textdomain registered on init hook
- All user-facing PHP strings wrapped in __() / esc_html__()
- Translated strings array passed to account JS via wp_localize_script
- woocow-es_ES.po/.mo — Spanish translation
- woocow-ro_RO.po/.mo — Romanian translation (with correct plural forms)
- English remains the fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 08:38:52 +01:00
const max = unlimited ? '∞' : formatMB ( m . quota ) ;
2026-02-27 08:06:22 +01:00
const col = pct > 85 ? '#e74c3c' : pct > 60 ? '#f39c12' : '#27ae60' ;
html += ` <div class="woocow-mailbox-row" data-email=" ${ esc ( m . username ) } ">
< div class = "woocow-mbox-main" >
< div class = "woocow-mbox-address" >
< span class = "woocow-mbox-icon" > ✉ < / s p a n >
< strong > $ { esc ( m . username ) } < / s t r o n g >
$ { m . name ? ` <span class="woocow-mbox-name">( ${ esc ( m . name ) } )</span> ` : '' }
< / d i v >
< div class = "woocow-quota-wrap" >
< div class = "woocow-quota-bar-outer" >
< div class = "woocow-quota-bar-inner" style = "width:${pct}%;background:${col}" > < / d i v >
< / d i v >
< span class = "woocow-quota-text" > $ { used } / $ { max } ( $ { pct } % ) < / s p a n >
< / d i v >
< / d i v >
< div class = "woocow-mbox-actions" >
< button class = "woocow-btn woocow-btn-sm wc-change-pw"
data - server = "${esc(sid)}" data - domain = "${esc(domain)}" data - email = "${esc(m.username)}" >
feat: domains, transports, logs, quarantine, spam filter, i18n + UX fixes
Features added:
- Admin > Domains: add domains to Mailcow servers, auto-generate DKIM,
display full DNS record set (MX, SPF, DMARC, DKIM, autoconfig CNAMEs)
with one-click copy per record
- Admin > Transports: manage sender-dependent relay hosts (add/delete)
- Admin > Logs: view Postfix, Dovecot, Rspamd, Ratelimit, API and other
server logs in a dark scrollable panel
- My Account: per-domain Quarantine panel — view score, sender, subject,
date; permanently delete quarantined messages
- My Account: per-mailbox Spam Filter slider (1–15 threshold) saved via API
- My Account: Aliases & Forwarders (alias creation doubles as forwarder
to any external address)
UX fixes:
- Quota 0 now displays ∞ (unlimited) in both admin and account views
- Admin mailbox action buttons replaced with Dashicon icon buttons
(lock, chart-bar, trash) with title tooltips
i18n:
- load_plugin_textdomain registered on init hook
- All user-facing PHP strings wrapped in __() / esc_html__()
- Translated strings array passed to account JS via wp_localize_script
- woocow-es_ES.po/.mo — Spanish translation
- woocow-ro_RO.po/.mo — Romanian translation (with correct plural forms)
- English remains the fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 08:38:52 +01:00
🔑 Change Password
2026-02-27 08:06:22 +01:00
< / b u t t o n >
< button class = "woocow-btn woocow-btn-sm woocow-btn-outline wc-toggle-aliases"
data - server = "${esc(sid)}" data - domain = "${esc(domain)}" >
feat: domains, transports, logs, quarantine, spam filter, i18n + UX fixes
Features added:
- Admin > Domains: add domains to Mailcow servers, auto-generate DKIM,
display full DNS record set (MX, SPF, DMARC, DKIM, autoconfig CNAMEs)
with one-click copy per record
- Admin > Transports: manage sender-dependent relay hosts (add/delete)
- Admin > Logs: view Postfix, Dovecot, Rspamd, Ratelimit, API and other
server logs in a dark scrollable panel
- My Account: per-domain Quarantine panel — view score, sender, subject,
date; permanently delete quarantined messages
- My Account: per-mailbox Spam Filter slider (1–15 threshold) saved via API
- My Account: Aliases & Forwarders (alias creation doubles as forwarder
to any external address)
UX fixes:
- Quota 0 now displays ∞ (unlimited) in both admin and account views
- Admin mailbox action buttons replaced with Dashicon icon buttons
(lock, chart-bar, trash) with title tooltips
i18n:
- load_plugin_textdomain registered on init hook
- All user-facing PHP strings wrapped in __() / esc_html__()
- Translated strings array passed to account JS via wp_localize_script
- woocow-es_ES.po/.mo — Spanish translation
- woocow-ro_RO.po/.mo — Romanian translation (with correct plural forms)
- English remains the fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 08:38:52 +01:00
✉ Aliases & amp ; Forwarders
< / b u t t o n >
< button class = "woocow-btn woocow-btn-sm woocow-btn-outline wc-spam-score-btn"
data - server = "${esc(sid)}" data - domain = "${esc(domain)}"
data - email = "${esc(m.username)}" data - score = "${esc(m.spam_score || 5)}" >
🛡 Spam Filter
2026-02-27 08:06:22 +01:00
< / b u t t o n >
< a href = "${esc(webmail)}" target = "_blank" rel = "noopener" class = "woocow-btn woocow-btn-sm woocow-btn-ghost" >
feat: domains, transports, logs, quarantine, spam filter, i18n + UX fixes
Features added:
- Admin > Domains: add domains to Mailcow servers, auto-generate DKIM,
display full DNS record set (MX, SPF, DMARC, DKIM, autoconfig CNAMEs)
with one-click copy per record
- Admin > Transports: manage sender-dependent relay hosts (add/delete)
- Admin > Logs: view Postfix, Dovecot, Rspamd, Ratelimit, API and other
server logs in a dark scrollable panel
- My Account: per-domain Quarantine panel — view score, sender, subject,
date; permanently delete quarantined messages
- My Account: per-mailbox Spam Filter slider (1–15 threshold) saved via API
- My Account: Aliases & Forwarders (alias creation doubles as forwarder
to any external address)
UX fixes:
- Quota 0 now displays ∞ (unlimited) in both admin and account views
- Admin mailbox action buttons replaced with Dashicon icon buttons
(lock, chart-bar, trash) with title tooltips
i18n:
- load_plugin_textdomain registered on init hook
- All user-facing PHP strings wrapped in __() / esc_html__()
- Translated strings array passed to account JS via wp_localize_script
- woocow-es_ES.po/.mo — Spanish translation
- woocow-ro_RO.po/.mo — Romanian translation (with correct plural forms)
- English remains the fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 08:38:52 +01:00
↗ Webmail
2026-02-27 08:06:22 +01:00
< / a >
< / d i v >
< div class = "woocow-aliases-wrap" style = "display:none" >
< div class = "woocow-aliases-list" > < / d i v >
< div class = "woocow-alias-create-form" style = "display:none" >
< div class = "woocow-alias-fields" >
< input type = "email" class = "wc-alias-addr woocow-input" placeholder = "alias@${esc(domain)}" >
< span class = "woocow-arrow" > → < / s p a n >
< input type = "email" class = "wc-alias-goto woocow-input" placeholder = "destination@example.com" value = "${esc(m.username)}" >
< button class = "woocow-btn woocow-btn-primary woocow-btn-sm wc-alias-save"
data - server = "${esc(sid)}" data - domain = "${esc(domain)}" > Add < / b u t t o n >
< button class = "woocow-btn woocow-btn-sm wc-alias-cancel" > ✕ < / b u t t o n >
< / d i v >
< / d i v >
< button class = "woocow-btn woocow-btn-sm woocow-btn-outline wc-alias-add-btn" > + Add Alias < / b u t t o n >
< / d i v >
< / d i v > ` ;
} ) ;
$list . html ( html ) ;
} ) ;
} ) ;
// ── Aliases ───────────────────────────────────────────────────────────────
$ ( document ) . on ( 'click' , '.wc-toggle-aliases' , function ( ) {
const $row = $ ( this ) . closest ( '.woocow-mailbox-row' ) ;
const $wrap = $row . find ( '.woocow-aliases-wrap' ) ;
const sid = $ ( this ) . data ( 'server' ) ;
const domain = $ ( this ) . data ( 'domain' ) ;
if ( $wrap . is ( ':visible' ) ) {
$wrap . slideUp ( ) ;
return ;
}
const $list = $row . find ( '.woocow-aliases-list' ) . html ( '<p class="woocow-muted">Loading aliases…</p>' ) ;
$wrap . slideDown ( ) ;
ajax ( 'woocow_acct_aliases' , { server _id : sid , domain } ) . done ( res => {
if ( ! res . success ) {
$list . html ( ` <p class="woocow-error"> ${ esc ( res . data ) } </p> ` ) ;
return ;
}
const aliases = res . data ;
if ( ! aliases . length ) {
$list . html ( '<p class="woocow-muted">No aliases for this domain yet.</p>' ) ;
return ;
}
let html = '<ul class="woocow-alias-list">' ;
aliases . forEach ( a => {
html += ` <li>
< span class = "woocow-alias-addr" > $ { esc ( a . address ) } < / s p a n >
< span class = "woocow-arrow" > → < / s p a n >
< span class = "woocow-alias-goto" > $ { esc ( a . goto ) } < / s p a n >
< button class = "woocow-btn woocow-btn-danger woocow-btn-xs wc-alias-del"
data - id = "${esc(a.id)}" data - server = "${esc(sid)}" data - domain = "${esc(domain)}" > ✕ < / b u t t o n >
< / l i > ` ;
} ) ;
html += '</ul>' ;
$list . html ( html ) ;
} ) ;
} ) ;
$ ( document ) . on ( 'click' , '.wc-alias-add-btn' , function ( ) {
$ ( this ) . closest ( '.woocow-aliases-wrap' ) . find ( '.woocow-alias-create-form' ) . slideToggle ( ) ;
} ) ;
$ ( document ) . on ( 'click' , '.wc-alias-cancel' , function ( ) {
$ ( this ) . closest ( '.woocow-alias-create-form' ) . slideUp ( ) ;
} ) ;
$ ( document ) . on ( 'click' , '.wc-alias-save' , function ( ) {
const $form = $ ( this ) . closest ( '.woocow-alias-create-form' ) ;
const sid = $ ( this ) . data ( 'server' ) ;
const domain = $ ( this ) . data ( 'domain' ) ;
const addr = $form . find ( '.wc-alias-addr' ) . val ( ) . trim ( ) ;
const goto _ = $form . find ( '.wc-alias-goto' ) . val ( ) . trim ( ) ;
if ( ! addr || ! goto _ ) { alert ( 'Both alias and destination are required.' ) ; return ; }
ajax ( 'woocow_acct_alias_create' , { server _id : sid , domain , address : addr , goto : goto _ } ) . done ( res => {
if ( res . success ) {
// Refresh alias list
$ ( this ) . closest ( '.woocow-mailbox-row' ) . find ( '.wc-toggle-aliases' ) . trigger ( 'click' ) ;
setTimeout ( ( ) => { $ ( this ) . closest ( '.woocow-mailbox-row' ) . find ( '.wc-toggle-aliases' ) . trigger ( 'click' ) ; } , 300 ) ;
$form . slideUp ( ) ;
} else {
alert ( 'Error: ' + res . data ) ;
}
} ) ;
} ) ;
$ ( document ) . on ( 'click' , '.wc-alias-del' , function ( ) {
if ( ! confirm ( 'Delete this alias?' ) ) return ;
const $li = $ ( this ) . closest ( 'li' ) ;
const sid = $ ( this ) . data ( 'server' ) ;
const domain = $ ( this ) . data ( 'domain' ) ;
const id = $ ( this ) . data ( 'id' ) ;
ajax ( 'woocow_acct_alias_delete' , { server _id : sid , domain , alias _id : id } ) . done ( res => {
if ( res . success ) $li . fadeOut ( 300 , function ( ) { $ ( this ) . remove ( ) ; } ) ;
else alert ( 'Delete failed: ' + res . data ) ;
} ) ;
} ) ;
// ── Create Mailbox ────────────────────────────────────────────────────────
$ ( document ) . on ( 'click' , '.woocow-create-mbox-btn' , function ( ) {
const $panel = $ ( this ) . closest ( '.woocow-domain-panel' ) ;
$panel . find ( '.woocow-create-mbox-form' ) . slideToggle ( ) ;
} ) ;
$ ( document ) . on ( 'click' , '.wc-mbox-cancel' , function ( ) {
$ ( this ) . closest ( '.woocow-create-mbox-form' ) . slideUp ( ) ;
} ) ;
$ ( document ) . on ( 'click' , '.wc-mbox-submit' , function ( ) {
const $panel = $ ( this ) . closest ( '.woocow-domain-panel' ) ;
const sid = $panel . data ( 'server-id' ) ;
const domain = $panel . data ( 'domain' ) ;
const $form = $ ( this ) . closest ( '.woocow-create-mbox-form' ) ;
const $note = $form . find ( '.wc-mbox-notice' ) ;
const local = $form . find ( '.wc-mbox-local' ) . val ( ) . trim ( ) ;
const name = $form . find ( '.wc-mbox-name' ) . val ( ) . trim ( ) ;
const pass = $form . find ( '.wc-mbox-pass' ) . val ( ) ;
const pass2 = $form . find ( '.wc-mbox-pass2' ) . val ( ) ;
const quota = $form . find ( '.wc-mbox-quota' ) . val ( ) ;
if ( ! local || ! pass ) { $note . html ( '<span class="woocow-error">Username and password required.</span>' ) ; return ; }
if ( pass !== pass2 ) { $note . html ( '<span class="woocow-error">Passwords do not match.</span>' ) ; return ; }
$note . text ( 'Creating…' ) ;
ajax ( 'woocow_acct_mailbox_create' , { server _id : sid , domain , local _part : local , name , password : pass , password2 : pass2 , quota } ) . done ( res => {
if ( res . success ) {
$note . html ( '<span style="color:green">✓ Mailbox created!</span>' ) ;
$form . slideUp ( ) ;
// Refresh mailbox list
$panel . find ( '.woocow-load-mailboxes' ) . trigger ( 'click' ) ;
} else {
$note . html ( ` <span class="woocow-error"> ${ esc ( res . data ) } </span> ` ) ;
}
} ) ;
} ) ;
// ── Change Password Modal ─────────────────────────────────────────────────
$ ( document ) . on ( 'click' , '.wc-change-pw' , function ( ) {
const sid = $ ( this ) . data ( 'server' ) ;
const domain = $ ( this ) . data ( 'domain' ) ;
const email = $ ( this ) . data ( 'email' ) ;
$ ( '#woocow-pw-server-id' ) . val ( sid ) ;
$ ( '#woocow-pw-mailbox' ) . val ( email ) ;
$ ( '#woocow-pw-email' ) . text ( email ) ;
$ ( '#woocow-pw-new, #woocow-pw-new2' ) . val ( '' ) ;
$ ( '#woocow-pw-notice' ) . text ( '' ) ;
$ ( '#woocow-pw-modal' ) . fadeIn ( 200 ) ;
// Store domain on modal for verification
$ ( '#woocow-pw-modal' ) . data ( 'domain' , domain ) ;
} ) ;
$ ( '#woocow-pw-cancel' ) . on ( 'click' , ( ) => $ ( '#woocow-pw-modal' ) . fadeOut ( 200 ) ) ;
$ ( document ) . on ( 'click' , '#woocow-pw-modal' , function ( e ) {
if ( $ ( e . target ) . is ( '#woocow-pw-modal' ) ) $ ( this ) . fadeOut ( 200 ) ;
} ) ;
$ ( '#woocow-pw-save' ) . on ( 'click' , function ( ) {
const sid = $ ( '#woocow-pw-server-id' ) . val ( ) ;
const email = $ ( '#woocow-pw-mailbox' ) . val ( ) ;
const domain = $ ( '#woocow-pw-modal' ) . data ( 'domain' ) ;
const pass = $ ( '#woocow-pw-new' ) . val ( ) ;
const pass2 = $ ( '#woocow-pw-new2' ) . val ( ) ;
const $note = $ ( '#woocow-pw-notice' ) ;
if ( ! pass || pass !== pass2 ) {
$note . html ( '<span class="woocow-error">Passwords do not match or are empty.</span>' ) ;
return ;
}
$note . text ( 'Updating…' ) ;
ajax ( 'woocow_acct_mailbox_password' , { server _id : sid , domain , email , password : pass , password2 : pass2 } ) . done ( res => {
if ( res . success ) {
$note . html ( '<span style="color:green">✓ Password updated!</span>' ) ;
setTimeout ( ( ) => $ ( '#woocow-pw-modal' ) . fadeOut ( 200 ) , 1500 ) ;
} else {
$note . html ( ` <span class="woocow-error"> ${ esc ( res . data ) } </span> ` ) ;
}
} ) ;
} ) ;
feat: domains, transports, logs, quarantine, spam filter, i18n + UX fixes
Features added:
- Admin > Domains: add domains to Mailcow servers, auto-generate DKIM,
display full DNS record set (MX, SPF, DMARC, DKIM, autoconfig CNAMEs)
with one-click copy per record
- Admin > Transports: manage sender-dependent relay hosts (add/delete)
- Admin > Logs: view Postfix, Dovecot, Rspamd, Ratelimit, API and other
server logs in a dark scrollable panel
- My Account: per-domain Quarantine panel — view score, sender, subject,
date; permanently delete quarantined messages
- My Account: per-mailbox Spam Filter slider (1–15 threshold) saved via API
- My Account: Aliases & Forwarders (alias creation doubles as forwarder
to any external address)
UX fixes:
- Quota 0 now displays ∞ (unlimited) in both admin and account views
- Admin mailbox action buttons replaced with Dashicon icon buttons
(lock, chart-bar, trash) with title tooltips
i18n:
- load_plugin_textdomain registered on init hook
- All user-facing PHP strings wrapped in __() / esc_html__()
- Translated strings array passed to account JS via wp_localize_script
- woocow-es_ES.po/.mo — Spanish translation
- woocow-ro_RO.po/.mo — Romanian translation (with correct plural forms)
- English remains the fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 08:38:52 +01:00
// ── Quarantine ────────────────────────────────────────────────────────────
$ ( document ) . on ( 'click' , '.woocow-load-quarantine' , function ( ) {
const $panel = $ ( this ) . closest ( '.woocow-domain-panel' ) ;
const $wrap = $panel . find ( '.woocow-quarantine-wrap' ) ;
const $list = $panel . find ( '.woocow-quarantine-list' ) ;
const sid = $panel . data ( 'server-id' ) ;
const domain = $panel . data ( 'domain' ) ;
if ( $wrap . is ( ':visible' ) ) { $wrap . slideUp ( ) ; return ; }
$list . html ( '<p class="woocow-loading">Loading quarantine…</p>' ) ;
$wrap . slideDown ( ) ;
ajax ( 'woocow_acct_quarantine' , { server _id : sid , domain } ) . done ( res => {
if ( ! res . success ) { $list . html ( ` <p class="woocow-error"> ${ esc ( res . data ) } </p> ` ) ; return ; }
const msgs = res . data ;
if ( ! msgs . length ) { $list . html ( '<p class="woocow-muted">No quarantined messages for this domain.</p>' ) ; return ; }
let html = ` <table class="woocow-quarantine-table">
< thead > < tr > < th > From < / t h > < t h > T o < / t h > < t h > S u b j e c t < / t h > < t h > S c o r e < / t h > < t h > D a t e < / t h > < t h > < / t h > < / t r > < / t h e a d > < t b o d y > ` ;
msgs . forEach ( m => {
const date = new Date ( m . created * 1000 ) . toLocaleString ( ) ;
const score = parseFloat ( m . score ) . toFixed ( 1 ) ;
const virus = m . virus _flag == 1 ? ' 🦠' : '' ;
html += ` <tr>
< td > $ { esc ( m . sender ) } < / t d >
< td > $ { esc ( m . rcpt ) } < / t d >
< td > $ { esc ( m . subject ) } $ { virus } < / t d >
< td > < span class = "woocow-score-badge" style = "background:${score > 10 ? '#e74c3c' : score > 5 ? '#f39c12' : '#95a5a6'}" > $ { score } < / s p a n > < / t d >
< td style = "white-space:nowrap;font-size:12px" > $ { esc ( date ) } < / t d >
< td > < button class = "woocow-btn woocow-btn-danger woocow-btn-xs wc-q-del"
data - id = "${esc(m.id)}" data - server = "${esc(sid)}" data - domain = "${esc(domain)}" > Delete < / b u t t o n > < / t d >
< / t r > ` ;
} ) ;
html += '</tbody></table>' ;
html += '<p class="woocow-muted" style="margin-top:8px">To release a message to your inbox, use the link in your quarantine notification email or via Webmail.</p>' ;
$list . html ( html ) ;
} ) ;
} ) ;
$ ( document ) . on ( 'click' , '.wc-q-del' , function ( ) {
if ( ! confirm ( 'Permanently delete this quarantined message?' ) ) return ;
const $row = $ ( this ) . closest ( 'tr' ) ;
ajax ( 'woocow_acct_quarantine_delete' , {
server _id : $ ( this ) . data ( 'server' ) ,
domain : $ ( this ) . data ( 'domain' ) ,
qid : $ ( this ) . data ( 'id' ) ,
} ) . done ( res => {
if ( res . success ) $row . fadeOut ( 300 , function ( ) { $ ( this ) . remove ( ) ; } ) ;
else alert ( 'Delete failed: ' + res . data ) ;
} ) ;
} ) ;
// ── Spam Score ────────────────────────────────────────────────────────────
$ ( document ) . on ( 'click' , '.wc-spam-score-btn' , function ( ) {
const $row = $ ( this ) . closest ( '.woocow-mailbox-row' ) ;
const sid = $ ( this ) . data ( 'server' ) ;
const domain = $ ( this ) . data ( 'domain' ) ;
const email = $ ( this ) . data ( 'email' ) ;
const score = $ ( this ) . data ( 'score' ) ;
const $existing = $row . find ( '.woocow-spam-panel' ) ;
if ( $existing . length ) { $existing . slideToggle ( ) ; return ; }
$row . append ( `
< div class = "woocow-spam-panel" style = "margin-top:12px;padding-top:12px;border-top:1px dashed #e0e0e0" >
< strong > Spam Filter Threshold < / s t r o n g >
< p class = "woocow-muted" > Lower = stricter . Default is 5. Emails above this score go to spam / quarantine . < / p >
< div class = "woocow-flex-inline" style = "gap:10px;margin-top:8px" >
< input type = "range" class = "wc-spam-slider" min = "1" max = "15" step = "0.5" value = "${esc(score)}"
style = "width:200px" >
< span class = "wc-spam-val" > $ { parseFloat ( score ) . toFixed ( 1 ) } < / s p a n >
< button class = "woocow-btn woocow-btn-primary woocow-btn-sm wc-spam-save"
data - server = "${esc(sid)}" data - domain = "${esc(domain)}" data - email = "${esc(email)}" > Save < / b u t t o n >
< span class = "wc-spam-notice" > < / s p a n >
< / d i v >
< / d i v >
` );
$row . find ( '.wc-spam-slider' ) . on ( 'input' , function ( ) {
$row . find ( '.wc-spam-val' ) . text ( parseFloat ( $ ( this ) . val ( ) ) . toFixed ( 1 ) ) ;
} ) ;
} ) ;
$ ( document ) . on ( 'click' , '.wc-spam-save' , function ( ) {
const $panel = $ ( this ) . closest ( '.woocow-spam-panel' ) ;
const $note = $panel . find ( '.wc-spam-notice' ) ;
const score = $panel . find ( '.wc-spam-slider' ) . val ( ) ;
$note . text ( 'Saving…' ) ;
ajax ( 'woocow_acct_spam_score' , {
server _id : $ ( this ) . data ( 'server' ) ,
domain : $ ( this ) . data ( 'domain' ) ,
email : $ ( this ) . data ( 'email' ) ,
spam _score : score ,
} ) . done ( res => {
if ( res . success ) $note . html ( '<span style="color:green">✓ Saved</span>' ) ;
else $note . html ( ` <span class="woocow-error"> ${ esc ( res . data ) } </span> ` ) ;
} ) ;
} ) ;
2026-02-27 08:06:22 +01:00
} ) ( jQuery ) ;