fixed save photos for modsecurity

This commit is contained in:
Claudio 2025-09-22 10:47:48 +02:00
parent 78495880ca
commit df5e6d5656
3 changed files with 301 additions and 173 deletions

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,132 +1438,14 @@ $(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) {
const timestamp = new Date() if (!blob) {
.toISOString() console.log("Failed to generate final blob");
.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 = $(
'<div class="alert alert-success temp-alert" role="alert">Foto salvata con successo: ' +
response.file_path +
"</div>",
);
$("#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 = $("<option></option>")
.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 = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore: ' +
(response.message || "Errore sconosciuto") +
"</div>",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
}
},
error: function (xhr, status, error) {
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 durante la creazione dell\'immagine finale.</div>',
error +
" (" +
xhr.status +
")</div>",
); );
$("#partsModal .modal-body").prepend(errorMsg); $("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () { setTimeout(function () {
@ -1517,19 +1453,171 @@ $(document).ready(function () {
$(this).remove(); $(this).remove();
}); });
}, 5000); }, 5000);
}, return;
}); }
} catch (e) {
const errorMsg = $( console.log("Final blob generated, size:", blob.size);
'<div class="alert alert-danger temp-alert" role="alert">Errore durante la creazione dell\'immagine finale.</div>', const timestamp = new Date()
); .toISOString()
$("#partsModal .modal-body").prepend(errorMsg); .replace(/[:.]/g, "-");
setTimeout(function () { const iddatadb = $("#partsModal").data("iddatadb");
errorMsg.fadeOut(500, function () { const idquotations = $("#partsModal").data("idquotations");
$(this).remove(); 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 = $(
'<div class="alert alert-success temp-alert" role="alert">Foto salvata con successo: ' +
response.file_path +
"</div>",
);
$("#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 = $("<option></option>")
.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 = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore: ' +
(response.message ||
"Errore sconosciuto") +
"</div>",
);
$("#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 = $(
'<div class="alert alert-danger temp-alert" role="alert">Errore nel salvataggio della foto: ' +
error +
" (" +
xhr.status +
")</div>",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
},
}); });
}, 5000); },
} "image/png",
0.9,
);
} }
}); });

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)
@ -39,7 +63,7 @@ try {
':iddatadb' => $iddatadb, ':iddatadb' => $iddatadb,
':file_path' => $filePath, ':file_path' => $filePath,
':file_name' => $filename, ':file_name' => $filename,
':uploaded_by'=> $iduserlogin ':uploaded_by' => $iduserlogin
]); ]);
echo json_encode([ echo json_encode([
@ -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()]);
} }

View File

@ -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,34 +39,37 @@ 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)
"); ");
$stmt->execute([ $stmt->execute([
':idquotations' => $idquotations, ':idquotations' => $idquotations,
':file_path' => $filePath, ':file_path' => $filePath,
':file_name' => $filename, ':file_name' => $filename,
':uploaded_by' => $iduserlogin ':uploaded_by' => $iduserlogin
]); ]);
echo json_encode([ echo json_encode([
'success' => true, 'success' => true,
'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()]);