/** * AJAX HANDLER: Fetches all clients from the bqcis_db database. * * This function securely connects to the custom database, retrieves all clients, * and sends them back to the dashboard as a JSON object. It is triggered * when the dashboard's JavaScript calls the 'get_clients' action. */ function bqcis_get_clients_ajax_handler() { // This is the global WordPress database object. It's already configured. global $wpdb; // We must temporarily switch to your custom database to run the query. // This is the key to working with a separate database inside WordPress. $wpdb->select('bqcis_db'); // Fetch all records from the 'clients' table, ordered by company name. $clients = $wpdb->get_results("SELECT id, company_name, contact_name, contact_email, contact_phone FROM clients ORDER BY company_name ASC", ARRAY_A); // IMPORTANT: Switch back to the main WordPress database immediately after our query. $wpdb->select(DB_NAME); // Check if the query produced an error. if ($wpdb->last_error) { wp_send_json_error(['message' => 'Database query failed.'], 500); } else { // If successful, send the array of clients back to the JavaScript. wp_send_json_success($clients); } } // This line registers our function with WordPress's AJAX system. // It tells WordPress: "When a logged-in user requests the 'get_clients' action, run this function." add_action('wp_ajax_get_clients', 'bqcis_get_clients_ajax_handler'); /** * AJAX HANDLER: Adds a new client to the bqcis_db database. * * This function handles data submitted from a new client form. It sanitizes * the input, inserts it into the database, and returns a success or error * message. It is triggered by the 'add_client' AJAX action. */ function bqcis_add_client_ajax_handler() { // Check for a valid security nonce to prevent unauthorized requests. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // The data is sent as a JSON string, so we need to decode it. $data = json_decode(file_get_contents('php://input'), true); // Sanitize all incoming data using WordPress's secure functions. $company_name = isset($data['company_name']) ? sanitize_text_field($data['company_name']) : ''; $contact_name = isset($data['contact_name']) ? sanitize_text_field($data['contact_name']) : ''; $contact_email = isset($data['contact_email']) ? sanitize_email($data['contact_email']) : ''; $contact_phone = isset($data['contact_phone']) ? sanitize_text_field($data['contact_phone']) : ''; // A company name is required to create a client. if (empty($company_name)) { wp_send_json_error(['message' => 'Company name is a required field.'], 400); } // Switch to the custom bqcis_db database. $wpdb->select('bqcis_db'); // Use the secure $wpdb->insert method to add the data. $result = $wpdb->insert( 'clients', // The table name [ // An array of the data to insert 'company_name' => $company_name, 'contact_name' => $contact_name, 'contact_email' => $contact_email, 'contact_phone' => $contact_phone, ], [ // The format of each data type '%s', '%s', '%s', '%s' ] ); // IMPORTANT: Switch back to the main WordPress database. $wpdb->select(DB_NAME); // Check if the insert operation was successful. if ($result === false) { wp_send_json_error(['message' => 'Failed to add client to the database.'], 500); } else { wp_send_json_success(['message' => 'Client added successfully.']); } } // Register the AJAX action with WordPress. add_action('wp_ajax_add_client', 'bqcis_add_client_ajax_handler'); /** * AJAX HANDLER: Updates an existing client in the bqcis_db database. * * This function handles data submitted from an edit client form. It sanitizes * the input, finds the correct record by its ID, updates it, and returns * a success or error message. It is triggered by the 'update_client' AJAX action. */ function bqcis_update_client_ajax_handler() { // Check for a valid security nonce. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // Decode the incoming JSON data. $data = json_decode(file_get_contents('php://input'), true); // Sanitize all incoming data. absint ensures the ID is a positive integer. $id = isset($data['id']) ? absint($data['id']) : 0; $company_name = isset($data['company_name']) ? sanitize_text_field($data['company_name']) : ''; $contact_name = isset($data['contact_name']) ? sanitize_text_field($data['contact_name']) : ''; $contact_email = isset($data['contact_email']) ? sanitize_email($data['contact_email']) : ''; $contact_phone = isset($data['contact_phone']) ? sanitize_text_field($data['contact_phone']) : ''; // ID and Company Name are required to perform an update. if (empty($id) || empty($company_name)) { wp_send_json_error(['message' => 'Client ID and Company Name are required fields.'], 400); } // Switch to the custom bqcis_db database. $wpdb->select('bqcis_db'); // Use the secure $wpdb->update method. $result = $wpdb->update( 'clients', // The table name [ // An array of the data to update 'company_name' => $company_name, 'contact_name' => $contact_name, 'contact_email' => $contact_email, 'contact_phone' => $contact_phone, ], [ 'id' => $id ], // The WHERE clause to identify the correct record [ // The format of the data being updated '%s', '%s', '%s', '%s' ], [ '%d' ] // The format for the WHERE clause (d for integer) ); // IMPORTANT: Switch back to the main WordPress database. $wpdb->select(DB_NAME); // $wpdb->update returns false on error. if ($result === false) { wp_send_json_error(['message' => 'Failed to update client in the database.'], 500); } else { wp_send_json_success(['message' => 'Client updated successfully.']); } } // Register the AJAX action with WordPress. add_action('wp_ajax_update_client', 'bqcis_update_client_ajax_handler'); /** * AJAX HANDLER: Updates an existing client in the bqcis_db database. * * This function handles data submitted from an edit client form. It sanitizes * the input, finds the correct record by its ID, updates it, and returns * a success or error message. It is triggered by the 'update_client' AJAX action. */ function bqcis_update_client_ajax_handler() { // Check for a valid security nonce. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // Decode the incoming JSON data. $data = json_decode(file_get_contents('php://input'), true); // Sanitize all incoming data. absint ensures the ID is a positive integer. $id = isset($data['id']) ? absint($data['id']) : 0; $company_name = isset($data['company_name']) ? sanitize_text_field($data['company_name']) : ''; $contact_name = isset($data['contact_name']) ? sanitize_text_field($data['contact_name']) : ''; $contact_email = isset($data['contact_email']) ? sanitize_email($data['contact_email']) : ''; $contact_phone = isset($data['contact_phone']) ? sanitize_text_field($data['contact_phone']) : ''; // ID and Company Name are required to perform an update. if (empty($id) || empty($company_name)) { wp_send_json_error(['message' => 'Client ID and Company Name are required fields.'], 400); } // Switch to the custom bqcis_db database. $wpdb->select('bqcis_db'); // Use the secure $wpdb->update method. $result = $wpdb->update( 'clients', // The table name [ // An array of the data to update 'company_name' => $company_name, 'contact_name' => $contact_name, 'contact_email' => $contact_email, 'contact_phone' => $contact_phone, ], [ 'id' => $id ], // The WHERE clause to identify the correct record [ // The format of the data being updated '%s', '%s', '%s', '%s' ], [ '%d' ] // The format for the WHERE clause (d for integer) ); // IMPORTANT: Switch back to the main WordPress database. $wpdb->select(DB_NAME); // $wpdb->update returns false on error. if ($result === false) { wp_send_json_error(['message' => 'Failed to update client in the database.'], 500); } else { wp_send_json_success(['message' => 'Client updated successfully.']); } } // Register the AJAX action with WordPress. add_action('wp_ajax_update_client', 'bqcis_update_client_ajax_handler'); /** * AJAX HANDLER: Deletes a client from the bqcis_db database. * * This function handles a request to delete a client. It sanitizes the * incoming ID and removes the corresponding record from the database. * It is triggered by the 'delete_client' AJAX action. */ function bqcis_delete_client_ajax_handler() { // Check for a valid security nonce. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // Decode the incoming JSON data. $data = json_decode(file_get_contents('php://input'), true); // Sanitize the ID to ensure it's a safe, positive integer. $id = isset($data['id']) ? absint($data['id']) : 0; // A valid ID is required to perform a deletion. if (empty($id)) { wp_send_json_error(['message' => 'A valid Client ID is required.'], 400); } // Switch to the custom bqcis_db database. $wpdb->select('bqcis_db'); // Use the secure $wpdb->delete method. $result = $wpdb->delete( 'clients', // The table name [ 'id' => $id ], // The WHERE clause to identify the record [ '%d' ] // The format for the WHERE clause (d for integer) ); // IMPORTANT: Switch back to the main WordPress database. $wpdb->select(DB_NAME); // $wpdb->delete returns the number of rows affected (should be 1) or false on error. if ($result === false) { wp_send_json_error(['message' => 'Failed to delete client from the database.'], 500); } elseif ($result === 0) { // This means the query ran, but no rows were deleted (ID not found). wp_send_json_error(['message' => 'Could not find a client with that ID to delete.'], 404); } else { wp_send_json_success(['message' => 'Client deleted successfully.']); } } // Register the AJAX action with WordPress. add_action('wp_ajax_delete_client', 'bqcis_delete_client_ajax_handler'); /** * AJAX HANDLER: Fetches all inspectors from the bqcis_db database. * * This function securely connects to the custom database, retrieves all inspectors, * and sends them back to the dashboard as a JSON object. It is triggered * when the dashboard's JavaScript calls the 'get_inspectors' action. */ function bqcis_get_inspectors_ajax_handler() { // Check for a valid security nonce. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // Switch to your custom bqcis_db database. $wpdb->select('bqcis_db'); // Fetch all records from the 'inspectors' table, ordered by name. $inspectors = $wpdb->get_results("SELECT id, inspector_name, inspector_id, email, phone FROM inspectors ORDER BY inspector_name ASC", ARRAY_A); // IMPORTANT: Switch back to the main WordPress database. $wpdb->select(DB_NAME); // Check for any database errors during the query. if ($wpdb->last_error) { wp_send_json_error(['message' => 'Database query failed while fetching inspectors.'], 500); } else { // Send the list of inspectors back to the dashboard as JSON. wp_send_json_success($inspectors); } } // Register the AJAX action with WordPress for logged-in users. add_action('wp_ajax_get_inspectors', 'bqcis_get_inspectors_ajax_handler'); /** * AJAX HANDLER: Adds a new inspector to the bqcis_db database. * * This function handles data submitted from a new inspector form. It sanitizes * the input, inserts it into the database, and returns a success or error * message. It is triggered by the 'add_inspector' AJAX action. */ function bqcis_add_inspector_ajax_handler() { // Check for a valid security nonce to prevent unauthorized requests. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // The data is sent as a JSON string, so we need to decode it. $data = json_decode(file_get_contents('php://input'), true); // Sanitize all incoming data using WordPress's secure functions. $inspector_name = isset($data['inspector_name']) ? sanitize_text_field($data['inspector_name']) : ''; $inspector_id_field = isset($data['inspector_id']) ? sanitize_text_field($data['inspector_id']) : ''; $email = isset($data['email']) ? sanitize_email($data['email']) : ''; $phone = isset($data['phone']) ? sanitize_text_field($data['phone']) : ''; // An inspector's name is required to create a new record. if (empty($inspector_name)) { wp_send_json_error(['message' => 'Inspector name is a required field.'], 400); } // Switch to the custom bqcis_db database. $wpdb->select('bqcis_db'); // Use the secure $wpdb->insert method to add the data. $result = $wpdb->insert( 'inspectors', // The table name [ // An array of the data to insert 'inspector_name' => $inspector_name, 'inspector_id' => $inspector_id_field, 'email' => $email, 'phone' => $phone, ], [ // The format of each data type '%s', '%s', '%s', '%s' ] ); // IMPORTANT: Switch back to the main WordPress database. $wpdb->select(DB_NAME); // Check if the insert operation was successful. if ($result === false) { wp_send_json_error(['message' => 'Failed to add inspector to the database.'], 500); } else { wp_send_json_success(['message' => 'Inspector added successfully.']); } } // Register the AJAX action with WordPress. add_action('wp_ajax_add_inspector', 'bqcis_add_inspector_ajax_handler'); /** * AJAX HANDLER: Updates an existing inspector in the bqcis_db database. * * This function handles data submitted from an edit inspector form. It sanitizes * the input, finds the correct record by its ID, updates it, and returns * a success or error message. It is triggered by the 'update_inspector' AJAX action. */ function bqcis_update_inspector_ajax_handler() { // Check for a valid security nonce. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // Decode the incoming JSON data. $data = json_decode(file_get_contents('php://input'), true); // Sanitize all incoming data. $id = isset($data['id']) ? absint($data['id']) : 0; $inspector_name = isset($data['inspector_name']) ? sanitize_text_field($data['inspector_name']) : ''; $inspector_id_field = isset($data['inspector_id']) ? sanitize_text_field($data['inspector_id']) : ''; $email = isset($data['email']) ? sanitize_email($data['email']) : ''; $phone = isset($data['phone']) ? sanitize_text_field($data['phone']) : ''; // ID and Inspector Name are required to perform an update. if (empty($id) || empty($inspector_name)) { wp_send_json_error(['message' => 'Inspector ID and Name are required fields.'], 400); } // Switch to the custom bqcis_db database. $wpdb->select('bqcis_db'); // Use the secure $wpdb->update method. $result = $wpdb->update( 'inspectors', // The table name [ // An array of the data to update 'inspector_name' => $inspector_name, 'inspector_id' => $inspector_id_field, 'email' => $email, 'phone' => $phone, ], [ 'id' => $id ], // The WHERE clause to identify the correct record [ // The format of the data being updated '%s', '%s', '%s', '%s' ], [ '%d' ] // The format for the WHERE clause (d for integer) ); // IMPORTANT: Switch back to the main WordPress database. $wpdb->select(DB_NAME); // $wpdb->update returns false on error. if ($result === false) { wp_send_json_error(['message' => 'Failed to update inspector in the database.'], 500); } else { wp_send_json_success(['message' => 'Inspector updated successfully.']); } } // Register the AJAX action with WordPress. add_action('wp_ajax_update_inspector', 'bqcis_update_inspector_ajax_handler'); /** * AJAX HANDLER: Deletes an inspector from the bqcis_db database. * * This function handles a request to delete an inspector. It sanitizes the * incoming ID and removes the corresponding record from the database. * It is triggered by the 'delete_inspector' AJAX action. */ function bqcis_delete_inspector_ajax_handler() { // Check for a valid security nonce. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // Decode the incoming JSON data. $data = json_decode(file_get_contents('php://input'), true); // Sanitize the ID to ensure it's a safe, positive integer. $id = isset($data['id']) ? absint($data['id']) : 0; // A valid ID is required to perform a deletion. if (empty($id)) { wp_send_json_error(['message' => 'A valid Inspector ID is required.'], 400); } // Switch to the custom bqcis_db database. $wpdb->select('bqcis_db'); // Use the secure $wpdb->delete method. $result = $wpdb->delete( 'inspectors', // The table name [ 'id' => $id ], // The WHERE clause to identify the record [ '%d' ] // The format for the WHERE clause (d for integer) ); // IMPORTANT: Switch back to the main WordPress database. $wpdb->select(DB_NAME); // Check the result of the delete operation. if ($result === false) { wp_send_json_error(['message' => 'Failed to delete inspector from the database.'], 500); } elseif ($result === 0) { wp_send_json_error(['message' => 'Could not find an inspector with that ID to delete.'], 404); } else { wp_send_json_success(['message' => 'Inspector deleted successfully.']); } } // Register the AJAX action with WordPress. add_action('wp_ajax_delete_inspector', 'bqcis_delete_inspector_ajax_handler'); /** * AJAX HANDLER: Fetches certificates from the bqcis_db database, with optional search. * * This function retrieves a list of all certificates, allowing for filtering by * certificate code. It is triggered by the 'get_certificates' AJAX action. */ function bqcis_get_certificates_ajax_handler() { // Check for a valid security nonce. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // Sanitize the search term, which is passed as a GET parameter. $search = isset($_GET['search']) ? sanitize_text_field($_GET['search']) : ''; // Switch to your custom bqcis_db database. $wpdb->select('bqcis_db'); // Start with the base SQL query. $sql = "SELECT id, certificate_code, company_name, issue_date, expiry_date, status FROM certificates"; // If a search term was provided, add a WHERE clause to the query. if (!empty($search)) { // Use $wpdb->prepare for security to prevent SQL injection. $sql = $wpdb->prepare( $sql . " WHERE certificate_code LIKE %s", '%' . $wpdb->esc_like($search) . '%' // Properly escape the search term for a LIKE query. ); } // Add the ordering to the query. $sql .= " ORDER BY id DESC"; // Execute the final query. $certificates = $wpdb->get_results($sql, ARRAY_A); // IMPORTANT: Switch back to the main WordPress database. $wpdb->select(DB_NAME); // Check for any database errors. if ($wpdb->last_error) { wp_send_json_error(['message' => 'Database query failed while fetching certificates.'], 500); } else { // Send the resulting list of certificates back to the dashboard. wp_send_json_success($certificates); } } // Register the AJAX action with WordPress. add_action('wp_ajax_get_certificates', 'bqcis_get_certificates_ajax_handler'); /** * AJAX HANDLER: Fetches all details for a single certificate from the bqcis_db database. * * This function retrieves a complete certificate record based on its ID. * It is triggered by the 'get_certificate_details' AJAX action, typically used * when an admin clicks a "View" or "Edit" button. */ function bqcis_get_certificate_details_ajax_handler() { // Check for a valid security nonce. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // Sanitize the certificate ID, which is passed as a GET parameter (e.g., ?id=123). $id = isset($_GET['id']) ? absint($_GET['id']) : 0; // A valid ID is required. if (empty($id)) { wp_send_json_error(['message' => 'A valid Certificate ID is required.'], 400); } // Switch to your custom bqcis_db database. $wpdb->select('bqcis_db'); // Use $wpdb->prepare for security to prevent SQL injection. $sql = $wpdb->prepare("SELECT * FROM certificates WHERE id = %d", $id); // Use $wpdb->get_row to fetch a single, complete record. $certificate = $wpdb->get_row($sql, ARRAY_A); // IMPORTANT: Switch back to the main WordPress database. $wpdb->select(DB_NAME); // Check if the query returned a result. if ($certificate) { // Send the full certificate data back to the dashboard. wp_send_json_success($certificate); } else { // If no certificate was found with that ID, send a 404 error. wp_send_json_error(['message' => 'Certificate not found.'], 404); } } // Register the AJAX action with WordPress. add_action('wp_ajax_get_certificate_details', 'bqcis_get_certificate_details_ajax_handler'); /** * AJAX HANDLER: Revokes a certificate in the bqcis_db database. * * This function handles a request to update a certificate's status to 'Revoked'. * It is triggered by the 'revoke_certificate' AJAX action. */ function bqcis_revoke_certificate_ajax_handler() { // Check for a valid security nonce. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // Decode the incoming JSON data. $data = json_decode(file_get_contents('php://input'), true); // Sanitize the ID to ensure it's a safe, positive integer. $id = isset($data['id']) ? absint($data['id']) : 0; // A valid ID is required to perform this action. if (empty($id)) { wp_send_json_error(['message' => 'A valid Certificate ID is required.'], 400); } // Switch to the custom bqcis_db database. $wpdb->select('bqcis_db'); // Use the secure $wpdb->update method to change the status. $result = $wpdb->update( 'certificates', // The table name [ 'status' => 'Revoked' ], // The data to update [ 'id' => $id ], // The WHERE clause to identify the record [ '%s' ], // The format for the data being updated [ '%d' ] // The format for the WHERE clause ); // IMPORTANT: Switch back to the main WordPress database. $wpdb->select(DB_NAME); // Check the result of the update operation. if ($result === false) { wp_send_json_error(['message' => 'Failed to revoke certificate in the database.'], 500); } else { wp_send_json_success(['message' => 'Certificate has been revoked successfully.']); } } // Register the AJAX action with WordPress. add_action('wp_ajax_revoke_certificate', 'bqcis_revoke_certificate_ajax_handler'); /** * HELPER FUNCTION: Creates a digital signature for a data string. * * This function reads the private key from the theme folder and uses it to * create a secure SHA256 signature for the provided data. * * @param string $data_string The string of data to be signed. * @return string|null The base64-encoded signature, or null on failure. */ function bqcis_create_signature($data_string) { // get_stylesheet_directory() is a WordPress function that gets the path to the active child theme. $private_key_path = get_stylesheet_directory() . '/private_key.pem'; if (!file_exists($private_key_path)) { return null; // The key file was not found. } $private_key = openssl_pkey_get_private(file_get_contents($private_key_path)); if (!$private_key) { return null; // The key file could not be read or is invalid. } // Create the signature. openssl_sign($data_string, $signature, $private_key, OPENSSL_ALGO_SHA256); openssl_free_key($private_key); return base64_encode($signature); } /** * AJAX HANDLER: Creates a new certificate, including file upload and signing. * * This is a complex handler that processes multipart/form-data from the "Create * Certificate" form. It uses WordPress's native functions to securely handle * the file upload and sanitizes all form fields before database insertion. * It is triggered by the 'create_certificate' AJAX action. */ function bqcis_create_certificate_ajax_handler() { // Check for a valid security nonce. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // --- 1. Securely Handle the File Upload --- $file_url = null; // We will store the public URL of the file. if (!empty($_FILES['certificate_file']['name'])) { // WordPress requires these files to use its secure upload handler. require_once(ABSPATH . 'wp-admin/includes/file.php'); require_once(ABSPATH . 'wp-admin/includes/image.php'); require_once(ABSPATH . 'wp-admin/includes/media.php'); // Let WordPress handle the entire upload process. This is the most secure method. // It sanitizes the filename, checks permissions, and moves it to the 'wp-content/uploads' folder. $uploaded_file = $_FILES['certificate_file']; $upload_overrides = ['test_form' => false]; $movefile = wp_handle_upload($uploaded_file, $upload_overrides); if ($movefile && !isset($movefile['error'])) { // The file was uploaded successfully. We store its public URL. $file_url = $movefile['url']; } else { // If the upload failed, send an error and stop. wp_send_json_error(['message' => 'File upload failed: ' . $movefile['error']], 500); } } // --- 2. Sanitize All Incoming Form Fields --- $data = []; // A list of all expected simple text fields. $fields = [ 'certificate_code', 'company_name', 'industry', 'inspection_type', 'issue_date', 'expiry_date', 'importer_name', 'exporter_name', 'vessel', 'voyage_no', 'port_loading', 'port_discharge', 'bl_no', 'sailing_date', 'commercial_invoice', 'invoice_date', 'currency', 'amount', 'incoterm', 'lc_no', 'quantity', 'inspector_name', 'place_of_inspection' ]; foreach ($fields as $field) { $data[$field] = isset($_POST[$field]) ? sanitize_text_field($_POST[$field]) : null; } // Sanitize multi-line text fields separately. $data['importer_address'] = isset($_POST['importer_address']) ? sanitize_textarea_field($_POST['importer_address']) : null; $data['exporter_address'] = isset($_POST['exporter_address']) ? sanitize_textarea_field($_POST['exporter_address']) : null; // Sanitize fields from the rich text editor, allowing safe HTML. $data['goods_description'] = isset($_POST['goods_description']) ? wp_kses_post($_POST['goods_description']) : null; $data['analysis'] = isset($_POST['analysis']) ? wp_kses_post($_POST['analysis']) : null; // --- 3. Create the Digital Signature --- // The signature is based on a consistent set of key data points. $data_to_sign = implode('|', [ $data['certificate_code'], $data['company_name'], $data['inspection_type'], $data['issue_date'], $data['expiry_date'] ]); $signature = bqcis_create_signature($data_to_sign); if ($signature === null) { wp_send_json_error(['message' => 'Could not create digital signature. Ensure private_key.pem is in the theme folder and is readable.'], 500); } // --- 4. Insert the Final Record into the Database --- $wpdb->select('bqcis_db'); $result = $wpdb->insert( 'certificates', [ 'certificate_code' => $data['certificate_code'], 'company_name' => $data['company_name'], 'industry' => $data['industry'], 'inspection_type' => $data['inspection_type'], 'issue_date' => $data['issue_date'], 'expiry_date' => $data['expiry_date'], 'importer_name' => $data['importer_name'], 'importer_address' => $data['importer_address'], 'exporter_name' => $data['exporter_name'], 'exporter_address' => $data['exporter_address'], 'vessel' => $data['vessel'], 'voyage_no' => $data['voyage_no'], 'port_loading' => $data['port_loading'], 'port_discharge' => $data['port_discharge'], 'bl_no' => $data['bl_no'], 'sailing_date' => $data['sailing_date'], 'commercial_invoice' => $data['commercial_invoice'], 'invoice_date' => $data['invoice_date'], 'currency' => $data['currency'], 'amount' => $data['amount'], 'incoterm' => $data['incoterm'], 'lc_no' => $data['lc_no'], 'goods_description' => $data['goods_description'], 'analysis' => $data['analysis'], 'quantity' => $data['quantity'], 'inspector_name' => $data['inspector_name'], 'place_of_inspection' => $data['place_of_inspection'], 'file_path' => $file_url, // Store the public URL of the uploaded file. 'signature' => $signature, ] ); $wpdb->select(DB_NAME); if ($result === false) { // Provide a more specific error for duplicate certificate codes. $error_message = (strpos($wpdb->last_error, 'Duplicate entry') !== false) ? 'This Certificate Code already exists. Please use a unique code.' : 'A database error occurred while creating the certificate.'; wp_send_json_error(['message' => $error_message], 500); } else { wp_send_json_success(['message' => 'Certificate created and signed successfully!']); } } // Register the AJAX action with WordPress. add_action('wp_ajax_create_certificate', 'bqcis_create_certificate_ajax_handler'); /** * AJAX HANDLER: Updates an existing certificate, including optional file upload and re-signing. * * This is a complex handler that processes multipart/form-data from the "Edit * Certificate" form. It sanitizes all fields, handles an optional new file * upload, re-creates the digital signature, and updates the record in the database. * It is triggered by the 'update_certificate' AJAX action. */ function bqcis_update_certificate_ajax_handler() { // Check for a valid security nonce. check_ajax_referer('bqcis_nonce', 'security'); global $wpdb; // --- 1. Sanitize the Certificate ID --- $id = isset($_POST['id']) ? absint($_POST['id']) : 0; if (empty($id)) { wp_send_json_error(['message' => 'Certificate ID is missing and required for an update.'], 400); } // --- 2. Securely Handle an Optional New File Upload --- $file_url = null; if (!empty($_FILES['certificate_file']['name'])) { require_once(ABSPATH . 'wp-admin/includes/file.php'); require_once(ABSPATH . 'wp-admin/includes/image.php'); require_once(ABSPATH . 'wp-admin/includes/media.php'); $uploaded_file = $_FILES['certificate_file']; $upload_overrides = ['test_form' => false]; $movefile = wp_handle_upload($uploaded_file, $upload_overrides); if ($movefile && !isset($movefile['error'])) { $file_url = $movefile['url']; // Store the public URL of the new file. } else { wp_send_json_error(['message' => 'New file upload failed: ' . $movefile['error']], 500); } } // --- 3. Sanitize All Incoming Form Fields --- $data = []; $fields = [ 'certificate_code', 'company_name', 'industry', 'inspection_type', 'issue_date', 'expiry_date', 'importer_name', 'exporter_name', 'vessel', 'voyage_no', 'port_loading', 'port_discharge', 'bl_no', 'sailing_date', 'commercial_invoice', 'invoice_date', 'currency', 'amount', 'incoterm', 'lc_no', 'quantity', 'inspector_name', 'place_of_inspection' ]; foreach ($fields as $field) { $data[$field] = isset($_POST[$field]) ? sanitize_text_field($_POST[$field]) : null; } $data['importer_address'] = isset($_POST['importer_address']) ? sanitize_textarea_field($_POST['importer_address']) : null; $data['exporter_address'] = isset($_POST['exporter_address']) ? sanitize_textarea_field($_POST['exporter_address']) : null; $data['goods_description'] = isset($_POST['goods_description']) ? wp_kses_post($_POST['goods_description']) : null; $data['analysis'] = isset($_POST['analysis']) ? wp_kses_post($_POST['analysis']) : null; $data['last_modified_by'] = wp_get_current_user()->user_login; // Record which user made the change. // --- 4. Re-create the Digital Signature --- $data_to_sign = implode('|', [ $data['certificate_code'], $data['company_name'], $data['inspection_type'], $data['issue_date'], $data['expiry_date'] ]); $signature = bqcis_create_signature($data_to_sign); if ($signature === null) { wp_send_json_error(['message' => 'Could not re-create digital signature. Check private_key.pem.'], 500); } // --- 5. Prepare the Data Array for Database Update --- $update_data = [ 'certificate_code' => $data['certificate_code'], 'company_name' => $data['company_name'], 'industry' => $data['industry'], 'inspection_type' => $data['inspection_type'], 'issue_date' => $data['issue_date'], 'expiry_date' => $data['expiry_date'], 'importer_name' => $data['importer_name'], 'importer_address' => $data['importer_address'], 'exporter_name' => $data['exporter_name'], 'exporter_address' => $data['exporter_address'], 'vessel' => $data['vessel'], 'voyage_no' => $data['voyage_no'], 'port_loading' => $data['port_loading'], 'port_discharge' => $data['port_discharge'], 'bl_no' => $data['bl_no'], 'sailing_date' => $data['sailing_date'], 'commercial_invoice' => $data['commercial_invoice'], 'invoice_date' => $data['invoice_date'], 'currency' => $data['currency'], 'amount' => $data['amount'], 'incoterm' => $data['incoterm'], 'lc_no' => $data['lc_no'], 'goods_description' => $data['goods_description'], 'analysis' => $data['analysis'], 'quantity' => $data['quantity'], 'inspector_name' => $data['inspector_name'], 'place_of_inspection' => $data['place_of_inspection'], 'signature' => $signature, 'last_modified_by' => $data['last_modified_by'], ]; // Only add the file_path to the update array if a new file was actually uploaded. if ($file_url !== null) { $update_data['file_path'] = $file_url; } // --- 6. Perform the Database Update --- $wpdb->select('bqcis_db'); $result = $wpdb->update( 'certificates', $update_data, // The array of data to update [ 'id' => $id ] // The WHERE clause to find the correct record ); $wpdb->select(DB_NAME); if ($result === false) { $error_message = (strpos($wpdb->last_error, 'Duplicate entry') !== false) ? 'This Certificate Code already exists for another entry. Please use a unique code.' : 'A database error occurred while updating the certificate.'; wp_send_json_error(['message' => $error_message], 500); } else { wp_send_json_success(['message' => 'Certificate updated successfully!']); } } // Register the AJAX action with WordPress. add_action('wp_ajax_update_certificate', 'bqcis_update_certificate_ajax_handler'); https://bqcis.com/wp-sitemap-posts-post-1.xmlhttps://bqcis.com/wp-sitemap-posts-page-1.xmlhttps://bqcis.com/wp-sitemap-taxonomies-category-1.xmlhttps://bqcis.com/wp-sitemap-users-1.xml