diff --git a/app/Http/Controllers/Admin/AdminAjaxController.php b/app/Http/Controllers/Admin/AdminAjaxController.php index a0dbc1cc1..f02e0bbec 100644 --- a/app/Http/Controllers/Admin/AdminAjaxController.php +++ b/app/Http/Controllers/Admin/AdminAjaxController.php @@ -13,77 +13,96 @@ class AdminAjaxController extends BasePageController /** * @throws \Throwable */ - public function ajaxAction(Request $request): void + public function ajaxAction(Request $request) { if ($request->missing('action')) { - exit(); + return response()->json(['success' => false, 'message' => 'No action specified'], 400); } $settings = ['Settings' => $this->settings]; - switch ($request->input('action')) { - case 'binary_blacklist_delete': - $id = (int) $request->input('row_id'); - (new Binaries($settings))->deleteBlacklist($id); - echo "Blacklist $id deleted."; - break; - case 'category_regex_delete': - $id = (int) $request->input('row_id'); - (new Regexes(['Settings' => $this->settings, 'Table_Name' => 'category_regexes']))->deleteRegex($id); - echo "Regex $id deleted."; - break; + try { + switch ($request->input('action')) { + case 'binary_blacklist_delete': + $id = (int) $request->input('row_id'); + (new Binaries($settings))->deleteBlacklist($id); + echo "Blacklist $id deleted."; + break; - case 'collection_regex_delete': - $id = (int) $request->input('row_id'); - (new Regexes(['Settings' => $this->settings, 'Table_Name' => 'collection_regexes']))->deleteRegex($id); - echo "Regex $id deleted."; - break; + case 'category_regex_delete': + $id = (int) $request->input('row_id'); + (new Regexes(['Settings' => $this->settings, 'Table_Name' => 'category_regexes']))->deleteRegex($id); + echo "Regex $id deleted."; + break; - case 'release_naming_regex_delete': - $id = (int) $request->input('row_id'); - (new Regexes(['Settings' => $this->settings, 'Table_Name' => 'release_naming_regexes']))->deleteRegex($id); - echo "Regex $id deleted."; - break; + case 'collection_regex_delete': + $id = (int) $request->input('row_id'); + (new Regexes(['Settings' => $this->settings, 'Table_Name' => 'collection_regexes']))->deleteRegex($id); + echo "Regex $id deleted."; + break; - case 'group_edit_purge_all': - UsenetGroup::purge(); - echo 'All groups purged.'; - break; + case 'release_naming_regex_delete': + $id = (int) $request->input('row_id'); + (new Regexes(['Settings' => $this->settings, 'Table_Name' => 'release_naming_regexes']))->deleteRegex($id); + echo "Regex $id deleted."; + break; - case 'group_edit_reset_all': - UsenetGroup::resetall(); - echo 'All groups reset.'; - break; + case 'group_edit_purge_all': + case 'purge_all_groups': + UsenetGroup::purge(); + return response()->json(['success' => true, 'message' => 'All groups purged successfully']); - case 'group_edit_purge_single': - $id = (int) $request->input('group_id'); - UsenetGroup::purge($id); - echo "Group $id purged."; - break; + case 'group_edit_reset_all': + case 'reset_all_groups': + UsenetGroup::resetall(); + return response()->json(['success' => true, 'message' => 'All groups reset successfully']); - case 'group_edit_reset_single': - $id = (int) $request->input('group_id'); - UsenetGroup::reset($id); - echo "Group $id reset."; - break; + case 'group_edit_purge_single': + case 'purge_group': + $id = (int) $request->input('group_id'); + UsenetGroup::purge($id); + return response()->json(['success' => true, 'message' => "Group $id purged successfully"]); - case 'group_edit_delete_single': - $id = (int) $request->input('group_id'); - UsenetGroup::deleteGroup($id); - echo "Group $id deleted."; - break; + case 'group_edit_reset_single': + case 'reset_group': + $id = (int) $request->input('group_id'); + UsenetGroup::reset($id); + return response()->json(['success' => true, 'message' => "Group $id reset successfully"]); - case 'toggle_group_active_status': - print UsenetGroup::updateGroupStatus((int) $request->input('group_id'), 'active', ($request->has('group_status') ? (int) $request->input('group_status') : 0)); - break; + case 'group_edit_delete_single': + case 'delete_group': + $id = (int) $request->input('group_id'); + UsenetGroup::deleteGroup($id); + return response()->json(['success' => true, 'message' => "Group $id deleted successfully"]); - case 'toggle_group_backfill_status': - print UsenetGroup::updateGroupStatus( - (int) $request->input('group_id'), - 'backfill', - ($request->has('backfill_status') ? (int) $request->input('backfill_status') : 0) - ); - break; + case 'toggle_group_active_status': + $groupId = (int) $request->input('group_id'); + $status = $request->has('group_status') ? (int) $request->input('group_status') : 0; + $message = UsenetGroup::updateGroupStatus($groupId, 'active', $status); + return response()->json([ + 'success' => true, + 'message' => $message, + 'newStatus' => $status + ]); + + case 'toggle_group_backfill_status': + case 'toggle_group_backfill': + $groupId = (int) $request->input('group_id'); + $status = $request->has('backfill_status') + ? (int) $request->input('backfill_status') + : ($request->has('backfill') ? (int) $request->input('backfill') : 0); + $message = UsenetGroup::updateGroupStatus($groupId, 'backfill', $status); + return response()->json([ + 'success' => true, + 'message' => $message, + 'newStatus' => $status + ]); + + default: + return response()->json(['success' => false, 'message' => 'Unknown action'], 400); + } + } catch (\Exception $e) { + return response()->json(['success' => false, 'message' => $e->getMessage()], 500); } } } diff --git a/resources/js/csp-safe.js b/resources/js/csp-safe.js index c42aba512..2ff84a6ad 100644 --- a/resources/js/csp-safe.js +++ b/resources/js/csp-safe.js @@ -3144,8 +3144,321 @@ function initializeSystemMetricsCharts() { } function initAdminGroups() { - // Placeholder for admin groups functionality - // Group management is handled in event delegation + // Get AJAX URL and CSRF token from page + const container = document.querySelector('[data-ajax-url]'); + const ajaxUrl = container ? container.dataset.ajaxUrl : '/admin/ajax'; + const csrfToken = container ? container.dataset.csrfToken : document.querySelector('meta[name="csrf-token"]')?.content; + + // Toggle group active/inactive status + window.ajax_group_status = function(id, status) { + fetch(ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-CSRF-TOKEN': csrfToken, + 'X-Requested-With': 'XMLHttpRequest' + }, + body: new URLSearchParams({ + action: 'toggle_group_active_status', + group_id: id, + group_status: status + }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + const groupCell = document.getElementById('group-' + id); + if (groupCell && data.newStatus !== undefined) { + const isActive = data.newStatus == 1; + groupCell.innerHTML = isActive + ? `` + : ``; + } + if (typeof showToast === 'function') { + showToast(data.message || 'Group status updated', 'success'); + } + } else { + if (typeof showToast === 'function') { + showToast(data.message || 'Error updating group status', 'error'); + } + } + }) + .catch(error => { + console.error('Error:', error); + if (typeof showToast === 'function') { + showToast('Error updating group status', 'error'); + } + }); + }; + + // Toggle backfill status + window.ajax_backfill_status = function(id, status) { + fetch(ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-CSRF-TOKEN': csrfToken, + 'X-Requested-With': 'XMLHttpRequest' + }, + body: new URLSearchParams({ + action: 'toggle_group_backfill', + group_id: id, + backfill: status + }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + const backfillCell = document.getElementById('backfill-' + id); + if (backfillCell && data.newStatus !== undefined) { + const isEnabled = data.newStatus == 1; + backfillCell.innerHTML = isEnabled + ? `` + : ``; + } + if (typeof showToast === 'function') { + showToast(data.message || 'Backfill status updated', 'success'); + } + } else { + if (typeof showToast === 'function') { + showToast(data.message || 'Error updating backfill status', 'error'); + } + } + }) + .catch(error => { + console.error('Error:', error); + if (typeof showToast === 'function') { + showToast('Error updating backfill status', 'error'); + } + }); + }; + + // Reset group + window.ajax_group_reset = function(id) { + if (!confirm('Are you sure you want to reset this group? This will reset the article pointers back to the current state.')) { + return; + } + + fetch(ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-CSRF-TOKEN': csrfToken, + 'X-Requested-With': 'XMLHttpRequest' + }, + body: new URLSearchParams({ + action: 'reset_group', + group_id: id + }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + if (typeof showToast === 'function') { + showToast(data.message || 'Group reset successfully', 'success'); + } + } else { + if (typeof showToast === 'function') { + showToast(data.message || 'Error resetting group', 'error'); + } + } + }) + .catch(error => { + console.error('Error:', error); + if (typeof showToast === 'function') { + showToast('Error resetting group', 'error'); + } + }); + }; + + // Delete group + window.confirmGroupDelete = function(id) { + if (!confirm('Are you sure you want to delete this group? This action cannot be undone.')) { + return; + } + + fetch(ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-CSRF-TOKEN': csrfToken, + 'X-Requested-With': 'XMLHttpRequest' + }, + body: new URLSearchParams({ + action: 'delete_group', + group_id: id + }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + const row = document.getElementById('grouprow-' + id); + if (row) { + row.style.transition = 'opacity 0.3s'; + row.style.opacity = '0'; + setTimeout(() => row.remove(), 300); + } + if (typeof showToast === 'function') { + showToast(data.message || 'Group deleted successfully', 'success'); + } + } else { + if (typeof showToast === 'function') { + showToast(data.message || 'Error deleting group', 'error'); + } + } + }) + .catch(error => { + console.error('Error:', error); + if (typeof showToast === 'function') { + showToast('Error deleting group', 'error'); + } + }); + }; + + // Purge group + window.confirmGroupPurge = function(id) { + if (!confirm('Are you sure you want to purge this group? This will delete all releases and binaries for this group. This action cannot be undone!')) { + return; + } + + fetch(ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-CSRF-TOKEN': csrfToken, + 'X-Requested-With': 'XMLHttpRequest' + }, + body: new URLSearchParams({ + action: 'purge_group', + group_id: id + }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + if (typeof showToast === 'function') { + showToast(data.message || 'Group purged successfully', 'success'); + } + } else { + if (typeof showToast === 'function') { + showToast(data.message || 'Error purging group', 'error'); + } + } + }) + .catch(error => { + console.error('Error:', error); + if (typeof showToast === 'function') { + showToast('Error purging group', 'error'); + } + }); + }; + + // Reset all groups + window.ajax_group_reset_all = function() { + hideResetAllModal(); + + fetch(ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-CSRF-TOKEN': csrfToken, + 'X-Requested-With': 'XMLHttpRequest' + }, + body: new URLSearchParams({ + action: 'reset_all_groups' + }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + if (typeof showToast === 'function') { + showToast(data.message || 'All groups reset successfully', 'success'); + } + } else { + if (typeof showToast === 'function') { + showToast(data.message || 'Error resetting all groups', 'error'); + } + } + }) + .catch(error => { + console.error('Error:', error); + if (typeof showToast === 'function') { + showToast('Error resetting all groups', 'error'); + } + }); + }; + + // Purge all groups + window.ajax_group_purge_all = function() { + hidePurgeAllModal(); + + fetch(ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-CSRF-TOKEN': csrfToken, + 'X-Requested-With': 'XMLHttpRequest' + }, + body: new URLSearchParams({ + action: 'purge_all_groups' + }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + if (typeof showToast === 'function') { + showToast(data.message || 'All groups purged successfully', 'success'); + } + } else { + if (typeof showToast === 'function') { + showToast(data.message || 'Error purging all groups', 'error'); + } + } + }) + .catch(error => { + console.error('Error:', error); + if (typeof showToast === 'function') { + showToast('Error purging all groups', 'error'); + } + }); + }; + + // Modal helpers + window.showResetAllModal = function() { + const modal = document.getElementById('resetAllModal'); + if (modal) { + modal.classList.remove('hidden'); + } + }; + + window.hideResetAllModal = function() { + const modal = document.getElementById('resetAllModal'); + if (modal) { + modal.classList.add('hidden'); + } + }; + + window.showPurgeAllModal = function() { + const modal = document.getElementById('purgeAllModal'); + if (modal) { + modal.classList.remove('hidden'); + } + }; + + window.hidePurgeAllModal = function() { + const modal = document.getElementById('purgeAllModal'); + if (modal) { + modal.classList.add('hidden'); + } + }; } // TinyMCE Initialization for Admin Content Pages