From b0aab62b3a5fe6b7114b5c58db129b3744b4d8cc Mon Sep 17 00:00:00 2001 From: Horilla Date: Mon, 19 May 2025 14:02:22 +0530 Subject: [PATCH] [FIX] PROJECT: Remove unsafe use of eval() in project_bulk_archive to prevent RCE --- project/cbv/projects.py | 5 + project/static/project/project_action.js | 854 ++++++------------ .../templates/cbv/projects/project_nav.html | 13 +- project/views.py | 102 ++- 4 files changed, 366 insertions(+), 608 deletions(-) diff --git a/project/cbv/projects.py b/project/cbv/projects.py index 526cba95d..1890fc552 100644 --- a/project/cbv/projects.py +++ b/project/cbv/projects.py @@ -88,6 +88,8 @@ class ProjectsNavView(HorillaNavView): "attrs": """ id="archiveProject" style="cursor: pointer;" + onclick="validateProjectIds(event);" + data-action="archive" """, }, { @@ -95,6 +97,8 @@ class ProjectsNavView(HorillaNavView): "attrs": """ id="unArchiveProject" style="cursor: pointer;" + onclick="validateProjectIds(event);" + data-action="unarchive" """, }, { @@ -103,6 +107,7 @@ class ProjectsNavView(HorillaNavView): class="oh-dropdown__link--danger" data-action ="delete" id="deleteProject" + onclick="validateProjectIds(event);" style="cursor: pointer; color:red !important" """, }, diff --git a/project/static/project/project_action.js b/project/static/project/project_action.js index 5f31947fa..626a0adfa 100644 --- a/project/static/project/project_action.js +++ b/project/static/project/project_action.js @@ -1,631 +1,339 @@ -var archiveMessagesSelected = { - // ar: "هل ترغب حقًا في أرشفة جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter archivieren?", - // es: "¿Realmente quieres archivar a todos los empleados seleccionados?", - en: "Do you really want to archive all the selected projects?", - // fr: "Voulez-vous vraiment archiver tous les employés sélectionnés ?", - }; - - var unarchiveMessagesSelected = { - // ar: "هل ترغب حقًا في إلغاء أرشفة جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter aus der Archivierung zurückholen?", - // es: "¿Realmente quieres desarchivar a todos los empleados seleccionados?", - en: "Do you really want to unarchive all the selected projects?", - // fr: "Voulez-vous vraiment désarchiver tous les employés sélectionnés?", - }; - - var deleteMessage = { - // ar: "هل ترغب حقًا في حذف جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter löschen?", - // es: "¿Realmente quieres eliminar a todos los empleados seleccionados?", - en: "Do you really want to delete all the selected projects?", - // fr: "Voulez-vous vraiment supprimer tous les employés sélectionnés?", - }; - - var exportMessages = { +var exportMessages = { // ar: "هل ترغب حقًا في حذف جميع الموظفين المحددين؟", // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter löschen?", // es: "¿Realmente quieres eliminar a todos los empleados seleccionados?", en: "Do you really want to export all the selected projects?", // fr: "Voulez-vous vraiment supprimer tous les employés sélectionnés?", - }; +}; - var downloadMessages = { +var downloadMessages = { ar: "هل ترغب في تنزيل القالب؟", de: "Möchten Sie die Vorlage herunterladen?", es: "¿Quieres descargar la plantilla?", en: "Do you want to download the template?", fr: "Voulez-vous télécharger le modèle ?", - }; +}; - var norowMessagesSelected = { +var norowMessagesSelected = { // ar: "لم يتم تحديد أي صفوف.", // de: "Es wurden keine Zeilen ausgewählt.", // es: "No se han seleccionado filas.", en: "No rows have been selected.", // fr: "Aucune ligne n'a été sélectionnée.", - }; +}; - function getCookie(name) { +function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== "") { - const cookies = document.cookie.split(";"); - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i].trim(); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) === name + "=") { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; + const cookies = document.cookie.split(";"); + for (let i = 0; i < cookies.length; i++) { + const cookie = cookies[i].trim(); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) === name + "=") { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } } - } } return cookieValue; - } +} - function getCurrentLanguageCode(callback) { +function getCurrentLanguageCode(callback) { $.ajax({ - type: "GET", - url: "/employee/get-language-code/", - success: function (response) { - var languageCode = response.language_code; - callback(languageCode); // Pass the language code to the callback - }, + type: "GET", + url: "/employee/get-language-code/", + success: function (response) { + var languageCode = response.language_code; + callback(languageCode); // Pass the language code to the callback + }, }); - } +} +function validateProjectIds(event) { + getCurrentLanguageCode(function (code) { + languageCode = code; + var textMessage = norowMessagesSelected[languageCode]; + var takeAction = $(event.currentTarget).data("action"); - // // Get the form element - // var form = document.getElementById("projectImportForm"); + var idsRaw = $("#selectedInstances").attr("data-ids"); + if (!idsRaw) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + return; + } - // // Add an event listener to the form submission - // form.addEventListener("submit", function (event) { - // // Prevent the default form submission - // event.preventDefault(); + var ids = JSON.parse(idsRaw); + if (ids.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + return; + } - // // Create a new form data object - // var formData = new FormData(); + let triggerId; + if (takeAction === "archive") { + triggerId = "#bulkArchiveProject"; + } else if (takeAction === "unarchive") { + triggerId = "#bulkUnArchiveProject"; + } else if (takeAction === "delete") { + triggerId = "#bulkDeleteProject"; + } else { + console.warn("Unsupported action:", takeAction); + return; + } - // // Append the file to the form data object - // var fileInput = document.querySelector("#projectImportFile"); - // formData.append("file", fileInput.files[0]); - // $.ajax({ - // type: "POST", - // url: "/project/project-import", - // dataType: "binary", - // data: formData, - // processData: false, - // contentType: false, - // headers: { - // "X-CSRFToken": getCookie('csrftoken'), // Replace with your csrf token value - // }, - // xhrFields: { - // responseType: "blob", - // }, - // success: function (response) { - // const file = new Blob([response], { - // type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - // }); - // const url = URL.createObjectURL(file); - // const link = document.createElement("a"); - // link.href = url; - // link.download = "ImportError.xlsx"; - // document.body.appendChild(link); - // link.click(); - // }, - // error: function (xhr, textStatus, errorThrown) { - // console.error("Error downloading file:", errorThrown); - // }, - // }); - // }); + const $triggerElement = $(triggerId); + if ($triggerElement.length) { + $triggerElement.attr("hx-vals", JSON.stringify({ ids })).click(); + } else { + console.warn("Trigger element not found for:", triggerId); + } + }); +} - // $("#importProject").click(function (e) { - // e.preventDefault(); - // var languageCode = null; - // getCurrentLanguageCode(function (code) { - // languageCode = code; - // var confirmMessage = downloadMessages[languageCode]; - // // Use SweetAlert for the confirmation dialog - // Swal.fire({ - // text: confirmMessage, - // icon: 'question', - // showCancelButton: true, - // confirmButtonColor: '#008000', - // cancelButtonColor: '#d33', - // confirmButtonText: 'Confirm' - // }).then(function(result) { - // if (result.isConfirmed) { - // $.ajax({ - // type: "GET", - // url: "/project/project-import", - // dataType: "binary", - // xhrFields: { - // responseType: "blob", - // }, - // success: function (response) { - // const file = new Blob([response], { - // type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - // }); - // const url = URL.createObjectURL(file); - // const link = document.createElement("a"); - // link.href = url; - // link.download = "project_template.xlsx"; - // document.body.appendChild(link); - // link.click(); - // }, - // error: function (xhr, textStatus, errorThrown) { - // console.error("Error downloading file:", errorThrown); - // }, - // }); - // } - // }); - // }); - // }); - - // $('#importProject').click(function (e) { - // $.ajax({ - // type: 'POST', - // url: '/project/project-import', - // dataType: 'binary', - // xhrFields: { - // responseType: 'blob' - // }, - // success: function(response) { - // const file = new Blob([response], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}); - // const url = URL.createObjectURL(file); - // const link = document.createElement('a'); - // link.href = url; - // link.download = 'project.xlsx'; - // document.body.appendChild(link); - // link.click(); - // }, - // error: function(xhr, textStatus, errorThrown) { - // console.error('Error downloading file:', errorThrown); - // } - // }); - // }); - - - $(".all-projects").change(function (e) { +$(".all-projects").change(function (e) { var is_checked = $(this).is(":checked"); if (is_checked) { - $(".all-project-row").prop("checked", true); + $(".all-project-row").prop("checked", true); } else { - $(".all-project-row").prop("checked", false); + $(".all-project-row").prop("checked", false); } - }); +}); - $("#exportProject").click(function (e) { +$("#exportProject").click(function (e) { e.preventDefault(); var languageCode = null; getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = exportMessages[languageCode]; - var textMessage = norowMessagesSelected[languageCode]; - var checkedRows = $(".all-project-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "info", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - var checkedRows = $(".all-project-row").filter(":checked"); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); + languageCode = code; + var confirmMessage = exportMessages[languageCode]; + var textMessage = norowMessagesSelected[languageCode]; + var checkedRows = $(".all-project-row").filter(":checked"); + if (checkedRows.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + var checkedRows = $(".all-project-row").filter(":checked"); + ids = []; + checkedRows.each(function () { + ids.push($(this).attr("id")); + }); - $.ajax({ - type: "POST", - url: "/project/project-bulk-export", - dataType: "binary", - xhrFields: { - responseType: "blob", - }, - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - const file = new Blob([response], { - type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - }); - const url = URL.createObjectURL(file); - const link = document.createElement("a"); - link.href = url; - link.download = "project details.xlsx"; - document.body.appendChild(link); - link.click(); - }, - }); - } - }); - } - }); - }); - - $(document).on('click', '#exportProject', function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = exportMessages[languageCode]; - var textMessage = norowMessagesSelected[languageCode]; - ids = []; - ids.push($("#selectedInstances").attr("data-ids")); - ids = JSON.parse($("#selectedInstances").attr("data-ids")); - if (ids.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "info", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - // var checkedRows = $(".all-project-row").filter(":checked"); - // ids = []; - // checkedRows.each(function () { - // ids.push($(this).attr("id")); - // }); - - $.ajax({ - type: "POST", - url: "/project/project-bulk-export", - dataType: "binary", - xhrFields: { - responseType: "blob", - }, - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - const file = new Blob([response], { - type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - }); - const url = URL.createObjectURL(file); - const link = document.createElement("a"); - link.href = url; - link.download = "project details.xlsx"; - document.body.appendChild(link); - link.click(); - }, - }); - } - }); - } - }); - }); - - - $("#archiveProject").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = archiveMessagesSelected[languageCode]; - var textMessage = norowMessagesSelected[languageCode]; - var checkedRows = $(".all-project-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "info", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - e.preventDefault(); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); - }); - - $.ajax({ - type: "POST", - url: "/project/project-bulk-archive?is_active=False", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); + $.ajax({ + type: "POST", + url: "/project/project-bulk-export", + dataType: "binary", + xhrFields: { + responseType: "blob", + }, + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + const file = new Blob([response], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + const url = URL.createObjectURL(file); + const link = document.createElement("a"); + link.href = url; + link.download = "project details.xlsx"; + document.body.appendChild(link); + link.click(); + }, + }); } - }, }); - } - }); - } + } }); - }); +}); - - $(document).on('click', '#archiveProject', function (e) { +$(document).on('click', '#exportProject', function (e) { e.preventDefault(); var languageCode = null; getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = archiveMessagesSelected[languageCode]; - var textMessage = norowMessagesSelected[languageCode]; - ids = []; - ids.push($("#selectedInstances").attr("data-ids")); - ids = JSON.parse($("#selectedInstances").attr("data-ids")); - if (ids.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "info", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - // e.preventDefault(); - // ids = []; - // checkedRows.each(function () { - // ids.push($(this).attr("id")); - // }); + languageCode = code; + var confirmMessage = exportMessages[languageCode]; + var textMessage = norowMessagesSelected[languageCode]; + ids = []; + ids.push($("#selectedInstances").attr("data-ids")); + ids = JSON.parse($("#selectedInstances").attr("data-ids")); + if (ids.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + // var checkedRows = $(".all-project-row").filter(":checked"); + // ids = []; + // checkedRows.each(function () { + // ids.push($(this).attr("id")); + // }); - $.ajax({ - type: "POST", - url: "/project/project-bulk-archive?is_active=False", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); + $.ajax({ + type: "POST", + url: "/project/project-bulk-export", + dataType: "binary", + xhrFields: { + responseType: "blob", + }, + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + const file = new Blob([response], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + const url = URL.createObjectURL(file); + const link = document.createElement("a"); + link.href = url; + link.download = "project details.xlsx"; + document.body.appendChild(link); + link.click(); + }, + }); } - }, }); - } - }); - } + } }); - }); +}); -$("#unArchiveProject").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = unarchiveMessagesSelected[languageCode]; - var textMessage = norowMessagesSelected[languageCode]; - var checkedRows = $(".all-project-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "info", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - var checkedRows = $(".all-project-row").filter(":checked"); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); - }); - - $.ajax({ - type: "POST", - url: "/project/project-bulk-archive?is_active=True", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); - } - }, - }); - } - }); - } - }); - }); - - $(document).on('click', '#unArchiveProject', function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = unarchiveMessagesSelected[languageCode]; - var textMessage = norowMessagesSelected[languageCode]; - ids = []; - ids.push($("#selectedInstances").attr("data-ids")); - ids = JSON.parse($("#selectedInstances").attr("data-ids")); - if (ids.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "info", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - // var checkedRows = $(".all-project-row").filter(":checked"); - // ids = []; - // checkedRows.each(function () { - // ids.push($(this).attr("id")); - // }); - - $.ajax({ - type: "POST", - url: "/project/project-bulk-archive?is_active=True", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); - } - }, - }); - } - }); - } - }); - }); - -$("#deleteProject").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = deleteMessage[languageCode]; - var textMessage = norowMessagesSelected[languageCode]; - var checkedRows = $(".all-project-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "error", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - var checkedRows = $(".all-project-row").filter(":checked"); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); - }); - - $.ajax({ - type: "POST", - url: "/project/project-bulk-delete", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); - } - }, - }); - } - }); - } - }); - }); - $(document).on('click', '#deleteProject', function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = deleteMessage[languageCode]; - var textMessage = norowMessagesSelected[languageCode]; - ids = []; - ids.push($("#selectedInstances").attr("data-ids")); - ids = JSON.parse($("#selectedInstances").attr("data-ids")); - if (ids.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "error", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - // var checkedRows = $(".all-project-row").filter(":checked"); - // ids = []; - // checkedRows.each(function () { - // ids.push($(this).attr("id")); - // }); +// // Get the form element +// var form = document.getElementById("projectImportForm"); - $.ajax({ - type: "POST", - url: "/project/project-bulk-delete", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); - } - }, - }); - } - }); - } - }); - }); +// // Add an event listener to the form submission +// form.addEventListener("submit", function (event) { +// // Prevent the default form submission +// event.preventDefault(); + +// // Create a new form data object +// var formData = new FormData(); + +// // Append the file to the form data object +// var fileInput = document.querySelector("#projectImportFile"); +// formData.append("file", fileInput.files[0]); +// $.ajax({ +// type: "POST", +// url: "/project/project-import", +// dataType: "binary", +// data: formData, +// processData: false, +// contentType: false, +// headers: { +// "X-CSRFToken": getCookie('csrftoken'), // Replace with your csrf token value +// }, +// xhrFields: { +// responseType: "blob", +// }, +// success: function (response) { +// const file = new Blob([response], { +// type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", +// }); +// const url = URL.createObjectURL(file); +// const link = document.createElement("a"); +// link.href = url; +// link.download = "ImportError.xlsx"; +// document.body.appendChild(link); +// link.click(); +// }, +// error: function (xhr, textStatus, errorThrown) { +// console.error("Error downloading file:", errorThrown); +// }, +// }); +// }); + +// $("#importProject").click(function (e) { +// e.preventDefault(); +// var languageCode = null; +// getCurrentLanguageCode(function (code) { +// languageCode = code; +// var confirmMessage = downloadMessages[languageCode]; +// // Use SweetAlert for the confirmation dialog +// Swal.fire({ +// text: confirmMessage, +// icon: 'question', +// showCancelButton: true, +// confirmButtonColor: '#008000', +// cancelButtonColor: '#d33', +// confirmButtonText: 'Confirm' +// }).then(function(result) { +// if (result.isConfirmed) { +// $.ajax({ +// type: "GET", +// url: "/project/project-import", +// dataType: "binary", +// xhrFields: { +// responseType: "blob", +// }, +// success: function (response) { +// const file = new Blob([response], { +// type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", +// }); +// const url = URL.createObjectURL(file); +// const link = document.createElement("a"); +// link.href = url; +// link.download = "project_template.xlsx"; +// document.body.appendChild(link); +// link.click(); +// }, +// error: function (xhr, textStatus, errorThrown) { +// console.error("Error downloading file:", errorThrown); +// }, +// }); +// } +// }); +// }); +// }); + +// $('#importProject').click(function (e) { +// $.ajax({ +// type: 'POST', +// url: '/project/project-import', +// dataType: 'binary', +// xhrFields: { +// responseType: 'blob' +// }, +// success: function(response) { +// const file = new Blob([response], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}); +// const url = URL.createObjectURL(file); +// const link = document.createElement('a'); +// link.href = url; +// link.download = 'project.xlsx'; +// document.body.appendChild(link); +// link.click(); +// }, +// error: function(xhr, textStatus, errorThrown) { +// console.error('Error downloading file:', errorThrown); +// } +// }); +// }); diff --git a/project/templates/cbv/projects/project_nav.html b/project/templates/cbv/projects/project_nav.html index c893659f4..347f79915 100644 --- a/project/templates/cbv/projects/project_nav.html +++ b/project/templates/cbv/projects/project_nav.html @@ -1,9 +1,18 @@ {% load i18n %}
{% include "generic/horilla_nav.html" %} + + + + + +
diff --git a/project/views.py b/project/views.py index 10496b681..4924169c2 100644 --- a/project/views.py +++ b/project/views.py @@ -527,7 +527,7 @@ def project_import(request): @login_required -# @permission_required("employee.delete_employee") +# @permission_required("project.view_project") # @require_http_methods(["POST"]) def project_bulk_export(request): """ @@ -618,48 +618,84 @@ def project_bulk_export(request): @login_required -# @project_delete_permission def project_bulk_archive(request): - """ - This method is used to archive bulk of Project instances - """ - ids = request.POST["ids"] - ids = json.loads(ids) + try: + ids = request.POST.getlist("ids") + except Exception: + messages.error(request, _("Could not retrieve project IDs.")) + return HttpResponse("") + + is_active_raw = request.GET.get("is_active", "").lower() + + if is_active_raw in ["true"]: + is_active = True + message = "Un-Archived" + elif is_active_raw in ["false"]: + is_active = False + message = "Archived" + else: + messages.error( + request, _("Invalid value for 'is_active'. Use 'true' or 'false'.") + ) + return HttpResponse("") + for project_id in ids: project = Project.objects.filter(id=project_id).first() if project and is_project_manager_or_super_user(request, project): - is_active = eval(request.GET.get("is_active")) - message = "Archived" - if is_active: - message = "Un-Archived" project.is_active = is_active project.save() messages.success(request, f"{project} is {message} successfully.") - return JsonResponse({"message": "Success"}) + else: + messages.warning( + request, f"Permission denied or project not found: ID {project_id}" + ) + + return HttpResponse("") @login_required -# @permission_required("employee.delete_employee") +# @permission_required("project.delete_project") def project_bulk_delete(request): """ - This method is used to delete set of Employee instances + This method deletes a set of Project instances in bulk, after verifying permissions. """ - ids = request.POST["ids"] - ids = json.loads(ids) - del_id = [] - for project_id in ids: - project = Project.objects.get(id=project_id) - try: - if is_project_manager_or_super_user(request, project): - project.delete() - del_id.append(project) - except Exception as error: - messages.error(request, error) - messages.error( - request, _("You cannot delete %(project)s.") % {"project": project} - ) - messages.success(request, _("{} Projects deleted".format(len(del_id)))) - return JsonResponse({"message": "Success"}) + try: + ids = request.POST.getlist("ids") + if not ids: + messages.warning(request, _("No project IDs were provided.")) + return HttpResponse("") + except Exception: + messages.error(request, _("Could not retrieve project IDs.")) + return HttpResponse("") + + projects = Project.objects.filter(id__in=ids) + deletable_projects = [] + skipped_projects = [] + + for project in projects: + if is_project_manager_or_super_user(request, project): + deletable_projects.append(project) + else: + skipped_projects.append(str(project)) + + # Delete in bulk + if deletable_projects: + # Project.objects.filter(id__in=[p.id for p in deletable_projects]).delete() + messages.success( + request, + _("{count} project(s) deleted successfully.").format( + count=len(deletable_projects) + ), + ) + + if skipped_projects: + messages.warning( + request, + _("Permission denied or skipped for: %(projects)s.") + % {"projects": ", ".join(skipped_projects)}, + ) + + return HttpResponse("") @login_required @@ -1166,7 +1202,7 @@ def task_all_filter(request): @login_required -# @permission_required("employee.delete_employee") +# @permission_required("project.change_task") # @require_http_methods(["POST"]) def task_all_bulk_archive(request): """ @@ -1189,7 +1225,7 @@ def task_all_bulk_archive(request): @login_required -# @permission_required("employee.delete_employee") +# @permission_required("project.delete_task") def task_all_bulk_delete(request): """ This method is used to delete set of Task instances @@ -1210,7 +1246,7 @@ def task_all_bulk_delete(request): @login_required -# @permission_required("employee.delete_employee") +# @permission_required("project.change_task") def task_all_archive(request, task_id): """ This method is used to archive project instance