/** * WooCow – Admin JavaScript */ (function ($) { 'use strict'; const ajax = (action, data) => $.post(woocow.ajax_url, { action, nonce: woocow.nonce, ...data }); const notice = ($el, type, msg, autohide = true) => { $el.html(`

${msg}

`); if (autohide) setTimeout(() => $el.find('.notice').fadeOut(), 4000); }; // ── Servers Page ────────────────────────────────────────────────────────── if ($('#wc-servers-table-wrap').length) { let editId = 0; const loadServers = () => { ajax('woocow_servers_list').done(res => { if (!res.success) return; const rows = res.data; if (!rows.length) { $('#wc-servers-table-wrap').html('

No servers yet. Add one above.

'); return; } let html = ``; rows.forEach(s => { const badge = s.active == 1 ? 'Active' : 'Inactive'; html += ``; }); html += '
NameURLStatusAddedActions
${esc(s.name)} ${esc(s.url)} ${badge} ${s.created_at.split(' ')[0]}
'; $('#wc-servers-table-wrap').html(html); $('#wc-servers-loading').hide(); }); }; loadServers(); // Show add form $('#wc-add-server').on('click', () => { editId = 0; $('#wc-server-id').val(''); $('#wc-server-name, #wc-server-url, #wc-server-key').val(''); $('#wc-server-active').prop('checked', true); $('#wc-server-form-title').text('Add Server'); $('#wc-server-form').slideDown(); }); // Edit row $(document).on('click', '.wc-srv-edit', function () { editId = $(this).data('id'); const $row = $(this).closest('tr'); $('#wc-server-id').val(editId); $('#wc-server-name').val($row.data('name')); $('#wc-server-url').val($row.data('url')); $('#wc-server-key').val(''); $('#wc-server-active').prop('checked', true); $('#wc-server-form-title').text('Edit Server'); $('#wc-server-form').slideDown(); $('html, body').animate({ scrollTop: 0 }, 300); }); // Save $('#wc-server-save').on('click', () => { const data = { id: $('#wc-server-id').val(), name: $('#wc-server-name').val().trim(), url: $('#wc-server-url').val().trim(), api_key: $('#wc-server-key').val().trim(), active: $('#wc-server-active').is(':checked') ? 1 : 0, }; if (!data.name || !data.url || (!data.api_key && !editId)) { notice($('#wc-notices'), 'error', 'Please fill in all required fields.'); return; } ajax('woocow_server_save', data).done(res => { if (res.success) { notice($('#wc-notices'), 'success', 'Server saved.'); $('#wc-server-form').slideUp(); loadServers(); } else { notice($('#wc-notices'), 'error', res.data || 'Save failed.'); } }); }); // Test connection $('#wc-server-test').on('click', () => { const $result = $('#wc-server-test-result').text('Testing…'); ajax('woocow_server_test', { id: $('#wc-server-id').val(), url: $('#wc-server-url').val().trim(), api_key: $('#wc-server-key').val().trim(), }).done(res => { if (res.success) { $result.html(`✓ Connected – Mailcow ${esc(res.data.version)}`); } else { $result.html(`✗ ${esc(res.data)}`); } }); }); // Test from row $(document).on('click', '.wc-srv-test', function () { const id = $(this).data('id'); const $td = $(this).closest('td'); $td.append(' Testing…'); ajax('woocow_server_test', { id }).done(res => { $td.find('.wc-inline-test').html( res.success ? ` ✓ v${esc(res.data.version)}` : ` ✗ ${esc(res.data)}` ); }); }); // Delete $(document).on('click', '.wc-srv-del', function () { if (!confirm('Delete this server? All domain assignments for it will also be removed.')) return; ajax('woocow_server_delete', { id: $(this).data('id') }).done(res => { if (res.success) loadServers(); else notice($('#wc-notices'), 'error', res.data); }); }); $('#wc-server-cancel').on('click', () => $('#wc-server-form').slideUp()); } // ── Assignments Page ────────────────────────────────────────────────────── if ($('#wc-assignments-table-wrap').length) { const loadAssignments = () => { ajax('woocow_assignments_list').done(res => { if (!res.success) return; const rows = res.data; if (!rows.length) { $('#wc-assignments-table-wrap').html('

No assignments yet.

'); $('#wc-assignments-loading').hide(); return; } let html = ``; rows.forEach(r => { html += ``; }); html += '
CustomerEmailDomainServerAssignedActions
${esc(r.display_name)} ${esc(r.user_email)} ${esc(r.domain)} ${esc(r.server_name)} ${r.created_at.split(' ')[0]}
'; $('#wc-assignments-table-wrap').html(html); $('#wc-assignments-loading').hide(); }); }; // Load servers into select ajax('woocow_servers_list').done(res => { if (!res.success) return; res.data.filter(s => s.active == 1).forEach(s => { $('#wc-assign-server').append(``); }); }); // Server → load domains $('#wc-assign-server').on('change', function () { const sid = $(this).val(); $('#wc-assign-domain').html(''); if (!sid) { $('#wc-domain-row').hide(); return; } ajax('woocow_server_domains', { server_id: sid }).done(res => { if (!res.success) { alert('Could not load domains: ' + res.data); return; } $('#wc-assign-domain').html(''); res.data.forEach(d => { $('#wc-assign-domain').append(``); }); $('#wc-domain-row').show(); }); }); // Customer autocomplete let searchTimer; $('#wc-cust-search').on('input', function () { clearTimeout(searchTimer); const term = $(this).val().trim(); if (term.length < 2) { $('#wc-cust-results').hide(); return; } searchTimer = setTimeout(() => { ajax('woocow_customers_search', { term }).done(res => { if (!res.success || !res.data.length) { $('#wc-cust-results').hide(); return; } let html = ''; res.data.forEach(c => { html += `
${esc(c.label)}
`; }); $('#wc-cust-results').html(html).show(); }); }, 250); }); $(document).on('click', '.woocow-ac-item', function () { $('#wc-cust-id').val($(this).data('id')); $('#wc-cust-search').val(''); $('#wc-cust-selected').text($(this).data('label')); $('#wc-cust-results').hide(); }); $(document).on('click', function (e) { if (!$(e.target).closest('#wc-cust-results, #wc-cust-search').length) { $('#wc-cust-results').hide(); } }); // Save assignment $('#wc-assign-save').on('click', () => { const customer_id = $('#wc-cust-id').val(); const server_id = $('#wc-assign-server').val(); const domain = $('#wc-assign-domain').val(); if (!customer_id || !server_id || !domain) { notice($('#wc-assign-notice'), 'error', 'Please select a customer, server, and domain.'); return; } ajax('woocow_assignment_save', { customer_id, server_id, domain }).done(res => { if (res.success) { notice($('#wc-assign-notice'), 'success', `Domain ${esc(domain)} assigned.`); $('#wc-cust-id').val(''); $('#wc-cust-selected').text(''); loadAssignments(); } else { notice($('#wc-assign-notice'), 'error', res.data); } }); }); // Delete assignment $(document).on('click', '.wc-assign-del', function () { if (!confirm('Remove this domain assignment?')) return; ajax('woocow_assignment_delete', { id: $(this).data('id') }).done(res => { if (res.success) loadAssignments(); }); }); loadAssignments(); } // ── Mailboxes Page ──────────────────────────────────────────────────────── if ($('#wc-mb-table-wrap').length) { let currentServerId = null; let currentDomain = null; // Server → load domains $('#wc-mb-server').on('change', function () { const sid = $(this).val(); $('#wc-mb-domain').hide().html(''); $('#wc-mb-load').prop('disabled', true); if (!sid) return; ajax('woocow_server_domains', { server_id: sid }).done(res => { if (!res.success) return; res.data.forEach(d => { $('#wc-mb-domain').append(``); }); $('#wc-mb-domain').show(); }); }); $('#wc-mb-domain').on('change', function () { $('#wc-mb-load').prop('disabled', !$(this).val()); }); const loadMailboxes = () => { currentServerId = $('#wc-mb-server').val(); currentDomain = $('#wc-mb-domain').val(); if (!currentServerId || !currentDomain) return; $('#wc-mb-table-wrap').html('

Loading…

'); ajax('woocow_admin_mailboxes', { server_id: currentServerId, domain: currentDomain }).done(res => { if (!res.success) { $('#wc-mb-table-wrap').html(`

${esc(res.data)}

`); return; } const boxes = res.data.mailboxes || []; const webmail = res.data.webmail_url; if (!boxes.length) { $('#wc-mb-table-wrap').html('

No mailboxes found for this domain.

'); } else { let html = ``; boxes.forEach(m => { const pct = m.percent_in_use || 0; const used = formatMB(m.quota_used); const max = formatMB(m.quota); const bar = `
`; const quotaMB = Math.round((m.quota || 0) / 1024 / 1024); html += ``; }); html += '
EmailNameQuota UsedQuota MaxActiveActions
${esc(m.username)} ${esc(m.name)} ${used} ${bar} ${max} ${m.active == 1 ? '✓' : '–'}
'; $('#wc-mb-table-wrap').html(html); } $('#wc-mb-domain-label').text(currentDomain); $('#wc-mb-create').show(); }); }; $('#wc-mb-load').on('click', loadMailboxes); // Create mailbox modal $('#wc-mb-create').on('click', () => { $('#wc-mb-local, #wc-mb-fullname, #wc-mb-pass, #wc-mb-pass2').val(''); $('#wc-mb-quota').val(1024); $('#wc-mb-modal-notice').text(''); $('#wc-mb-modal').show(); }); $('#wc-mb-modal-cancel').on('click', () => $('#wc-mb-modal').hide()); $(document).on('keydown', e => { if (e.key === 'Escape') $('#wc-mb-modal').hide(); }); $('#wc-mb-modal-save').on('click', () => { const data = { server_id: currentServerId, domain: currentDomain, local_part: $('#wc-mb-local').val().trim(), name: $('#wc-mb-fullname').val().trim(), password: $('#wc-mb-pass').val(), password2: $('#wc-mb-pass2').val(), quota: $('#wc-mb-quota').val(), }; $('#wc-mb-modal-notice').text('Creating…'); ajax('woocow_admin_mailbox_create', data).done(res => { if (res.success) { $('#wc-mb-modal').hide(); notice($('#wc-mb-notices'), 'success', `Mailbox ${esc(res.data.email)} created.`); loadMailboxes(); } else { $('#wc-mb-modal-notice').html(`${esc(res.data)}`); } }); }); // Delete mailbox $(document).on('click', '.wc-mb-del', function () { const email = $(this).data('email'); if (!confirm(`Delete mailbox ${email}? This cannot be undone.`)) return; ajax('woocow_admin_mailbox_delete', { server_id: currentServerId, email }).done(res => { if (res.success) loadMailboxes(); else notice($('#wc-mb-notices'), 'error', res.data); }); }); // ── Edit modal: Reset PW ────────────────────────────────────────────── let editEmail = ''; let editType = ''; const openEditModal = (email, type, currentQuota) => { editEmail = email; editType = type; $('#wc-mb-edit-subtitle').text(email); $('#wc-mb-edit-notice').text(''); $('#wc-mb-edit-pw-section, #wc-mb-edit-quota-section').hide(); if (type === 'password') { $('#wc-mb-edit-title').text('Reset Password'); $('#wc-mb-edit-pass, #wc-mb-edit-pass2').val(''); $('#wc-mb-edit-pw-section').show(); setTimeout(() => $('#wc-mb-edit-pass').trigger('focus'), 100); } else { $('#wc-mb-edit-title').text('Set Quota'); $('#wc-mb-edit-quota').val(currentQuota || 1024); $('#wc-mb-edit-quota-section').show(); setTimeout(() => $('#wc-mb-edit-quota').trigger('focus'), 100); } $('#wc-mb-edit-modal').show(); }; $(document).on('click', '.wc-mb-reset-pw', function () { openEditModal($(this).data('email'), 'password', null); }); $(document).on('click', '.wc-mb-set-quota', function () { openEditModal($(this).data('email'), 'quota', $(this).data('quota')); }); $('#wc-mb-edit-cancel').on('click', () => $('#wc-mb-edit-modal').hide()); $(document).on('keydown', e => { if (e.key === 'Escape') $('#wc-mb-edit-modal').hide(); }); $('#wc-mb-edit-save').on('click', () => { const $note = $('#wc-mb-edit-notice').text('Saving…'); const data = { server_id: currentServerId, email: editEmail, type: editType }; if (editType === 'password') { data.password = $('#wc-mb-edit-pass').val(); data.password2 = $('#wc-mb-edit-pass2').val(); if (!data.password) { $note.html('Password cannot be empty.'); return; } if (data.password !== data.password2) { $note.html('Passwords do not match.'); return; } } else { data.quota = $('#wc-mb-edit-quota').val(); if (!data.quota || data.quota < 1) { $note.html('Enter a valid quota.'); return; } } ajax('woocow_admin_mailbox_edit', data).done(res => { if (res.success) { $('#wc-mb-edit-modal').hide(); const msg = editType === 'password' ? 'Password updated.' : 'Quota updated.'; notice($('#wc-mb-notices'), 'success', `${esc(editEmail)} — ${msg}`); if (editType === 'quota') loadMailboxes(); // refresh to show new quota } else { $note.html(`${esc(res.data)}`); } }); }); } // ── Utilities ───────────────────────────────────────────────────────────── 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'; } })(jQuery);