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 = $(
'
Nessuna foto caricata da salvare.
',
);
@@ -1325,12 +1328,33 @@ $(document).ready(function () {
return;
}
+ if (!fabricCanvas) {
+ console.log("fabricCanvas is undefined");
+ const errorMsg = $(
+ 'Errore: Canvas Fabric.js non inizializzato.
',
+ );
+ $("#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 = $(
'Errore: l\'immagine non è caricata correttamente.
',
);
@@ -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 = $(
+ 'Errore durante la conversione dell\'overlay Fabric.js.
',
+ );
+ $("#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 = $(
'Errore durante il rendering dell\'overlay Fabric.js.
',
);
@@ -1372,6 +1425,7 @@ $(document).ready(function () {
}, 5000);
};
} catch (e) {
+ console.log("Error generating Fabric DataURL:", e);
const errorMsg = $(
'Errore durante la generazione dell\'immagine annotata.
',
);
@@ -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 = $(
- 'Foto salvata con successo: ' +
- response.file_path +
- "
",
- );
- $("#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 = $(
- 'Errore: ' +
- (response.message || "Errore sconosciuto") +
- "
",
- );
- $("#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 = $(
- 'Errore nel salvataggio della foto: ' +
- error +
- " (" +
- xhr.status +
- ")
",
+ 'Errore durante la creazione dell\'immagine finale.
',
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
@@ -1517,19 +1453,171 @@ $(document).ready(function () {
$(this).remove();
});
}, 5000);
- },
- });
- } catch (e) {
- const errorMsg = $(
- 'Errore durante la creazione dell\'immagine finale.
',
- );
- $("#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 = $(
+ 'Foto salvata con successo: ' +
+ response.file_path +
+ "
",
+ );
+ $("#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 = $(
+ 'Errore: ' +
+ (response.message ||
+ "Errore sconosciuto") +
+ "
",
+ );
+ $("#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 = $(
+ 'Errore nel salvataggio della foto: ' +
+ error +
+ " (" +
+ xhr.status +
+ ")
",
+ );
+ $("#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()]);