fixed save photos for modsecurity

This commit is contained in:
2025-09-22 10:47:48 +02:00
parent 78495880ca
commit df5e6d5656
3 changed files with 301 additions and 173 deletions
+128 -40
View File
@@ -1312,7 +1312,10 @@ $(document).ready(function () {
// SAVE PHOTO // SAVE PHOTO
// =================== // ===================
$("#savePhotoBtn").on("click", function () { $("#savePhotoBtn").on("click", function () {
console.log("savePhotoBtn clicked");
if (!$("#samplePhoto").attr("src")) { if (!$("#samplePhoto").attr("src")) {
console.log("No image source found");
const errorMsg = $( const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Nessuna foto caricata da salvare.</div>', '<div class="alert alert-danger temp-alert" role="alert">Nessuna foto caricata da salvare.</div>',
); );
@@ -1325,12 +1328,33 @@ $(document).ready(function () {
return; return;
} }
if (!fabricCanvas) {
console.log("fabricCanvas is undefined");
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore: Canvas Fabric.js non inizializzato.</div>',
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
return;
}
const canvas = document.getElementById("photoCanvas"); const canvas = document.getElementById("photoCanvas");
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
const img = $("#samplePhoto")[0]; 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) { if (!img.complete || img.naturalWidth === 0) {
console.log("Image not loaded correctly");
const errorMsg = $( const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore: l\'immagine non è caricata correttamente.</div>', '<div class="alert alert-danger temp-alert" role="alert">Errore: l\'immagine non è caricata correttamente.</div>',
); );
@@ -1343,24 +1367,53 @@ $(document).ready(function () {
return; return;
} }
// Imposta le dimensioni del canvas console.log(
"Setting canvas dimensions:",
photoData.naturalWidth,
photoData.naturalHeight,
);
canvas.width = photoData.naturalWidth; canvas.width = photoData.naturalWidth;
canvas.height = photoData.naturalHeight; canvas.height = photoData.naturalHeight;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height); ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// Renderizza Fabric overlay scalato su photoCanvas console.log("Rendering Fabric canvas");
try { try {
const fabricDataURL = fabricCanvas.toDataURL({ const fabricDataURL = fabricCanvas.toDataURL({
format: "png", format: "png",
multiplier: 1 / photoData.scale, multiplier: 1 / photoData.scale,
}); });
console.log(
"Fabric DataURL generated, length:",
fabricDataURL.length,
);
const tempImg = new Image(); const tempImg = new Image();
tempImg.src = fabricDataURL; tempImg.src = fabricDataURL;
tempImg.onload = function () { tempImg.onload = function () {
console.log("Drawing Fabric overlay on canvas");
ctx.drawImage(tempImg, 0, 0, canvas.width, canvas.height); 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 = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore durante la conversione dell\'overlay Fabric.js.</div>',
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
});
}; };
tempImg.onerror = function () { tempImg.onerror = function () {
console.log("Error loading Fabric overlay image");
const errorMsg = $( const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore durante il rendering dell\'overlay Fabric.js.</div>', '<div class="alert alert-danger temp-alert" role="alert">Errore durante il rendering dell\'overlay Fabric.js.</div>',
); );
@@ -1372,6 +1425,7 @@ $(document).ready(function () {
}, 5000); }, 5000);
}; };
} catch (e) { } catch (e) {
console.log("Error generating Fabric DataURL:", e);
const errorMsg = $( const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore durante la generazione dell\'immagine annotata.</div>', '<div class="alert alert-danger temp-alert" role="alert">Errore durante la generazione dell\'immagine annotata.</div>',
); );
@@ -1384,10 +1438,25 @@ $(document).ready(function () {
return; return;
} }
// 1. PRIMA - Modifica la funzione finalizeSave per pulire la foto originale: function finalizeSave(fabricBlob) {
function finalizeSave() { console.log("Finalizing save");
try { canvas.toBlob(
const dataURL = canvas.toDataURL("image/png"); function (blob) {
if (!blob) {
console.log("Failed to generate final blob");
const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore durante la creazione dell\'immagine finale.</div>',
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
return;
}
console.log("Final blob generated, size:", blob.size);
const timestamp = new Date() const timestamp = new Date()
.toISOString() .toISOString()
.replace(/[:.]/g, "-"); .replace(/[:.]/g, "-");
@@ -1397,35 +1466,48 @@ $(document).ready(function () {
const endpoint = idquotations const endpoint = idquotations
? "save_annotated_photo_quotation.php" ? "save_annotated_photo_quotation.php"
: "save_annotated_photo.php"; : "save_annotated_photo.php";
const dataParam = idquotations
? { idquotations: id }
: { iddatadb: id };
const finalName = `photo_${id}_${timestamp}.png`; 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({ $.ajax({
url: endpoint, url: endpoint,
method: "POST", method: "POST",
data: { data: formData,
dataURL: dataURL, processData: false,
filename: finalName, contentType: false,
...dataParam,
},
success: function (response) { success: function (response) {
console.log("AJAX success:", response);
if (response.success) { if (response.success) {
// Mostra messaggio non-blocking
const successMsg = $( const successMsg = $(
'<div class="alert alert-success temp-alert" role="alert">Foto salvata con successo: ' + '<div class="alert alert-success temp-alert" role="alert">Foto salvata con successo: ' +
response.file_path + response.file_path +
"</div>", "</div>",
); );
$("#partsModal .modal-body").prepend(successMsg); $("#partsModal .modal-body").prepend(
successMsg,
);
setTimeout(function () { setTimeout(function () {
successMsg.fadeOut(500, function () { successMsg.fadeOut(500, function () {
$(this).remove(); $(this).remove();
}); });
}, 5000); }, 5000);
// AGGIORNA IL DROPDOWN E TORNA ALLA PRIMA FOTO
const photoSelector = $("#photoSelector"); const photoSelector = $("#photoSelector");
if (photoSelector.length > 0) { if (photoSelector.length > 0) {
const newPhotoPath = response.file_path; const newPhotoPath = response.file_path;
@@ -1435,7 +1517,6 @@ $(document).ready(function () {
const optionCount = const optionCount =
photoSelector.find("option").length; photoSelector.find("option").length;
// Aggiungi la nuova opzione al dropdown
const newOption = $("<option></option>") const newOption = $("<option></option>")
.val(newPhotoPath) .val(newPhotoPath)
.text( .text(
@@ -1443,27 +1524,31 @@ $(document).ready(function () {
); );
photoSelector.append(newOption); photoSelector.append(newOption);
// Seleziona la prima foto del dropdown
const firstPhotoPath = photoSelector const firstPhotoPath = photoSelector
.find("option:first") .find("option:first")
.val(); .val();
photoSelector.val(firstPhotoPath); photoSelector.val(firstPhotoPath);
// Carica effettivamente la prima foto console.log(
"Loading first photo:",
firstPhotoPath,
);
loadSinglePhoto(firstPhotoPath); loadSinglePhoto(firstPhotoPath);
} else { } else {
// Se non c'è dropdown, ricarica la foto corrente per vedere la versione pulita
const currentPhoto = const currentPhoto =
$("#samplePhoto").attr("src"); $("#samplePhoto").attr("src");
if (currentPhoto) { if (currentPhoto) {
console.log(
"Reloading current photo:",
currentPhoto,
);
loadSinglePhoto(currentPhoto); loadSinglePhoto(currentPhoto);
} }
} }
// PULISCI LA FOTO ORIGINALE (come richiesto) const currentPhoto =
const currentPhoto = $("#samplePhoto").attr("src"); $("#samplePhoto").attr("src");
// Inizializza annotazioni vuote per la nuova foto salvata
const newPhoto = response.file_path; const newPhoto = response.file_path;
photoAnnotations[newPhoto] = { photoAnnotations[newPhoto] = {
markers: [], markers: [],
@@ -1475,7 +1560,6 @@ $(document).ready(function () {
}, },
}; };
// SVUOTA LE ANNOTAZIONI DELLA FOTO ORIGINALE
photoAnnotations[currentPhoto] = { photoAnnotations[currentPhoto] = {
markers: [], markers: [],
hasDescriptions: false, hasDescriptions: false,
@@ -1486,13 +1570,18 @@ $(document).ready(function () {
}, },
}; };
// PULISCI IL CANVAS COMPLETAMENTE console.log("Clearing canvas and annotations");
clearCanvasMarkers(true); // Pulisce marker e descrizioni clearCanvasMarkers(true);
clearUnsaved(); clearUnsaved();
} else { } else {
console.log(
"AJAX response error:",
response.message,
);
const errorMsg = $( const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore: ' + '<div class="alert alert-danger temp-alert" role="alert">Errore: ' +
(response.message || "Errore sconosciuto") + (response.message ||
"Errore sconosciuto") +
"</div>", "</div>",
); );
$("#partsModal .modal-body").prepend(errorMsg); $("#partsModal .modal-body").prepend(errorMsg);
@@ -1504,6 +1593,12 @@ $(document).ready(function () {
} }
}, },
error: function (xhr, status, error) { error: function (xhr, status, error) {
console.log(
"AJAX error:",
status,
error,
xhr.status,
);
const errorMsg = $( const errorMsg = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio della foto: ' + '<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio della foto: ' +
error + error +
@@ -1519,17 +1614,10 @@ $(document).ready(function () {
}, 5000); }, 5000);
}, },
}); });
} catch (e) { },
const errorMsg = $( "image/png",
'<div class="alert alert-danger temp-alert" role="alert">Errore durante la creazione dell\'immagine finale.</div>', 0.9,
); );
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
} }
}); });
+37 -14
View File
@@ -1,36 +1,60 @@
<?php <?php
header('Content-Type: application/json'); header('Content-Type: application/json');
include('include/headscript.php');
include('include/headscript.php'); // აქედან უნდა იყოს DB კავშირიც
error_reporting(E_ALL); error_reporting(E_ALL);
ini_set('display_errors', 1); ini_set('display_errors', 1);
$dataURL = $_POST['dataURL'] ?? null; $file = $_FILES['file'] ?? null;
$filename = $_POST['filename'] ?? null; $filename = $_POST['filename'] ?? null;
$iddatadb = $_POST['iddatadb'] ?? null; // 🟢 ახალი ველი $iddatadb = $_POST['iddatadb'] ?? null;
if (!$dataURL || !$filename || !$iddatadb) { if (!$file || !$filename || !$iddatadb) {
echo json_encode(['success' => false, 'message' => 'Dati mancanti']); echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
exit; 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 { try {
// --- ფაილის შენახვა --- $dbHandler = DBHandlerSelect::getInstance();
$data = explode(',', $dataURL)[1]; $pdo = $dbHandler->getConnection();
$decodedData = base64_decode($data); $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'; $dirPath = '../photostrf/annotated';
if (!file_exists($dirPath)) { if (!file_exists($dirPath)) {
mkdir($dirPath, 0777, true); mkdir($dirPath, 0755, true);
} }
$filePath = $dirPath . '/' . $filename; $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(); if (!move_uploaded_file($file['tmp_name'], $filePath)) {
$pdo = $db->getConnection(); echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio del file']);
exit;
}
// --- ბაზაში ჩაწერა ---
$stmt = $pdo->prepare(" $stmt = $pdo->prepare("
INSERT INTO datadb_photos (iddatadb, file_path, file_name, uploaded_at, uploaded_by) INSERT INTO datadb_photos (iddatadb, file_path, file_name, uploaded_at, uploaded_by)
VALUES (:iddatadb, :file_path, :file_name, NOW(), :uploaded_by) VALUES (:iddatadb, :file_path, :file_name, NOW(), :uploaded_by)
@@ -47,7 +71,6 @@ try {
'file_path' => $filePath, 'file_path' => $filePath,
'message' => 'Foto salvata con successo e registrata nel DB' 'message' => 'Foto salvata con successo e registrata nel DB'
]); ]);
} catch (Exception $e) { } catch (Exception $e) {
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]); echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
} }
@@ -1,21 +1,35 @@
<?php <?php
header('Content-Type: application/json'); header('Content-Type: application/json');
include('include/headscript.php'); include('include/headscript.php');
error_reporting(E_ALL); error_reporting(E_ALL);
ini_set('display_errors', 1); ini_set('display_errors', 1);
$dataURL = $_POST['dataURL'] ?? null; $file = $_FILES['file'] ?? null;
$filename = $_POST['filename'] ?? null; $filename = $_POST['filename'] ?? null;
$idquotations = $_POST['idquotations'] ?? null; $idquotations = $_POST['idquotations'] ?? null;
if (!$dataURL || !$filename || !$idquotations) { if (!$file || !$filename || !$idquotations) {
echo json_encode(['success' => false, 'message' => 'Dati mancanti']); echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
exit; 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 { try {
// Verifica che idquotations esista nella tabella quotations
$dbHandler = DBHandlerSelect::getInstance(); $dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection(); $pdo = $dbHandler->getConnection();
$stmt = $pdo->prepare("SELECT idquotations FROM quotations WHERE idquotations = :idquotations"); $stmt = $pdo->prepare("SELECT idquotations FROM quotations WHERE idquotations = :idquotations");
@@ -25,19 +39,22 @@ try {
exit; exit;
} }
// Salva l'immagine
$data = explode(',', $dataURL)[1];
$decodedData = base64_decode($data);
$dirPath = '../photostrf/annotated'; $dirPath = '../photostrf/annotated';
if (!file_exists($dirPath)) { if (!file_exists($dirPath)) {
mkdir($dirPath, 0777, true); mkdir($dirPath, 0755, true);
} }
$filePath = $dirPath . '/' . $filename; $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(" $stmt = $pdo->prepare("
INSERT INTO datadb_photos (idquotations, file_path, file_name, uploaded_at, uploaded_by) INSERT INTO datadb_photos (idquotations, file_path, file_name, uploaded_at, uploaded_by)
VALUES (:idquotations, :file_path, :file_name, NOW(), :uploaded_by) VALUES (:idquotations, :file_path, :file_name, NOW(), :uploaded_by)