/** * Activity Log - Admin JavaScript * * @package King_Addons */ (function ($) { 'use strict'; const ActivityLog = { state: { page: 1, perPage: 20, filters: {}, loading: false }, /** * Initialize the Activity Log module. */ init: function () { this.bindEvents(); // Load logs if on logs tab if ($('#ka-al-table').length) { this.loadLogs(); } }, /** * Bind all event handlers. */ bindEvents: function () { // Filter events $('#ka-al-filter-apply').on('click', this.applyFilters.bind(this)); $('#ka-al-filter-reset').on('click', this.resetFilters.bind(this)); $('#ka-al-search').on('keypress', function (e) { if (e.which === 13) { ActivityLog.applyFilters(); } }); // Pagination $(document).on('click', '.ka-al-page-btn', this.handlePagination.bind(this)); $(document).on('click', '.ka-al-page-num', this.handlePageNumber.bind(this)); // View event details $(document).on('click', '.ka-al-view-btn', this.openDrawer.bind(this)); // Close drawer $('.ka-al-drawer-close, .ka-al-drawer-backdrop').on('click', this.closeDrawer.bind(this)); $(document).on('keydown', function (e) { if (e.key === 'Escape') { ActivityLog.closeDrawer(); } }); // Settings $('#ka-al-save-settings').on('click', this.saveSettings.bind(this)); $('#ka-al-purge-logs').on('click', this.purgeLogs.bind(this)); // Export $('#ka-al-export-btn').on('click', this.exportLogs.bind(this)); }, /** * Apply filters and reload logs. */ applyFilters: function () { this.state.page = 1; this.state.filters = { search: $('#ka-al-search').val(), event_key: $('#ka-al-filter-event').val(), severity: $('#ka-al-filter-severity').val(), user_id: $('#ka-al-filter-user').val(), date_from: $('#ka-al-filter-date-from').val(), date_to: $('#ka-al-filter-date-to').val() }; this.loadLogs(); }, /** * Reset all filters. */ resetFilters: function () { $('#ka-al-search').val(''); $('#ka-al-filter-event').val(''); $('#ka-al-filter-severity').val(''); $('#ka-al-filter-user').val(''); $('#ka-al-filter-date-from').val(''); $('#ka-al-filter-date-to').val(''); this.state.filters = {}; this.state.page = 1; this.loadLogs(); }, /** * Load logs via AJAX. */ loadLogs: function () { if (this.state.loading) { return; } this.state.loading = true; const $tbody = $('#ka-al-table-body'); // Show loading state $tbody.html(`
${kngActivityLog.i18n.loading} `); const data = { action: 'kng_activity_log_get_logs', nonce: kngActivityLog.nonce, page: this.state.page, per_page: this.state.perPage, ...this.state.filters }; $.post(kngActivityLog.ajaxUrl, data, (response) => { this.state.loading = false; if (!response.success) { $tbody.html(` ${kngActivityLog.i18n.error} `); return; } this.renderLogs(response.data); }).fail(() => { this.state.loading = false; $tbody.html(` ${kngActivityLog.i18n.error} `); }); }, /** * Render logs table. * * @param {Object} data Response data with items, total, pages. */ renderLogs: function (data) { const $tbody = $('#ka-al-table-body'); if (!data.items || data.items.length === 0) { $tbody.html(` ${kngActivityLog.i18n.noResults} `); this.renderPagination(0, 0); return; } let html = ''; data.items.forEach((item) => { const date = new Date(item.created_at); const formattedDate = date.toLocaleDateString() + ' ' + date.toLocaleTimeString(); html += ` ${this.escapeHtml(formattedDate)} ${this.escapeHtml(item.event_label)} ${this.escapeHtml(item.severity)} ${this.escapeHtml(item.user_login || 'System')} ${this.escapeHtml(item.object_title || '-')} ${this.escapeHtml(item.ip || '-')} `; }); $tbody.html(html); this.renderPagination(data.total, data.pages); }, /** * Render pagination controls. * * @param {number} total Total items. * @param {number} pages Total pages. */ renderPagination: function (total, pages) { const $pagination = $('#ka-al-pagination'); const $info = $pagination.find('.ka-al-pagination-info'); const $numbers = $pagination.find('.ka-al-page-numbers'); const $prevBtn = $pagination.find('[data-page="prev"]'); const $nextBtn = $pagination.find('[data-page="next"]'); // Update info const start = ((this.state.page - 1) * this.state.perPage) + 1; const end = Math.min(this.state.page * this.state.perPage, total); $info.text(`Showing ${start}-${end} of ${total} events`); // Update prev/next buttons $prevBtn.prop('disabled', this.state.page <= 1); $nextBtn.prop('disabled', this.state.page >= pages); // Render page numbers let numbersHtml = ''; const maxVisible = 5; let startPage = Math.max(1, this.state.page - Math.floor(maxVisible / 2)); let endPage = Math.min(pages, startPage + maxVisible - 1); if (endPage - startPage < maxVisible - 1) { startPage = Math.max(1, endPage - maxVisible + 1); } for (let i = startPage; i <= endPage; i++) { numbersHtml += ` `; } $numbers.html(numbersHtml); }, /** * Handle pagination button click. * * @param {Event} e Click event. */ handlePagination: function (e) { const $btn = $(e.currentTarget); const action = $btn.data('page'); if (action === 'prev' && this.state.page > 1) { this.state.page--; this.loadLogs(); } else if (action === 'next') { this.state.page++; this.loadLogs(); } }, /** * Handle page number click. * * @param {Event} e Click event. */ handlePageNumber: function (e) { const page = parseInt($(e.currentTarget).data('page'), 10); if (page !== this.state.page) { this.state.page = page; this.loadLogs(); } }, /** * Open event details drawer. * * @param {Event} e Click event. */ openDrawer: function (e) { const id = $(e.currentTarget).data('id'); const $drawer = $('#ka-al-drawer'); const $body = $drawer.find('.ka-al-drawer-body'); // Show loading $body.html(`
${kngActivityLog.i18n.loading}
`); $drawer.addClass('open'); $('body').css('overflow', 'hidden'); // Load event details $.post(kngActivityLog.ajaxUrl, { action: 'kng_activity_log_get_event', nonce: kngActivityLog.nonce, id: id }, (response) => { if (!response.success) { $body.html(`

${kngActivityLog.i18n.error}

`); return; } this.renderEventDetails(response.data, $body); }); }, /** * Render event details in drawer. * * @param {Object} event Event data. * @param {jQuery} $container Container element. */ renderEventDetails: function (event, $container) { const date = new Date(event.created_at); const formattedDate = date.toLocaleDateString() + ' ' + date.toLocaleTimeString(); let html = `
Event ${this.escapeHtml(event.event_label)}
Time ${this.escapeHtml(formattedDate)}
Severity ${this.escapeHtml(event.severity_label)}
User ${this.escapeHtml(event.user_login || 'System')} (${event.user_role || 'N/A'})
IP Address ${this.escapeHtml(event.ip || 'N/A')}
Object Type ${this.escapeHtml(event.object_type || 'N/A')}
Object ${this.escapeHtml(event.object_title || 'N/A')} (ID: ${event.object_id || 'N/A'})
Context ${this.escapeHtml(event.context)}
Source ${this.escapeHtml(event.source)}
${this.escapeHtml(event.message)}
`; // Add user agent if available if (event.user_agent) { html += `
User Agent ${this.escapeHtml(event.user_agent)}
`; } // Add data JSON if available if (event.data && Object.keys(event.data).length > 0) { html += `

Additional Data

${JSON.stringify(event.data, null, 2)}
`; } $container.html(html); }, /** * Close event details drawer. */ closeDrawer: function () { $('#ka-al-drawer').removeClass('open'); $('body').css('overflow', ''); }, /** * Save settings via AJAX. */ saveSettings: function () { const $btn = $('#ka-al-save-settings'); const $form = $('#ka-al-settings-form'); // Collect settings const settings = { enabled: $form.find('[name="enabled"]').is(':checked'), retention_days: $form.find('[name="retention_days"]').val() || 14, modules: { auth: $form.find('[name="modules[auth]"]').is(':checked'), content: $form.find('[name="modules[content]"]').is(':checked'), users: $form.find('[name="modules[users]"]').is(':checked'), plugins: $form.find('[name="modules[plugins]"]').is(':checked'), themes: $form.find('[name="modules[themes]"]').is(':checked') }, excluded_roles: $form.find('[name="excluded_roles[]"]').val() || [], ip_storage: $form.find('[name="ip_storage"]').val(), store_user_agent: $form.find('[name="store_user_agent"]').is(':checked') }; $btn.prop('disabled', true).text('Saving...'); $.post(kngActivityLog.ajaxUrl, { action: 'kng_activity_log_save_settings', nonce: kngActivityLog.nonce, settings: settings }, (response) => { $btn.prop('disabled', false).text('Save Settings'); if (response.success) { this.showNotice('success', kngActivityLog.i18n.settingsSaved); } else { this.showNotice('error', response.data?.message || kngActivityLog.i18n.error); } }).fail(() => { $btn.prop('disabled', false).text('Save Settings'); this.showNotice('error', kngActivityLog.i18n.error); }); }, /** * Purge all logs. */ purgeLogs: function () { if (!confirm(kngActivityLog.i18n.confirmPurge)) { return; } const $btn = $('#ka-al-purge-logs'); $btn.prop('disabled', true).text('Purging...'); $.post(kngActivityLog.ajaxUrl, { action: 'kng_activity_log_purge', nonce: kngActivityLog.nonce }, (response) => { $btn.prop('disabled', false).text('Purge All Logs'); if (response.success) { this.showNotice('success', response.data.message); } else { this.showNotice('error', response.data?.message || kngActivityLog.i18n.error); } }).fail(() => { $btn.prop('disabled', false).text('Purge All Logs'); this.showNotice('error', kngActivityLog.i18n.error); }); }, /** * Export logs to CSV. */ exportLogs: function () { const $btn = $('#ka-al-export-btn'); const format = $('input[name="export_format"]:checked').val() || 'csv'; $btn.prop('disabled', true); $.post(kngActivityLog.ajaxUrl, { action: 'kng_activity_log_export', nonce: kngActivityLog.nonce, ...this.state.filters }, (response) => { $btn.prop('disabled', false); if (!response.success) { this.showNotice('error', response.data?.message || kngActivityLog.i18n.error); return; } // Create and trigger download const blob = new Blob([response.data.content], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); const url = URL.createObjectURL(blob); link.setAttribute('href', url); link.setAttribute('download', response.data.filename); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); this.showNotice('success', kngActivityLog.i18n.exportSuccess); }).fail(() => { $btn.prop('disabled', false); this.showNotice('error', kngActivityLog.i18n.error); }); }, /** * Show a temporary notice. * * @param {string} type Notice type (success, error). * @param {string} message Message text. */ showNotice: function (type, message) { // Remove any existing notices $('.ka-al-notice').remove(); const bgColor = type === 'success' ? 'rgba(48, 209, 88, 0.95)' : 'rgba(255, 69, 58, 0.95)'; const $notice = $(`
${this.escapeHtml(message)}
`); $('body').append($notice); setTimeout(() => { $notice.fadeOut(300, function () { $(this).remove(); }); }, 3000); }, /** * Escape HTML for safe rendering. * * @param {string} str String to escape. * @returns {string} Escaped string. */ escapeHtml: function (str) { if (!str) return ''; const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } }; // Initialize on DOM ready $(document).ready(function () { ActivityLog.init(); }); })(jQuery);