diff --git a/public/userarea/parts.js b/public/userarea/parts.js index df353b3..fe525d3 100644 --- a/public/userarea/parts.js +++ b/public/userarea/parts.js @@ -1312,7 +1312,10 @@ $(document).ready(function () { // SAVE PHOTO // =================== $("#savePhotoBtn").on("click", function () { + console.log("savePhotoBtn clicked"); + if (!$("#samplePhoto").attr("src")) { + console.log("No image source found"); const errorMsg = $( '', ); @@ -1325,12 +1328,33 @@ $(document).ready(function () { return; } + if (!fabricCanvas) { + console.log("fabricCanvas is undefined"); + const errorMsg = $( + '', + ); + $("#partsModal .modal-body").prepend(errorMsg); + setTimeout(function () { + errorMsg.fadeOut(500, function () { + $(this).remove(); + }); + }, 5000); + return; + } + const canvas = document.getElementById("photoCanvas"); const ctx = canvas.getContext("2d"); const img = $("#samplePhoto")[0]; - // Verifica che l'immagine sia caricata + console.log( + "Image loaded:", + img.complete, + "Natural width:", + img.naturalWidth, + ); + if (!img.complete || img.naturalWidth === 0) { + console.log("Image not loaded correctly"); const errorMsg = $( '', ); @@ -1343,24 +1367,53 @@ $(document).ready(function () { return; } - // Imposta le dimensioni del canvas + console.log( + "Setting canvas dimensions:", + photoData.naturalWidth, + photoData.naturalHeight, + ); canvas.width = photoData.naturalWidth; canvas.height = photoData.naturalHeight; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); - // Renderizza Fabric overlay scalato su photoCanvas + console.log("Rendering Fabric canvas"); try { const fabricDataURL = fabricCanvas.toDataURL({ format: "png", multiplier: 1 / photoData.scale, }); + console.log( + "Fabric DataURL generated, length:", + fabricDataURL.length, + ); + const tempImg = new Image(); tempImg.src = fabricDataURL; tempImg.onload = function () { + console.log("Drawing Fabric overlay on canvas"); ctx.drawImage(tempImg, 0, 0, canvas.width, canvas.height); - finalizeSave(); + + fetch(fabricDataURL) + .then((res) => res.blob()) + .then((blob) => { + console.log("Fabric blob generated, size:", blob.size); + finalizeSave(blob); + }) + .catch((err) => { + console.log("Error converting DataURL to Blob:", err); + const errorMsg = $( + '', + ); + $("#partsModal .modal-body").prepend(errorMsg); + setTimeout(function () { + errorMsg.fadeOut(500, function () { + $(this).remove(); + }); + }, 5000); + }); }; tempImg.onerror = function () { + console.log("Error loading Fabric overlay image"); const errorMsg = $( '', ); @@ -1372,6 +1425,7 @@ $(document).ready(function () { }, 5000); }; } catch (e) { + console.log("Error generating Fabric DataURL:", e); const errorMsg = $( '', ); @@ -1384,132 +1438,14 @@ $(document).ready(function () { return; } - // 1. PRIMA - Modifica la funzione finalizeSave per pulire la foto originale: - function finalizeSave() { - try { - const dataURL = canvas.toDataURL("image/png"); - const timestamp = new Date() - .toISOString() - .replace(/[:.]/g, "-"); - const iddatadb = $("#partsModal").data("iddatadb"); - const idquotations = $("#partsModal").data("idquotations"); - const id = iddatadb || idquotations; - const endpoint = idquotations - ? "save_annotated_photo_quotation.php" - : "save_annotated_photo.php"; - const dataParam = idquotations - ? { idquotations: id } - : { iddatadb: id }; - const finalName = `photo_${id}_${timestamp}.png`; - - $.ajax({ - url: endpoint, - method: "POST", - data: { - dataURL: dataURL, - filename: finalName, - ...dataParam, - }, - success: function (response) { - if (response.success) { - // Mostra messaggio non-blocking - const successMsg = $( - '", - ); - $("#partsModal .modal-body").prepend(successMsg); - setTimeout(function () { - successMsg.fadeOut(500, function () { - $(this).remove(); - }); - }, 5000); - - // AGGIORNA IL DROPDOWN E TORNA ALLA PRIMA FOTO - const photoSelector = $("#photoSelector"); - if (photoSelector.length > 0) { - const newPhotoPath = response.file_path; - const newPhotoName = newPhotoPath - .split("/") - .pop(); - const optionCount = - photoSelector.find("option").length; - - // Aggiungi la nuova opzione al dropdown - const newOption = $("") - .val(newPhotoPath) - .text( - `Photo ${optionCount + 1} - ${newPhotoName}`, - ); - photoSelector.append(newOption); - - // Seleziona la prima foto del dropdown - const firstPhotoPath = photoSelector - .find("option:first") - .val(); - photoSelector.val(firstPhotoPath); - - // Carica effettivamente la prima foto - loadSinglePhoto(firstPhotoPath); - } else { - // Se non c'è dropdown, ricarica la foto corrente per vedere la versione pulita - const currentPhoto = - $("#samplePhoto").attr("src"); - if (currentPhoto) { - loadSinglePhoto(currentPhoto); - } - } - - // PULISCI LA FOTO ORIGINALE (come richiesto) - const currentPhoto = $("#samplePhoto").attr("src"); - - // Inizializza annotazioni vuote per la nuova foto salvata - const newPhoto = response.file_path; - photoAnnotations[newPhoto] = { - markers: [], - hasDescriptions: false, - descriptionPosition: { x: 10, y: 10 }, - descriptionSize: { - width: photoData.displayWidth * 0.3, - height: photoData.displayHeight * 0.3, - }, - }; - - // SVUOTA LE ANNOTAZIONI DELLA FOTO ORIGINALE - photoAnnotations[currentPhoto] = { - markers: [], - hasDescriptions: false, - descriptionPosition: { x: 10, y: 10 }, - descriptionSize: { - width: photoData.displayWidth * 0.3, - height: photoData.displayHeight * 0.3, - }, - }; - - // PULISCI IL CANVAS COMPLETAMENTE - clearCanvasMarkers(true); // Pulisce marker e descrizioni - clearUnsaved(); - } else { - const errorMsg = $( - '", - ); - $("#partsModal .modal-body").prepend(errorMsg); - setTimeout(function () { - errorMsg.fadeOut(500, function () { - $(this).remove(); - }); - }, 5000); - } - }, - error: function (xhr, status, error) { + function finalizeSave(fabricBlob) { + console.log("Finalizing save"); + canvas.toBlob( + function (blob) { + if (!blob) { + console.log("Failed to generate final blob"); const errorMsg = $( - '", + '', ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { @@ -1517,19 +1453,171 @@ $(document).ready(function () { $(this).remove(); }); }, 5000); - }, - }); - } catch (e) { - const errorMsg = $( - '', - ); - $("#partsModal .modal-body").prepend(errorMsg); - setTimeout(function () { - errorMsg.fadeOut(500, function () { - $(this).remove(); + return; + } + + console.log("Final blob generated, size:", blob.size); + const timestamp = new Date() + .toISOString() + .replace(/[:.]/g, "-"); + const iddatadb = $("#partsModal").data("iddatadb"); + const idquotations = $("#partsModal").data("idquotations"); + const id = iddatadb || idquotations; + const endpoint = idquotations + ? "save_annotated_photo_quotation.php" + : "save_annotated_photo.php"; + const finalName = `photo_${id}_${timestamp}.png`; + + console.log( + "Sending AJAX request to:", + endpoint, + "with filename:", + finalName, + "and ID:", + id, + ); + + const formData = new FormData(); + formData.append("file", blob, finalName); + formData.append("filename", finalName); + formData.append( + idquotations ? "idquotations" : "iddatadb", + id, + ); + + $.ajax({ + url: endpoint, + method: "POST", + data: formData, + processData: false, + contentType: false, + success: function (response) { + console.log("AJAX success:", response); + if (response.success) { + const successMsg = $( + '", + ); + $("#partsModal .modal-body").prepend( + successMsg, + ); + setTimeout(function () { + successMsg.fadeOut(500, function () { + $(this).remove(); + }); + }, 5000); + + const photoSelector = $("#photoSelector"); + if (photoSelector.length > 0) { + const newPhotoPath = response.file_path; + const newPhotoName = newPhotoPath + .split("/") + .pop(); + const optionCount = + photoSelector.find("option").length; + + const newOption = $("") + .val(newPhotoPath) + .text( + `Photo ${optionCount + 1} - ${newPhotoName}`, + ); + photoSelector.append(newOption); + + const firstPhotoPath = photoSelector + .find("option:first") + .val(); + photoSelector.val(firstPhotoPath); + + console.log( + "Loading first photo:", + firstPhotoPath, + ); + loadSinglePhoto(firstPhotoPath); + } else { + const currentPhoto = + $("#samplePhoto").attr("src"); + if (currentPhoto) { + console.log( + "Reloading current photo:", + currentPhoto, + ); + loadSinglePhoto(currentPhoto); + } + } + + const currentPhoto = + $("#samplePhoto").attr("src"); + + const newPhoto = response.file_path; + photoAnnotations[newPhoto] = { + markers: [], + hasDescriptions: false, + descriptionPosition: { x: 10, y: 10 }, + descriptionSize: { + width: photoData.displayWidth * 0.3, + height: photoData.displayHeight * 0.3, + }, + }; + + photoAnnotations[currentPhoto] = { + markers: [], + hasDescriptions: false, + descriptionPosition: { x: 10, y: 10 }, + descriptionSize: { + width: photoData.displayWidth * 0.3, + height: photoData.displayHeight * 0.3, + }, + }; + + console.log("Clearing canvas and annotations"); + clearCanvasMarkers(true); + clearUnsaved(); + } else { + console.log( + "AJAX response error:", + response.message, + ); + const errorMsg = $( + '", + ); + $("#partsModal .modal-body").prepend(errorMsg); + setTimeout(function () { + errorMsg.fadeOut(500, function () { + $(this).remove(); + }); + }, 5000); + } + }, + error: function (xhr, status, error) { + console.log( + "AJAX error:", + status, + error, + xhr.status, + ); + const errorMsg = $( + '", + ); + $("#partsModal .modal-body").prepend(errorMsg); + setTimeout(function () { + errorMsg.fadeOut(500, function () { + $(this).remove(); + }); + }, 5000); + }, }); - }, 5000); - } + }, + "image/png", + 0.9, + ); } }); diff --git a/public/userarea/save_annotated_photo.php b/public/userarea/save_annotated_photo.php index 3745b08..2f277ba 100644 --- a/public/userarea/save_annotated_photo.php +++ b/public/userarea/save_annotated_photo.php @@ -1,36 +1,60 @@ false, 'message' => 'Dati mancanti']); exit; } +if (!preg_match('/^[a-zA-Z0-9_-]+\.(png|jpg|jpeg)$/', $filename)) { + echo json_encode(['success' => false, 'message' => 'Nome file non valido']); + exit; +} + +if (!is_numeric($iddatadb)) { + echo json_encode(['success' => false, 'message' => 'ID non valido']); + exit; +} + +$allowedTypes = ['image/png', 'image/jpeg']; +if (!in_array($file['type'], $allowedTypes)) { + echo json_encode(['success' => false, 'message' => 'Formato file non supportato']); + exit; +} + try { - // --- ფაილის შენახვა --- - $data = explode(',', $dataURL)[1]; - $decodedData = base64_decode($data); + $dbHandler = DBHandlerSelect::getInstance(); + $pdo = $dbHandler->getConnection(); + $stmt = $pdo->prepare("SELECT iddatadb FROM datadb WHERE iddatadb = :iddatadb"); + $stmt->execute([':iddatadb' => $iddatadb]); + if (!$stmt->fetch()) { + echo json_encode(['success' => false, 'message' => 'iddatadb non valido']); + exit; + } $dirPath = '../photostrf/annotated'; if (!file_exists($dirPath)) { - mkdir($dirPath, 0777, true); + mkdir($dirPath, 0755, true); } $filePath = $dirPath . '/' . $filename; - file_put_contents($filePath, $decodedData); + if (file_exists($filePath)) { + echo json_encode(['success' => false, 'message' => 'File già esistente']); + exit; + } - $db = DBHandlerSelect::getInstance(); - $pdo = $db->getConnection(); + if (!move_uploaded_file($file['tmp_name'], $filePath)) { + echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio del file']); + exit; + } - // --- ბაზაში ჩაწერა --- $stmt = $pdo->prepare(" INSERT INTO datadb_photos (iddatadb, file_path, file_name, uploaded_at, uploaded_by) VALUES (:iddatadb, :file_path, :file_name, NOW(), :uploaded_by) @@ -39,7 +63,7 @@ try { ':iddatadb' => $iddatadb, ':file_path' => $filePath, ':file_name' => $filename, - ':uploaded_by'=> $iduserlogin + ':uploaded_by' => $iduserlogin ]); echo json_encode([ @@ -47,7 +71,6 @@ try { 'file_path' => $filePath, 'message' => 'Foto salvata con successo e registrata nel DB' ]); - } catch (Exception $e) { echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]); } diff --git a/public/userarea/save_annotated_photo_quotation.php b/public/userarea/save_annotated_photo_quotation.php index fff1d05..1b89b07 100644 --- a/public/userarea/save_annotated_photo_quotation.php +++ b/public/userarea/save_annotated_photo_quotation.php @@ -1,21 +1,35 @@ false, 'message' => 'Dati mancanti']); exit; } +if (!preg_match('/^[a-zA-Z0-9_-]+\.(png|jpg|jpeg)$/', $filename)) { + echo json_encode(['success' => false, 'message' => 'Nome file non valido']); + exit; +} + +if (!is_numeric($idquotations)) { + echo json_encode(['success' => false, 'message' => 'ID non valido']); + exit; +} + +$allowedTypes = ['image/png', 'image/jpeg']; +if (!in_array($file['type'], $allowedTypes)) { + echo json_encode(['success' => false, 'message' => 'Formato file non supportato']); + exit; +} + try { - // Verifica che idquotations esista nella tabella quotations $dbHandler = DBHandlerSelect::getInstance(); $pdo = $dbHandler->getConnection(); $stmt = $pdo->prepare("SELECT idquotations FROM quotations WHERE idquotations = :idquotations"); @@ -25,34 +39,37 @@ try { exit; } - // Salva l'immagine - $data = explode(',', $dataURL)[1]; - $decodedData = base64_decode($data); - $dirPath = '../photostrf/annotated'; if (!file_exists($dirPath)) { - mkdir($dirPath, 0777, true); + mkdir($dirPath, 0755, true); } $filePath = $dirPath . '/' . $filename; - file_put_contents($filePath, $decodedData); + if (file_exists($filePath)) { + echo json_encode(['success' => false, 'message' => 'File già esistente']); + exit; + } + + if (!move_uploaded_file($file['tmp_name'], $filePath)) { + echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio del file']); + exit; + } - // Registra nel database $stmt = $pdo->prepare(" INSERT INTO datadb_photos (idquotations, file_path, file_name, uploaded_at, uploaded_by) VALUES (:idquotations, :file_path, :file_name, NOW(), :uploaded_by) "); $stmt->execute([ ':idquotations' => $idquotations, - ':file_path' => $filePath, - ':file_name' => $filename, - ':uploaded_by' => $iduserlogin + ':file_path' => $filePath, + ':file_name' => $filename, + ':uploaded_by' => $iduserlogin ]); echo json_encode([ - 'success' => true, + 'success' => true, 'file_path' => $filePath, - 'message' => 'Foto salvata con successo e registrata nel DB' + 'message' => 'Foto salvata con successo e registrata nel DB' ]); } catch (Exception $e) { echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);