diff --git a/.gitignore b/.gitignore index 9c3c780..6d374e4 100755 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # App app/config.php +app/uploads # Logs logs diff --git a/app/assets/js/custom.js b/app/assets/js/custom.js index 1f4337b..0739c00 100644 --- a/app/assets/js/custom.js +++ b/app/assets/js/custom.js @@ -326,6 +326,19 @@ function ajax_request(options) { options.data = { json: JSON.stringify(payloadJson) }; + if (options.debugMode) { + isAjaxProcessing = false; + // Show FormData contents if applicable + if (options.formData instanceof FormData) { + // Log original formData before merging + for (let [key, value] of options.formData.entries()) { + console.log("FORMDATA: " + key, value); + } + } + // Show stringified JSON payload + console.log("REQUEST DATA:", options.data); + } + // IF formData exist, we pass as $_POST [not json] if (options.formData instanceof FormData) { // THE BYPASS: If formData exists, move all text data into it @@ -340,11 +353,6 @@ function ajax_request(options) { // --- START MODIFIED $.AJAX BLOCK --- let isSendingFiles = (options.data instanceof FormData); - if (options.debugMode) { - isAjaxProcessing = false; - console.log("REQUEST DATA:", options.data); - } - // Show loading overlay if (options.noLoading !== true) { $.LoadingOverlay("show", { diff --git a/app/assets/utils/db_auth.php b/app/assets/utils/db_auth.php index 242e77a..febe96b 100644 --- a/app/assets/utils/db_auth.php +++ b/app/assets/utils/db_auth.php @@ -289,10 +289,12 @@ class FileUploader { private function ensureDirectory() { if (!is_dir($this->target_dir)) { - mkdir($this->target_dir, 0755, true); + if (!mkdir($this->target_dir, 0755, true)) { + throw new Exception("Failed to create directory: " . $this->target_dir); + } } if (!is_writable($this->target_dir)) { - throw new Exception("Target directory is not writable."); + throw new Exception("Target directory is not writable: " . $this->target_dir); } } @@ -325,16 +327,25 @@ class FileUploader { $errors = []; - foreach ($_FILES[$field_name]['name'] as $key => $name) { - $error_code = $_FILES[$field_name]['error'][$key]; + // Normalize single file to array structure + $files = $_FILES[$field_name]; + if (!is_array($files['name'])) { + $files['name'] = [$files['name']]; + $files['tmp_name'] = [$files['tmp_name']]; + $files['error'] = [$files['error']]; + $files['size'] = [$files['size']]; + } + + foreach ($files['name'] as $key => $name) { + $error_code = $files['error'][$key]; if ($error_code !== UPLOAD_ERR_OK) { $errors[] = "{$name}: " . $this->getUploadError($error_code); continue; } - $tmp_name = $_FILES[$field_name]['tmp_name'][$key]; - $file_size = $_FILES[$field_name]['size'][$key]; + $tmp_name = $files['tmp_name'][$key]; + $file_size = $files['size'][$key]; $extension = strtolower(pathinfo($name, PATHINFO_EXTENSION)); if ($file_size > $this->max_size) { @@ -356,7 +367,11 @@ class FileUploader { continue; } - $file_id = uniqid() . "_" . time() . "." . $extension; + // FILE NAME SANITIZATION + UNIQUE ID + $original = pathinfo($name, PATHINFO_FILENAME); + $original = preg_replace('/[^a-zA-Z0-9_-]/', '_', $original); // sanitize + $file_id = $original . "_" . uniqid() . "." . $extension; + $destination = $this->target_dir . $file_id; if (move_uploaded_file($tmp_name, $destination)) { @@ -378,6 +393,13 @@ class FileUploader { $final = array_merge($keep, $this->uploaded_files); return implode(",", array_filter($final)); } + + /** + * Build final CSV string for DB as single file + */ + public function getUploadedFiles() { + return $this->uploaded_files; + } /** * Rollback uploaded files if DB fails diff --git a/app/contact/api/engine/manage_contact.php b/app/contact/api/engine/manage_contact.php index a813a61..a74f540 100644 --- a/app/contact/api/engine/manage_contact.php +++ b/app/contact/api/engine/manage_contact.php @@ -10,14 +10,14 @@ // Cleanup deleted files if ($id > 0) { - $sql = "SELECT `files` FROM md_contact WHERE company_id = :company_id AND id = :id"; + $sql = "SELECT `contact_image` FROM md_contact WHERE company_id = :company_id AND id = :id"; $sth = $pdo2->prepare($sql); $sth->execute([":company_id" => $company_id, ":id" => $id]); $uploader->cleanup($sth->fetchColumn(), $data['keep_files'] ?? ''); } // Upload new files - $errors = $uploader->upload('contact_files'); + $errors = $uploader->upload('contact_image'); if (!empty($errors)) { $answer["success"] = 0; $answer["message"] = $errors[0]; @@ -52,7 +52,7 @@ ":shipping_location"=> $data["shipping_location"], ":shipping_address" => $data["shipping_address"], ":remark" => $data["remark"], - ":files" => $db_image_string, + ":contact_image" => $db_image_string, ":status" => (int)$data["status"], ":log" => json_encode($table_log), ]; @@ -68,7 +68,7 @@ `shipping_location` = :shipping_location, `shipping_address` = :shipping_address, `remark` = :remark, - `files` = :files, + `contact_image` = :contact_image, `status` = :status, `log` = :log WHERE id = :id AND company_id = :company_id"; @@ -76,10 +76,10 @@ } else { $sql = "INSERT INTO md_contact (company_id, contact_name, tax_id, organization, branch, contact_type, - billing_address, shipping_location, shipping_address, remark, files, status, log) + billing_address, shipping_location, shipping_address, remark, contact_image, status, log) VALUES (:company_id, :contact_name, :tax_id, :organization, :branch, :contact_type, - :billing_address, :shipping_location, :shipping_address, :remark, :files, :status, :log)"; + :billing_address, :shipping_location, :shipping_address, :remark, :contact_image, :status, :log)"; } $sth = $pdo->prepare($sql); diff --git a/app/contact/manage_contact.php b/app/contact/manage_contact.php index ccfa8a9..574286d 100644 --- a/app/contact/manage_contact.php +++ b/app/contact/manage_contact.php @@ -158,7 +158,7 @@ parallelUploads: 10, acceptedFiles: "image/*", addRemoveLinks: true, - paramName: "contact_files" + paramName: "contact_image" }); }); @@ -168,8 +168,8 @@ let files = myDropzone.getQueuedFiles(); files.forEach(function(file) { - // Use 'contact_files[]' so PHP creates an array in $_FILES - formData.append('contact_files[]', file); + // Use 'contact_image[]' so PHP creates an array in $_FILES + formData.append('contact_image[]', file); }) let keepFiles = []; @@ -237,8 +237,8 @@ let item = res.output; // 2. Handle files specifically (No loop needed for the row) - if (item.files) { - let fileArray = item.files.split(','); + if (item.contact_image) { + let fileArray = item.contact_image.split(','); fileArray.forEach(function(fileName) { if (fileName.trim() !== "") { diff --git a/app/inventory/api/engine/manage_product.php b/app/inventory/api/engine/manage_product.php index 057bd45..1214aad 100644 --- a/app/inventory/api/engine/manage_product.php +++ b/app/inventory/api/engine/manage_product.php @@ -3,11 +3,40 @@ require '../../../assets/utils/db_auth.php'; + $id = (int)$data['id']; + try { - dbTransaction($pdo2, function($pdo) use ($data, $company_id, $logging) { + /** 1. FILE HANDLING **/ + $uploader = new FileUploader($include_url . "uploads/product/"); - $id = (int)$data['id']; + if (!empty($_FILES['product_image']['name'])) { + // Cleanup old image when updating + if ($id > 0) { + $sql = "SELECT `product_image` FROM md_product WHERE company_id = :company_id AND id = :id"; + $sth = $pdo2->prepare($sql); + $sth->execute([":company_id" => $company_id, ":id" => $id]); + $existing_files = $sth->fetchColumn(); + + $uploader->cleanup($existing_files, ''); + } + + $errors = $uploader->upload('product_image'); + if (!empty($errors)) { + $answer["success"] = 0; + $answer["message"] = $errors[0]; + exit(json_encode($answer)); + } + + // Get the single uploaded filename directly + $db_image_string = $uploader->getUploadedFiles()[0] ?? ''; + + } else { + // No new file — keep existing, skip cleanup entirely + $db_image_string = $data['keep_files'] ?? ''; + } + + dbTransaction($pdo2, function($pdo) use ($data, $company_id, $logging, $id, $db_image_string) { // get existing log $sql = "SELECT `log` FROM md_product @@ -44,7 +73,7 @@ ":price" => $data["price"], ":min_stock" => $data["min_stock"], ":category" => $data["category"], - ":product_image"=> $data["product_image"], + ":product_image"=> $db_image_string, ":description" => $data["description"], ":status" => $data["status"], ":log" => json_encode($table_log), @@ -65,7 +94,7 @@ ":price" => $data["price"], ":min_stock" => $data["min_stock"], ":category" => $data["category"], - ":product_image"=> $data["product_image"], + ":product_image"=> $db_image_string, ":description" => $data["description"], ":status" => $data["status"], ":log" => json_encode($table_log), @@ -83,6 +112,7 @@ } catch (Exception $e) { $answer["success"] = 0; $answer["message"] = "Something went wrong"; + $answer["error"] = $e->getMessage(); } exit(json_encode($answer)); diff --git a/app/inventory/manage_product.php b/app/inventory/manage_product.php index 056ac79..f07d6c1 100644 --- a/app/inventory/manage_product.php +++ b/app/inventory/manage_product.php @@ -48,22 +48,34 @@ - -