get_all_fields(null, true); if (empty($all_fields)) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('CFEP RENDER_BLOCK: No fields found!'); return $block_content; } // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('CFEP RENDER_BLOCK: Found ' . count($all_fields) . ' fields in database'); // Create position map $field_positions = array(); foreach ($all_fields as $field) { if (!$field['enabled']) continue; $field_key = $field['field_key']; $position = (int)$field['position']; $field_positions[$field_key] = $position; // Also map variations $variations = array( str_replace(array('shipping_', 'billing_'), '', $field_key), str_replace('cfep/', '', $field_key), 'cfep-' . str_replace(array('shipping_', 'billing_', 'cfep/'), '', $field_key), ); foreach ($variations as $var) { $field_positions[$var] = $position; } // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('CFEP RENDER_BLOCK: Field "' . $field_key . '" position: ' . $position); } // DEBUG: Log the HTML to see structure // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('CFEP RENDER_BLOCK: HTML Preview: ' . substr($block_content, 0, 500)); // Parse and reorder HTML $block_content = $this->reorder_fields_in_html($block_content, $field_positions); return $block_content; } /** * Reorder fields in HTML using DOM manipulation */ private function reorder_fields_in_html($html, $field_positions) { if (empty($html) || empty($field_positions)) { return $html; } // Use DOMDocument to parse HTML $dom = new DOMDocument(); // Suppress errors for malformed HTML libxml_use_internal_errors(true); // Load HTML with UTF-8 encoding $dom->loadHTML('' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); libxml_clear_errors(); // Find all form fields (divs with class containing 'wp-block' or 'wc-block') $xpath = new DOMXPath($dom); $field_divs = $xpath->query("//*[contains(@class, 'wp-block') or contains(@class, 'wc-block')]"); if ($field_divs->length === 0) { return $html; } // Extract fields with their positions $fields_array = array(); foreach ($field_divs as $index => $field_div) { $class_attr = $field_div->getAttribute('class'); // Try to identify field from classes or id attributes $position = 9999; // Default to bottom // Look for field identifiers in class names foreach ($field_positions as $field_key => $field_position) { if (strpos($class_attr, $field_key) !== false) { $position = $field_position; break; } // Also check input elements inside $inputs = $field_div->getElementsByTagName('input'); foreach ($inputs as $input) { $input_name = $input->getAttribute('name'); if (strpos($input_name, $field_key) !== false) { $position = $field_position; break 2; } } // Check select elements $selects = $field_div->getElementsByTagName('select'); foreach ($selects as $select) { $select_name = $select->getAttribute('name'); if (strpos($select_name, $field_key) !== false) { $position = $field_position; break 2; } } } $fields_array[] = array( 'position' => $position, 'original_index' => $index, 'element' => $field_div, ); } // Sort by position usort($fields_array, function($a, $b) { return $a['position'] - $b['position']; }); // Get parent element if ($field_divs->length > 0 && $field_divs->item(0)->parentNode) { $parent = $field_divs->item(0)->parentNode; // Reorder elements in DOM foreach ($fields_array as $field_data) { $parent->appendChild($field_data['element']); } } // Return modified HTML $new_html = $dom->saveHTML(); // Remove XML declaration that was added $new_html = str_replace('', '', $new_html); return $new_html; } /** * THE SECRET SAUCE: Inject custom fields into default address fields EARLY * This makes WooCommerce treat them as native fields that can be reordered! * IMPORTANT: Only inject for Block checkout FRONTEND rendering! */ public function inject_custom_fields_into_defaults($fields) { // CRITICAL 1: NEVER inject in admin if (is_admin()) { return $fields; } // CRITICAL 2: NEVER inject during AJAX requests if (wp_doing_ajax()) { return $fields; } // CRITICAL 3: NEVER inject during REST API calls (except Block checkout) if (defined('REST_REQUEST') && REST_REQUEST) { // Only allow if it's the Block checkout store API if (!isset($_SERVER['REQUEST_URI']) || strpos(sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])), 'wc/store') === false) { return $fields; } } // CRITICAL 4: Only inject on actual checkout page if (!is_checkout()) { return $fields; } // CRITICAL 5: Only inject for Block checkout, NOT Classic if (!function_exists('has_block')) { return $fields; } $checkout_page_id = function_exists('wc_get_page_id') ? wc_get_page_id('checkout') : 0; if (!$checkout_page_id || !has_block('woocommerce/checkout', $checkout_page_id)) { return $fields; // Not Block checkout } $all_fields = CFEP_Block_Field_Manager::instance()->get_all_fields(null, true); foreach ($all_fields as $field) { if (!$field['enabled']) continue; // Only inject address section custom fields if ($field['location'] !== 'address') continue; // Get base field key (remove shipping_/billing_ prefix) $key = str_replace(array('shipping_', 'billing_'), '', $field['field_key']); // Skip if it's already a default WooCommerce field $default_wc_fields = array('first_name', 'last_name', 'company', 'address_1', 'address_2', 'city', 'state', 'postcode', 'country', 'phone'); if (in_array($key, $default_wc_fields)) { continue; // Don't override WooCommerce defaults } // INJECT CUSTOM FIELD AS IF IT WERE A DEFAULT FIELD! $fields[$key] = array( 'label' => $field['label'], 'required' => (bool)$field['required'], 'class' => array('form-row-wide'), 'type' => $field['field_type'], 'priority' => (int)$field['position'] * 10, 'autocomplete' => 'off', ); // Add placeholder if exists if (!empty($field['placeholder'])) { $fields[$key]['placeholder'] = $field['placeholder']; } // Add options for select fields if ($field['field_type'] === 'select' && !empty($field['options'])) { $fields[$key]['options'] = array('' => __('Select...', 'akz-checkout-fields-manager-for-woocommerce')) + $field['options']; } // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('CFEP INJECT: Added custom field "' . $key . '" to default address fields at priority ' . ($field['position'] * 10)); } return $fields; } /** * Reorder shipping fields (will now include our injected custom fields!) */ public function reorder_shipping_fields($fields) { $all_fields = CFEP_Block_Field_Manager::instance()->get_all_fields(null, true); foreach ($all_fields as $field) { if (!$field['enabled']) continue; $key = $field['field_key']; $position = (int)$field['position']; // Address section fields (shipping_first_name, etc) if ($field['location'] === 'address' && isset($fields[$key])) { $fields[$key]['priority'] = $position * 10; // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('CFEP: Set shipping field ' . $key . ' priority to ' . ($position * 10)); } } return $fields; } public function control_billing_required($fields) { $all_fields = CFEP_Block_Field_Manager::instance()->get_all_fields(null, true); foreach ($all_fields as $field) { $key = $field['field_key']; if (isset($fields[$key])) { $fields[$key]['required'] = (bool)$field['required']; } } return $fields; } public function control_required_status($fields) { $all_fields = CFEP_Block_Field_Manager::instance()->get_all_fields(null, true); foreach ($all_fields as $field) { // Remove prefix to get base field name $key = str_replace(array('shipping_', 'billing_'), '', $field['field_key']); if (isset($fields[$key])) { $fields[$key]['required'] = (bool)$field['required']; } } return $fields; } public function reorder_billing_fields($fields) { $all_fields = CFEP_Block_Field_Manager::instance()->get_all_fields(null, true); foreach ($all_fields as $field) { if (!$field['enabled']) continue; $key = $field['field_key']; $position = (int)$field['position']; // Contact section fields (billing_email, etc) if ($field['location'] === 'contact' && isset($fields[$key])) { $fields[$key]['priority'] = $position * 10; // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('CFEP: Set billing field ' . $key . ' priority to ' . ($position * 10)); } } return $fields; } public function reorder_default_locale($locale) { $all_fields = CFEP_Block_Field_Manager::instance()->get_all_fields(null, true); foreach ($all_fields as $field) { if (!$field['enabled']) continue; $key = str_replace(array('shipping_', 'billing_'), '', $field['field_key']); $position = (int)$field['position']; // Default locale uses unprefixed keys if (in_array($key, array('first_name', 'last_name', 'company', 'address_1', 'address_2', 'city', 'state', 'postcode', 'country', 'phone'))) { $locale[$key]['priority'] = $position * 10; $locale[$key]['required'] = (bool)$field['required']; // Apply required status } } // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('CFEP: Default locale priorities set'); return $locale; } public function reorder_fields_locale($locales) { $all_fields = CFEP_Block_Field_Manager::instance()->get_all_fields(null, true); foreach ($locales as $country_code => $country_locale) { foreach ($all_fields as $field) { if (!$field['enabled']) continue; $key = str_replace(array('shipping_', 'billing_'), '', $field['field_key']); $position = (int)$field['position']; if (in_array($key, array('first_name', 'last_name', 'company', 'address_1', 'address_2', 'city', 'state', 'postcode', 'country', 'phone'))) { $locales[$country_code][$key]['priority'] = $position * 10; $locales[$country_code][$key]['required'] = (bool)$field['required']; // Apply required status } } } // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('CFEP: Country locale priorities set for ' . count($locales) . ' countries'); return $locales; } public function add_custom_field_priorities($fields) { $all_fields = CFEP_Block_Field_Manager::instance()->get_all_fields(null, true); foreach ($all_fields as $field) { if (!$field['enabled']) continue; $key = str_replace(array('shipping_', 'billing_'), '', $field['field_key']); $position = (int)$field['position']; if (isset($fields[$key])) { $fields[$key]['priority'] = $position * 10; } } return $fields; } public function register_fields() { if (!function_exists('woocommerce_register_additional_checkout_field')) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('CFEP: woocommerce_register_additional_checkout_field function not available!'); return; } $all_fields = CFEP_Block_Field_Manager::instance()->get_all_fields(null, true); foreach ($all_fields as $field) { if (!$field['enabled']) { continue; } // Skip default WooCommerce fields $default_fields = array( 'billing_email', 'shipping_first_name', 'shipping_last_name', 'shipping_company', 'shipping_address_1', 'shipping_address_2', 'shipping_city', 'shipping_state', 'shipping_postcode', 'shipping_country', 'shipping_phone' ); if (in_array($field['field_key'], $default_fields)) { continue; // Skip WooCommerce defaults } $location = $field['location']; // Map location if ($location === 'address') { $wc_location = 'address'; } elseif ($location === 'contact') { $wc_location = 'contact'; } else { $wc_location = 'order'; } $field_config = array( 'id' => 'cfep/' . $field['field_key'], 'label' => $field['label'], 'location' => $wc_location, 'required' => (bool)$field['required'], 'type' => $field['field_type'], ); // Add options for select fields if ($field['field_type'] === 'select' && !empty($field['options'])) { $options_array = array(); foreach ($field['options'] as $key => $value) { $options_array[] = array( 'value' => $key, 'label' => $value, ); } $field_config['options'] = $options_array; } // Add placeholder if exists if (!empty($field['placeholder'])) { $field_config['placeholder'] = $field['placeholder']; } woocommerce_register_additional_checkout_field($field_config); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log('CFEP: Registered field ' . $field['field_key'] . ' in location ' . $wc_location . ' via API'); } } }