ID : null; if ($profile_page_id && is_page($profile_page_id) && isset($_GET['profile_updated'])) { // Check if the redirect is simply adding/removing a trailing slash to our success URL // Allow this specific type of canonical redirect to proceed. $success_url_base = home_url('/trainer-profile/'); $success_url_with_flag = add_query_arg('profile_updated', '1', $success_url_base); // Compare URLs ignoring the trailing slash if (untrailingslashit($redirect_url) === untrailingslashit($success_url_with_flag)) { return $redirect_url; // Allow this specific redirect } // Otherwise, prevent any other canonical redirect on this specific request // error_log("[DEBUG CANONICAL] Preventing redirect from {$requested_url} to {$redirect_url}"); // Optional debug return false; } return $redirect_url; // Allow all other canonical redirects } /** * Checks if the profile form was submitted and processes it. * Hooked to wp_loaded. */ public function maybe_process_profile_submission() { if (isset($_POST['action']) && $_POST['action'] === self::PROFILE_ACTION) { $this->process_profile_submission(); } } /** * Enqueues styles and scripts. Reuses registration form styles. */ public function enqueue_scripts() { error_log('[DEBUG HVAC_Profile::enqueue_scripts] Method called.'); // Log method entry // Only enqueue on pages where the shortcode might be present // A more robust check might involve checking post content or using a flag $profile_page_object = get_page_by_path('trainer-profile', OBJECT, 'page'); $profile_page_id = $profile_page_object ? $profile_page_object->ID : null; error_log('[DEBUG HVAC_Profile::enqueue_scripts] Profile Page ID found: ' . print_r($profile_page_id, true)); // Log page ID result error_log('[DEBUG HVAC_Profile::enqueue_scripts] is_page check result: ' . (is_page($profile_page_id) ? 'true' : 'false')); // Log is_page result // More robust check: See if the current post content contains our shortcode global $post; $should_enqueue = false; if (is_a($post, 'WP_Post') && has_shortcode($post->post_content, 'hvac_trainer_profile')) { $should_enqueue = true; } error_log('[DEBUG HVAC_Profile::enqueue_scripts] Shortcode check result: ' . ($should_enqueue ? 'true' : 'false')); // Log shortcode check // if ($profile_page_id && is_page($profile_page_id)) { // Original check if ($should_enqueue) { // Use shortcode check instead error_log('[DEBUG HVAC_Profile::enqueue_scripts] Condition met, enqueuing scripts/styles.'); // Log condition met wp_enqueue_style( 'hvac-registration-style', HVAC_CE_PLUGIN_URL . 'assets/css/hvac-registration.css', array(), // Dependencies HVAC_CE_VERSION ); wp_enqueue_script( 'hvac-registration-script', HVAC_CE_PLUGIN_URL . 'assets/js/hvac-registration.js', array('jquery'), // Dependencies HVAC_CE_VERSION, true // Load in footer ); // Pass country/state data to JS (same as registration) $countries = $this->get_country_list(); $us_states = $this->get_us_states(); $ca_provinces = $this->get_canadian_provinces(); // Add debug logging error_log('[DEBUG HVAC_Profile::enqueue_scripts] Countries: ' . print_r($countries, true)); error_log('[DEBUG HVAC_Profile::enqueue_scripts] US States: ' . print_r($us_states, true)); error_log('[DEBUG HVAC_Profile::enqueue_scripts] CA Provinces: ' . print_r($ca_provinces, true)); wp_localize_script('hvac-registration-script', 'hvac_reg_vars', array( 'ajax_url' => admin_url('admin-ajax.php'), // If needed for future AJAX 'countries' => $countries, 'us_states' => $us_states, 'ca_provinces' => $ca_provinces, 'selected_country' => '', // Will be populated dynamically if needed 'selected_state' => '' // Will be populated dynamically if needed )); } } /** * Renders the profile form shortcode. * Handles security checks and retrieves messages from transients. */ public function render_profile_form() { error_log('[DEBUG PROFILE RENDER] Entering render_profile_form'); // Log entry // Ensure instance exists (runs constructor and adds hooks) when shortcode is rendered self::instance(); // Restore instantiation via shortcode render // --- Security Check --- if (!is_user_logged_in() || !current_user_can('edit_hvac_profile')) { // Use appropriate capability return '

You must be logged in as a trainer to view this page.

'; } $user_id = get_current_user_id(); $errors = []; $success_message = ''; $submitted_data = []; $output = ''; // Build output string // Check for messages/errors from redirect if (isset($_GET['profile_updated']) && $_GET['profile_updated'] === '1') { $success_message = 'Profile updated successfully.'; $output .= ''; // Keep debug marker $output .= '

' . esc_html($success_message) . '

'; // Append to output } elseif (isset($_GET['profile_error']) && $_GET['profile_error'] === '1' && isset($_GET['tid'])) { $transient_id_from_url = sanitize_key($_GET['tid']); $transient_key = self::TRANSIENT_PREFIX . $transient_id_from_url; $transient_data = get_transient($transient_key); if ($transient_data && is_array($transient_data)) { $errors = $transient_data['errors'] ?? []; $submitted_data = $transient_data['data'] ?? []; delete_transient($transient_key); } else { $errors['transient'] = 'Could not retrieve submission details. Please try again.'; } if (!empty($errors)) { $output .= '
'; // Append to output foreach ($errors as $error_key => $error_message) { $output .= '

Error: ' . esc_html($error_message) . '

'; // Append to output } $output .= '
'; // Append to output } } // Display the form HTML (needs to return instead of echo now) $output .= $this->display_profile_form_html($user_id, $errors, $submitted_data); // <<< Pass submitted_data, append output error_log('[DEBUG PROFILE RENDER] Exiting render_profile_form'); // Log exit return $output; // Return the built string } /** * Displays the actual profile form HTML. * NOW RETURNS HTML STRING INSTEAD OF ECHOING. * * @param int $user_id The ID of the current user. * @param array $errors Array of validation errors. * @param array $submitted_data Array of submitted form data (used for repopulation on error). * @return string Form HTML. */ private function display_profile_form_html($user_id, $errors = [], $submitted_data = []) { // <<< Added $submitted_data param error_log('[DEBUG PROFILE RENDER] Entering display_profile_form_html for user ID: ' . $user_id); // Log entry $current_user = get_userdata($user_id); if (!$current_user) { return '

Error: Could not load user data.

'; // Return instead of echo } // Fetch user meta data - adapt keys from registration logic $meta_keys = [ 'first_name', 'last_name', 'description', // Core WP fields 'user_url', // Core WP field 'user_linkedin', 'personal_accreditation', 'business_name', 'business_phone', 'business_email', 'business_website', 'business_description', 'user_country', 'user_state', 'user_city', 'user_zip', 'business_type', 'training_audience', 'training_formats', 'training_locations', 'training_resources', 'profile_image_id', // Store attachment ID instead of URL 'linked_venue_id' // Store linked venue post ID ]; $user_meta = []; foreach ($meta_keys as $key) { // Core WP fields are directly on the user object if (isset($current_user->$key)) { $user_meta[$key] = $current_user->$key; } else { $user_meta[$key] = get_user_meta($user_id, $key, true); } } // Special handling for user_email and display_name from user object $user_meta['user_email'] = $current_user->user_email; $user_meta['display_name'] = $current_user->display_name; // Prioritize submitted data (if available, e.g., after error) over stored meta for repopulation $form_data = !empty($submitted_data) ? $submitted_data : $user_meta; // <<< Use submitted data if present // Ensure array types for checkboxes/multiselects using the correct data source $array_keys = ['training_audience', 'training_formats', 'training_locations', 'training_resources']; foreach($array_keys as $key) { if (!isset($form_data[$key]) || !is_array($form_data[$key])) { // Check form_data $form_data[$key] = []; // Default to empty array if not set or not array } } // Get profile image URL (still based on stored meta) $profile_image_url = ''; if (!empty($user_meta['profile_image_id']) && is_numeric($user_meta['profile_image_id'])) { $profile_image_url = wp_get_attachment_image_url((int)$user_meta['profile_image_id'], 'thumbnail'); // Or another appropriate size } // Get linked venue info (still based on stored meta) $linked_venue_id = $user_meta['linked_venue_id'] ?? null; $venue_info = ''; if ($linked_venue_id && get_post_status($linked_venue_id) === 'publish') { $venue_title = get_the_title($linked_venue_id); $venue_url = get_permalink($linked_venue_id); // Or admin edit link if preferred $venue_info = 'Linked Training Venue: ' . esc_html($venue_title) . ''; // TODO: Add more venue details if needed (address etc.) } elseif ($linked_venue_id) { $venue_info = 'Linked Training Venue: (Venue not published or found)'; } else { $venue_info = 'No Training Venue linked to this profile.'; } // Start building HTML string $html = '
'; // Reuse registration form class $html .= '

Edit Trainer Profile

'; $html .= '
'; $html .= ''; $html .= wp_nonce_field(self::PROFILE_ACTION, 'hvac_profile_nonce', true, false); // Return nonce field instead of echoing // Account Information $html .= '
'; $html .= '

Account Information

'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['user_email'])) $html .= '

' . esc_html($errors['user_email']) . '

'; $html .= '
'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; $html .= 'Leave blank to keep current password. Must be 8+ chars, upper, lower, number.'; if (isset($errors['user_pass'])) $html .= '

' . esc_html($errors['user_pass']) . '

'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['confirm_password'])) $html .= '

' . esc_html($errors['confirm_password']) . '

'; $html .= '
'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; $html .= 'Required to update email or password.'; if (isset($errors['current_password'])) $html .= '

' . esc_html($errors['current_password']) . '

'; $html .= '
'; $html .= '
'; // End Account Info // Personal Information Section $html .= '
'; $html .= '

Personal Information

'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['first_name'])) $html .= '

' . esc_html($errors['first_name']) . '

'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['last_name'])) $html .= '

' . esc_html($errors['last_name']) . '

'; $html .= '
'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; $html .= 'This will be the name displayed to other users on the site.'; if (isset($errors['display_name'])) $html .= '

' . esc_html($errors['display_name']) . '

'; $html .= '
'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['user_url'])) $html .= '

' . esc_html($errors['user_url']) . '

'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['user_linkedin'])) $html .= '

' . esc_html($errors['user_linkedin']) . '

'; $html .= '
'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; $html .= 'Enter your abbreviated accreditations separated by commas.'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; $html .= 'A short bio about yourself. This will be displayed on your profile page.'; if (isset($errors['description'])) $html .= '

' . esc_html($errors['description']) . '

'; $html .= '
'; $html .= '
'; $html .= ''; if ($profile_image_url) { $html .= '
'; $html .= 'Current Profile Image'; $html .= ''; $html .= '
'; } $html .= ''; $html .= 'Upload a new image to replace the current one (.jpg, .png, .gif).'; if (isset($errors['profile_image'])) $html .= '

' . esc_html($errors['profile_image']) . '

'; $html .= '
'; $html .= '
'; // End Personal Info // Business Information Section $html .= '
'; $html .= '

Business Information

'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['business_name'])) $html .= '

' . esc_html($errors['business_name']) . '

'; $html .= '
'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['business_phone'])) $html .= '

' . esc_html($errors['business_phone']) . '

'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['business_email'])) $html .= '

' . esc_html($errors['business_email']) . '

'; $html .= '
'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['business_website'])) $html .= '

' . esc_html($errors['business_website']) . '

'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['business_description'])) $html .= '

' . esc_html($errors['business_description']) . '

'; $html .= '
'; $html .= '
'; // End Business Info // Address Information Section $html .= '
'; $html .= '

Address Information

'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['user_country'])) $html .= '

' . esc_html($errors['user_country']) . '

'; $html .= '
'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; $other_style = (($form_data['user_state'] ?? '') === 'Other' && ($form_data['user_country'] ?? '') !== 'United States' && ($form_data['user_country'] ?? '') !== 'Canada') ? '' : 'display:none;'; $html .= ''; if (isset($errors['user_state'])) $html .= '

' . esc_html($errors['user_state']) . '

'; if (isset($errors['user_state_other'])) $html .= '

' . esc_html($errors['user_state_other']) . '

'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['user_city'])) $html .= '

' . esc_html($errors['user_city']) . '

'; $html .= '
'; $html .= '
'; $html .= '
'; $html .= ''; $html .= ''; if (isset($errors['user_zip'])) $html .= '

' . esc_html($errors['user_zip']) . '

'; $html .= '
'; $html .= '
'; // End Address Info // Training Venue Section $html .= '
'; $html .= '

Training Venue

'; $html .= '
'; $html .= '

' . wp_kses_post($venue_info) . '

'; $html .= '
'; $html .= '
'; // End Training Venue // Training Information Section $html .= '
'; $html .= '

Training Information

'; $html .= '
'; $html .= ''; $html .= 'What type of business are you?'; $html .= '
'; $business_types = ["Manufacturer", "Distributor", "Contractor", "Consultant", "Educator", "Government", "Other"]; foreach ($business_types as $type) { $html .= ''; } $html .= '
'; if (isset($errors['business_type'])) $html .= '

' . esc_html($errors['business_type']) . '

'; $html .= '
'; $html .= '
'; $html .= ''; $html .= 'Who do you offer training to? (Select all that apply)'; $html .= '
'; $audience_options = [ "Anyone" => "Anyone (open to the public)", "Industry professionals" => "Industry professionals", "Internal staff" => "Internal staff in my company", "Registered students" => "Registered students/members of my org/institution" ]; $selected_audience = $form_data['training_audience'] ?? []; // Use form_data foreach ($audience_options as $value => $label) { $html .= ''; } $html .= '
'; if (isset($errors['training_audience'])) $html .= '

' . esc_html($errors['training_audience']) . '

'; $html .= '
'; $html .= '
'; $html .= ''; $html .= 'What formats of training do you offer?'; $html .= '
'; $format_options = ["In-person", "Virtual", "Hybrid", "On-demand"]; $selected_formats = $form_data['training_formats'] ?? []; // Use form_data foreach ($format_options as $option) { $html .= ''; } $html .= '
'; if (isset($errors['training_formats'])) $html .= '

' . esc_html($errors['training_formats']) . '

'; $html .= '
'; $html .= '
'; $html .= ''; $html .= 'Where are you willing to provide training? (Select all that apply)'; $html .= '
'; $location_options = ["Online", "Local", "Regional", "Travel National", "Travel International"]; $selected_locations = $form_data['training_locations'] ?? []; // Use form_data foreach ($location_options as $option) { $html .= ''; } $html .= '
'; if (isset($errors['training_locations'])) $html .= '

' . esc_html($errors['training_locations']) . '

'; $html .= '
'; $html .= '
'; $html .= ''; $html .= 'What training resources do you have access to? (Select all that apply)'; $html .= '
'; $resource_options = [ "Classroom" => "Classroom", "Training Lab" => "Training Lab", "Ducted Furnace(s)" => "Ducted Furnace(s)", "Ducted Air Handler(s)" => "Ducted Air Handler(s)", "Ducted Air Conditioner(s)" => "Ducted Air Conditioner(s)", "Ducted Heat Pump(s)" => "Ducted Heat Pump(s)", "Ductless Heat Pump(s)" => "Ductless Heat Pump(s)", "Training Manuals" => "Training Manuals", "Presentation Slides" => "Presentation Slides", "LMS Platform / SCORM Files" => "LMS Platform / SCORM Files", "Custom Curriculum" => "Custom Curriculum", "Other" => "Other" ]; $selected_resources = $form_data['training_resources'] ?? []; // Use form_data foreach ($resource_options as $value => $label) { $html .= ''; } $html .= '
'; if (isset($errors['training_resources'])) $html .= '

' . esc_html($errors['training_resources']) . '

'; $html .= '
'; $html .= '
'; // End Training Info $html .= '
'; $html .= ''; $html .= '
'; $html .= '
'; $html .= '
'; // End .hvac-profile-form error_log('[DEBUG PROFILE RENDER] Exiting display_profile_form_html'); // Log exit return $html; // Return the built string } /** * Processes the profile form submission. * Handles validation, user update, and redirects. */ public function process_profile_submission() { // Verify nonce if (!isset($_POST['hvac_profile_nonce']) || !wp_verify_nonce($_POST['hvac_profile_nonce'], self::PROFILE_ACTION)) { wp_die('Security check failed.'); } // Check user logged in if (!is_user_logged_in()) { wp_die('You must be logged in to update your profile.'); } $user_id = get_current_user_id(); // Check capability if (!current_user_can('edit_hvac_profile', $user_id)) { // Check capability for the specific user wp_die('You do not have permission to edit this profile.'); } // Sanitize POST data (basic sanitization, more specific in validation/update) $submitted_data = stripslashes_deep($_POST); // Validate data $errors = $this->validate_profile_data($submitted_data, $user_id); // Redirect back with errors if validation fails if (!empty($errors)) { $redirect_url = home_url('/trainer-profile/'); // Redirect back to profile page $this->redirect_with_errors($errors, $redirect_url, $submitted_data); // <<< Pass submitted data exit; // Stop execution after redirect } // Handle image upload/deletion $image_data = $_FILES['profile_image'] ?? null; $delete_image = isset($submitted_data['delete_profile_image']) && $submitted_data['delete_profile_image'] === '1'; // Attempt to update the user account $update_result = $this->update_trainer_account($user_id, $submitted_data, $image_data, $delete_image); // Handle update result if (is_wp_error($update_result)) { // Update failed, redirect back with error message(s) $errors = $update_result->get_error_messages(); // Get all error messages $error_codes = $update_result->get_error_codes(); $error_map = []; foreach($error_codes as $index => $code) { $error_map[$code] = $errors[$index]; // Map code to message } $redirect_url = home_url('/trainer-profile/'); $this->redirect_with_errors($error_map, $redirect_url, $submitted_data); // Pass error map and submitted data exit; } elseif ($update_result === true) { // Success! Redirect with success flag $redirect_url = add_query_arg('profile_updated', '1', home_url('/trainer-profile/')); wp_safe_redirect($redirect_url); exit; } else { // Should not happen if update_trainer_account always returns true or WP_Error $errors['general'] = 'An unexpected error occurred during profile update.'; $redirect_url = home_url('/trainer-profile/'); $this->redirect_with_errors($errors, $redirect_url, $submitted_data); // Pass submitted data exit; } } /** * Redirects back to a URL with errors stored in a transient. * NOW ACCEPTS SUBMITTED DATA TO STORE IN TRANSIENT. * * @param array $errors Array of errors (key => message). * @param string $redirect_url URL to redirect back to. * @param array $submitted_data The submitted form data. */ private function redirect_with_errors($errors, $redirect_url, $submitted_data) { // <<< Added $submitted_data param $transient_id = wp_generate_password(12, false); // Generate a unique ID for the transient $transient_key = self::TRANSIENT_PREFIX . $transient_id; // Store both errors and submitted data in the transient $transient_data = [ 'errors' => $errors, 'data' => $submitted_data, // <<< Store submitted data ]; set_transient($transient_key, $transient_data, MINUTE_IN_SECONDS * 5); // Store for 5 minutes // Add error flag and transient ID to the redirect URL $redirect_url = add_query_arg(array( 'profile_error' => '1', 'tid' => $transient_id ), $redirect_url); wp_safe_redirect($redirect_url); exit; } /** * Validates the submitted profile data. * Changed from private to public for unit testing. * * @param array $data Submitted form data. * @param int $user_id The ID of the user being updated. * @return array Array of errors (empty if valid). */ public function validate_profile_data($data, $user_id) { // Changed from private to public $errors = []; $current_user = get_userdata($user_id); // --- Account Information --- // Email if (empty($data['user_email'])) { $errors['user_email'] = 'Email address is required.'; } elseif (!is_email($data['user_email'])) { $errors['user_email'] = 'Invalid email address format.'; } elseif ($data['user_email'] !== $current_user->user_email && email_exists($data['user_email'])) { $errors['user_email'] = 'This email address is already in use by another account.'; } // Password (only validate if a new password is provided) if (!empty($data['user_pass'])) { if (strlen($data['user_pass']) < 8) { $errors['user_pass'] = 'Password must be at least 8 characters long.'; } elseif (!preg_match('/[A-Z]/', $data['user_pass'])) { $errors['user_pass'] = 'Password must contain at least one uppercase letter.'; } elseif (!preg_match('/[a-z]/', $data['user_pass'])) { $errors['user_pass'] = 'Password must contain at least one lowercase letter.'; } elseif (!preg_match('/[0-9]/', $data['user_pass'])) { $errors['user_pass'] = 'Password must contain at least one number.'; } elseif (empty($data['confirm_password'])) { $errors['confirm_password'] = 'Please confirm your new password.'; } elseif ($data['user_pass'] !== $data['confirm_password']) { $errors['confirm_password'] = 'Passwords do not match.'; } } // Current Password (required only if email or password changes) $email_changed = isset($data['user_email']) && $data['user_email'] !== $current_user->user_email; $password_changed = !empty($data['user_pass']); if (($email_changed || $password_changed) && empty($data['current_password'])) { $errors['current_password'] = 'Current password is required to update email or password.'; } // Note: Actual check if current password is correct happens in update_trainer_account // --- Personal Information --- if (empty($data['first_name'])) $errors['first_name'] = 'First Name is required.'; if (empty($data['last_name'])) $errors['last_name'] = 'Last Name is required.'; if (empty($data['display_name'])) $errors['display_name'] = 'Display Name is required.'; if (!empty($data['user_url']) && !filter_var($data['user_url'], FILTER_VALIDATE_URL)) $errors['user_url'] = 'Invalid Personal Website URL format.'; if (!empty($data['user_linkedin']) && !filter_var($data['user_linkedin'], FILTER_VALIDATE_URL)) $errors['user_linkedin'] = 'Invalid LinkedIn Profile URL format.'; if (empty($data['description'])) $errors['description'] = 'Biographical Info is required.'; // Profile Image Validation (basic) if (isset($_FILES['profile_image']) && $_FILES['profile_image']['error'] === UPLOAD_ERR_OK) { $allowed_types = ['image/jpeg', 'image/png', 'image/gif']; $file_info = wp_check_filetype_and_ext($_FILES['profile_image']['tmp_name'], $_FILES['profile_image']['name']); if (empty($file_info['type']) || !in_array($file_info['type'], $allowed_types)) { $errors['profile_image'] = 'Invalid file type. Please upload a JPG, PNG, or GIF image.'; } // Add size check if needed: // if ($_FILES['profile_image']['size'] > MAX_UPLOAD_SIZE) { // $errors['profile_image'] = 'File size exceeds limit.'; // } } elseif (isset($_FILES['profile_image']) && $_FILES['profile_image']['error'] !== UPLOAD_ERR_NO_FILE && $_FILES['profile_image']['error'] !== UPLOAD_ERR_OK) { $errors['profile_image'] = 'Error uploading profile image. Code: ' . $_FILES['profile_image']['error']; } // --- Business Information --- if (empty($data['business_name'])) $errors['business_name'] = 'Business Name is required.'; if (empty($data['business_phone'])) $errors['business_phone'] = 'Business Phone is required.'; if (empty($data['business_email'])) { $errors['business_email'] = 'Business Email is required.'; } elseif (!is_email($data['business_email'])) { $errors['business_email'] = 'Invalid Business Email format.'; } if (!empty($data['business_website']) && !filter_var($data['business_website'], FILTER_VALIDATE_URL)) $errors['business_website'] = 'Invalid Business Website URL format.'; if (empty($data['business_description'])) $errors['business_description'] = 'Business Description is required.'; // --- Address Information --- if (empty($data['user_country'])) $errors['user_country'] = 'Country is required.'; if (empty($data['user_state'])) { $errors['user_state'] = 'State/Province is required.'; } elseif ($data['user_state'] === 'Other' && empty($data['user_state_other'])) { $errors['user_state_other'] = 'Please specify your state/province.'; } if (empty($data['user_city'])) $errors['user_city'] = 'City is required.'; if (empty($data['user_zip'])) $errors['user_zip'] = 'Zip/Postal Code is required.'; // --- Training Information --- if (empty($data['business_type'])) $errors['business_type'] = 'Business Type is required.'; if (empty($data['training_audience'])) $errors['training_audience'] = 'Please select at least one option for Training Audience.'; if (empty($data['training_formats'])) $errors['training_formats'] = 'Please select at least one option for Training Formats.'; if (empty($data['training_locations'])) $errors['training_locations'] = 'Please select at least one option for Training Locations.'; if (empty($data['training_resources'])) $errors['training_resources'] = 'Please select at least one option for Training Resources.'; // Ensure checkbox/multiselect data is arrays if submitted (even if empty) $array_keys = ['training_audience', 'training_formats', 'training_locations', 'training_resources']; foreach($array_keys as $key) { if (isset($data[$key]) && !is_array($data[$key])) { $errors[$key] = 'Invalid data format for ' . $key . '.'; } elseif (!isset($data[$key])) { // If the field is required and not submitted at all (e.g., disabled JS) if (in_array($key, ['training_audience', 'training_formats', 'training_locations', 'training_resources'])) { // Add other required array fields if any $errors[$key] = 'Please select at least one option for ' . str_replace('_', ' ', ucfirst($key)) . '.'; } } } return $errors; } /** * Updates the user account and meta data. * * @param int $user_id The ID of the user to update. * @param array $data Sanitized submitted form data. * @param array|null $image_data Uploaded file data from $_FILES['profile_image']. * @param bool $delete_image Whether to delete the current profile image. * @return bool|WP_Error True on success, WP_Error on failure. */ private function update_trainer_account($user_id, $data, $image_data, $delete_image) { error_log('[DEBUG UPDATE_ACCOUNT] Entering function for user ID: ' . $user_id); $update_args = array( 'ID' => $user_id, ); // Sanitize and prepare data for wp_update_user if (isset($data['user_email'])) { $update_args['user_email'] = sanitize_email($data['user_email']); } if (!empty($data['user_pass'])) { $update_args['user_pass'] = $data['user_pass']; // wp_update_user handles hashing } if (isset($data['user_url'])) { $update_args['user_url'] = esc_url_raw($data['user_url']); } if (isset($data['display_name'])) { $update_args['display_name'] = sanitize_text_field($data['display_name']); } if (isset($data['first_name'])) { $update_args['first_name'] = sanitize_text_field($data['first_name']); } if (isset($data['last_name'])) { $update_args['last_name'] = sanitize_text_field($data['last_name']); } if (isset($data['description'])) { $update_args['description'] = sanitize_textarea_field($data['description']); } // Check current password if email or password is being changed $current_user = get_userdata($user_id); $needs_current_password_check = false; error_log('[DEBUG UPDATE_ACCOUNT] Checking if password or email update is needed.'); // Check if password needs updating if (!empty($data['user_pass'])) { error_log('[DEBUG UPDATE_ACCOUNT] New password provided.'); $needs_current_password_check = true; } // Check if email needs updating if (isset($data['user_email']) && $data['user_email'] !== $current_user->user_email) { error_log('[DEBUG UPDATE_ACCOUNT] Email change detected: ' . $current_user->user_email . ' -> ' . $data['user_email']); $needs_current_password_check = true; } // Check current password if needed if ($needs_current_password_check) { error_log('[DEBUG UPDATE_ACCOUNT] Current password check required.'); if (empty($data['current_password'])) { error_log('[DEBUG UPDATE_ACCOUNT] Error: Current password missing for email/password update.'); $error = new WP_Error('missing_current_password', 'Current password is required to update email or password.'); error_log('[DEBUG UPDATE_ACCOUNT] Returning WP_Error: ' . $error->get_error_code()); return $error; } error_log('[DEBUG UPDATE_ACCOUNT] Checking current password...'); if (!wp_check_password($data['current_password'], $current_user->user_pass, $user_id)) { error_log('[DEBUG UPDATE_ACCOUNT] Error: Current password check failed for user ID: ' . $user_id); $error = new WP_Error('incorrect_current_password', 'The current password you entered is incorrect.'); error_log('[DEBUG UPDATE_ACCOUNT] Returning WP_Error: ' . $error->get_error_code()); return $error; } error_log('[DEBUG UPDATE_ACCOUNT] Current password check successful.'); } // Update the user core data error_log('[DEBUG UPDATE_ACCOUNT] Calling wp_update_user with args: ' . print_r($update_args, true)); $result = wp_update_user($update_args); if (is_wp_error($result)) { error_log('[DEBUG UPDATE_ACCOUNT] wp_update_user failed: ' . $result->get_error_message()); $error = new WP_Error('user_update_failed', 'Could not update user information: ' . $result->get_error_message()); error_log('[DEBUG UPDATE_ACCOUNT] Returning WP_Error: ' . $error->get_error_code()); return $error; // Return the modified error object } error_log('[DEBUG UPDATE_ACCOUNT] wp_update_user successful for user ID: ' . $user_id); // Update user meta data $meta_to_update = [ 'user_linkedin' => sanitize_text_field($data['user_linkedin'] ?? ''), 'personal_accreditation' => sanitize_text_field($data['personal_accreditation'] ?? ''), 'business_name' => sanitize_text_field($data['business_name'] ?? ''), 'business_phone' => sanitize_text_field($data['business_phone'] ?? ''), 'business_email' => sanitize_email($data['business_email'] ?? ''), 'business_website' => esc_url_raw($data['business_website'] ?? ''), 'business_description' => sanitize_textarea_field($data['business_description'] ?? ''), 'user_country' => sanitize_text_field($data['user_country'] ?? ''), 'user_state' => sanitize_text_field($data['user_state'] ?? ''), // Includes 'Other' or selected state 'user_state_other' => ($data['user_state'] ?? '') === 'Other' ? sanitize_text_field($data['user_state_other'] ?? '') : '', // Only save if 'Other' is selected 'user_city' => sanitize_text_field($data['user_city'] ?? ''), 'user_zip' => sanitize_text_field($data['user_zip'] ?? ''), 'business_type' => sanitize_text_field($data['business_type'] ?? ''), 'training_audience' => $data['training_audience'] ?? [], // Already validated as array or empty 'training_formats' => $data['training_formats'] ?? [], 'training_locations' => $data['training_locations'] ?? [], 'training_resources' => $data['training_resources'] ?? [], ]; error_log('[DEBUG UPDATE_ACCOUNT] Updating user meta: ' . print_r($meta_to_update, true)); foreach ($meta_to_update as $key => $value) { update_user_meta($user_id, $key, $value); } // Handle profile image upload/deletion if ($delete_image) { $current_image_id = get_user_meta($user_id, 'profile_image_id', true); error_log('[DEBUG UPDATE_ACCOUNT] Deleting profile image. Current ID: ' . $current_image_id); if ($current_image_id) { wp_delete_attachment($current_image_id, true); delete_user_meta($user_id, 'profile_image_id'); } } elseif (!empty($image_data['tmp_name'])) { error_log('[DEBUG UPDATE_ACCOUNT] Processing profile image upload: ' . print_r($image_data, true)); // Need wp-admin/includes/file.php, wp-admin/includes/image.php, wp-admin/includes/media.php require_once(ABSPATH . 'wp-admin/includes/file.php'); require_once(ABSPATH . 'wp-admin/includes/image.php'); require_once(ABSPATH . 'wp-admin/includes/media.php'); // Handle the upload // Note: media_handle_upload() expects the file input name ('profile_image' in this case) $attachment_id = media_handle_upload('profile_image', 0); // 0 means no parent post if (is_wp_error($attachment_id)) { error_log('[DEBUG UPDATE_ACCOUNT] Profile image upload failed: ' . $attachment_id->get_error_message()); // Optionally return error, or just log it and continue // return new WP_Error('image_upload_failed', 'Could not upload profile image: ' . $attachment_id->get_error_message()); } else { error_log('[DEBUG UPDATE_ACCOUNT] Profile image uploaded successfully. Attachment ID: ' . $attachment_id); // Delete old image if exists $current_image_id = get_user_meta($user_id, 'profile_image_id', true); error_log('[DEBUG UPDATE_ACCOUNT] Deleting old profile image. Current ID: ' . $current_image_id); if ($current_image_id && $current_image_id != $attachment_id) { wp_delete_attachment($current_image_id, true); } // Store new attachment ID update_user_meta($user_id, 'profile_image_id', $attachment_id); } } // TODO: Implement logic to update linked TEC Organizer/Venue posts if necessary // This might involve fetching the linked venue ID and updating its details based on profile changes. error_log('[DEBUG UPDATE_ACCOUNT] Update process completed successfully for user ID: ' . $user_id . '. Returning true.'); return true; } /** * Returns a list of countries. * Could be expanded or loaded from a config/API. * * @return array */ private function get_country_list() { return array( 'US' => 'United States', 'CA' => 'Canada', 'GB' => 'United Kingdom', 'AU' => 'Australia', // Add more countries as needed ); } /** * Returns a list of US states. * * @return array */ private function get_us_states() { return array( 'AL' => 'Alabama', 'AK' => 'Alaska', 'AZ' => 'Arizona', 'AR' => 'Arkansas', 'CA' => 'California', 'CO' => 'Colorado', 'CT' => 'Connecticut', 'DE' => 'Delaware', 'DC' => 'District Of Columbia', 'FL' => 'Florida', 'GA' => 'Georgia', 'HI' => 'Hawaii', 'ID' => 'Idaho', 'IL' => 'Illinois', 'IN' => 'Indiana', 'IA' => 'Iowa', 'KS' => 'Kansas', 'KY' => 'Kentucky', 'LA' => 'Louisiana', 'ME' => 'Maine', 'MD' => 'Maryland', 'MA' => 'Massachusetts', 'MI' => 'Michigan', 'MN' => 'Minnesota', 'MS' => 'Mississippi', 'MO' => 'Missouri', 'MT' => 'Montana', 'NE' => 'Nebraska', 'NV' => 'Nevada', 'NH' => 'New Hampshire', 'NJ' => 'New Jersey', 'NM' => 'New Mexico', 'NY' => 'New York', 'NC' => 'North Carolina', 'ND' => 'North Dakota', 'OH' => 'Ohio', 'OK' => 'Oklahoma', 'OR' => 'Oregon', 'PA' => 'Pennsylvania', 'RI' => 'Rhode Island', 'SC' => 'South Carolina', 'SD' => 'South Dakota', 'TN' => 'Tennessee', 'TX' => 'Texas', 'UT' => 'Utah', 'VT' => 'Vermont', 'VA' => 'Virginia', 'WA' => 'Washington', 'WV' => 'West Virginia', 'WI' => 'Wisconsin', 'WY' => 'Wyoming' ); } /** * Returns a list of Canadian provinces. * * @return array */ private function get_canadian_provinces() { return array( 'AB' => 'Alberta', 'BC' => 'British Columbia', 'MB' => 'Manitoba', 'NB' => 'New Brunswick', 'NL' => 'Newfoundland and Labrador', 'NS' => 'Nova Scotia', 'ON' => 'Ontario', 'PE' => 'Prince Edward Island', 'QC' => 'Quebec', 'SK' => 'Saskatchewan', 'NT' => 'Northwest Territories', 'NU' => 'Nunavut', 'YT' => 'Yukon' ); } } // End class HVAC_Profile