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"
+ />
+ )}
+
-