diff --git a/apps/channels/api_views.py b/apps/channels/api_views.py index dbdd4271..0126aaf9 100644 --- a/apps/channels/api_views.py +++ b/apps/channels/api_views.py @@ -1052,12 +1052,28 @@ class BulkDeleteLogosAPIView(APIView): ) def delete(self, request): logo_ids = request.data.get("logo_ids", []) + delete_files = request.data.get("delete_files", False) # Get logos and their usage info before deletion logos_to_delete = Logo.objects.filter(id__in=logo_ids) total_channels_affected = 0 - + local_files_deleted = 0 + for logo in logos_to_delete: + # Handle file deletion for local files + if delete_files and logo.url and logo.url.startswith('/data/logos'): + try: + if os.path.exists(logo.url): + os.remove(logo.url) + local_files_deleted += 1 + logger.info(f"Deleted local logo file: {logo.url}") + except Exception as e: + logger.error(f"Failed to delete logo file {logo.url}: {str(e)}") + return Response( + {"error": f"Failed to delete logo file {logo.url}: {str(e)}"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + if logo.channels.exists(): channel_count = logo.channels.count() total_channels_affected += channel_count @@ -1071,6 +1087,8 @@ class BulkDeleteLogosAPIView(APIView): message = f"Successfully deleted {deleted_count} logos" if total_channels_affected > 0: message += f" and removed them from {total_channels_affected} channels" + if local_files_deleted > 0: + message += f" and deleted {local_files_deleted} local files" return Response( {"message": message}, @@ -1157,6 +1175,20 @@ class LogoViewSet(viewsets.ModelViewSet): def destroy(self, request, *args, **kwargs): """Delete a logo and remove it from any channels using it""" logo = self.get_object() + delete_file = request.query_params.get('delete_file', 'false').lower() == 'true' + + # Check if it's a local file that should be deleted + if delete_file and logo.url and logo.url.startswith('/data/logos'): + try: + if os.path.exists(logo.url): + os.remove(logo.url) + logger.info(f"Deleted local logo file: {logo.url}") + except Exception as e: + logger.error(f"Failed to delete logo file {logo.url}: {str(e)}") + return Response( + {"error": f"Failed to delete logo file: {str(e)}"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) # Instead of preventing deletion, remove the logo from channels if logo.channels.exists(): diff --git a/frontend/src/api.js b/frontend/src/api.js index bcffc920..b285e2ea 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -1337,9 +1337,15 @@ export default class API { } } - static async deleteLogo(id) { + static async deleteLogo(id, deleteFile = false) { try { - await request(`${host}/api/channels/logos/${id}/`, { + const params = new URLSearchParams(); + if (deleteFile) { + params.append('delete_file', 'true'); + } + + const url = `${host}/api/channels/logos/${id}/?${params.toString()}`; + await request(url, { method: 'DELETE', }); @@ -1351,11 +1357,16 @@ export default class API { } } - static async deleteLogos(ids) { + static async deleteLogos(ids, deleteFiles = false) { try { + const body = { logo_ids: ids }; + if (deleteFiles) { + body.delete_files = true; + } + await request(`${host}/api/channels/logos/bulk-delete/`, { method: 'DELETE', - body: { logo_ids: ids }, + body: body, }); // Remove multiple logos from store diff --git a/frontend/src/components/ConfirmationDialog.jsx b/frontend/src/components/ConfirmationDialog.jsx index 8f96708d..1cfbe84d 100644 --- a/frontend/src/components/ConfirmationDialog.jsx +++ b/frontend/src/components/ConfirmationDialog.jsx @@ -29,12 +29,15 @@ const ConfirmationDialog = ({ onSuppressChange, size = 'md', zIndex = 1000, + showDeleteFileOption = false, + deleteFileLabel = "Also delete files from disk", }) => { const suppressWarning = useWarningsStore((s) => s.suppressWarning); const isWarningSuppressed = useWarningsStore((s) => s.isWarningSuppressed); const [suppressChecked, setSuppressChecked] = useState( isWarningSuppressed(actionKey) ); + const [deleteFiles, setDeleteFiles] = useState(false); const handleToggleSuppress = (e) => { setSuppressChecked(e.currentTarget.checked); @@ -47,13 +50,23 @@ const ConfirmationDialog = ({ if (suppressChecked) { suppressWarning(actionKey); } - onConfirm(); + if (showDeleteFileOption) { + onConfirm(deleteFiles); + } else { + onConfirm(); + } + setDeleteFiles(false); // Reset for next time + }; + + const handleClose = () => { + setDeleteFiles(false); // Reset for next time + onClose(); }; return ( )} + {showDeleteFileOption && ( + setDeleteFiles(event.currentTarget.checked)} + label={deleteFileLabel} + mb="md" + /> + )} + -