'; if (!empty($errors['transient'])) echo '
' . esc_html($errors['transient']) . '
'; // Nonce errors should ideally be caught in admin-post, but display if somehow set if (!empty($errors['nonce'])) echo '' . esc_html($errors['nonce']) . '
'; error_log('[HVAC REG DEBUG] render_registration_form: Errors before display_form_html: ' . print_r($errors, true)); if (!empty($errors['account'])) echo '' . esc_html($errors['account']) . '
'; echo ''; } // Display the form HTML, passing retrieved errors and submitted data // No success message here anymore, success leads to redirect $this->display_form_html($submitted_data, $errors); return ob_get_clean(); // --- End Render Form --- } /** * Processes the registration form submission via admin-post. * Handles validation, user creation, notifications, and redirects. */ public function process_registration_submission() { error_log('[HVAC REG DEBUG] process_registration_submission fired.'); $errors = []; $submitted_data = $_POST; // Capture submitted data early for potential repopulation $registration_page_url = home_url('/trainer-registration/'); // Adjust if slug changes // --- Verify Nonce --- if (!isset($_POST['hvac_registration_nonce']) || !wp_verify_nonce($_POST['hvac_registration_nonce'], 'hvac_trainer_registration')) { $errors['nonce'] = 'Security check failed. Please try submitting the form again.'; error_log('[HVAC REG DEBUG] Nonce check failed in admin-post.'); $this->redirect_with_errors($errors, $submitted_data, $registration_page_url); // No need for return/exit here, redirect_with_errors exits. } error_log('[HVAC REG DEBUG] Nonce check passed in admin-post.'); // --- File Upload Handling --- $profile_image_data = null; if (isset($_FILES['profile_image']) && $_FILES['profile_image']['error'] !== UPLOAD_ERR_NO_FILE) { if ($_FILES['profile_image']['error'] === UPLOAD_ERR_OK) { // Check if it's actually an uploaded file if (!is_uploaded_file($_FILES['profile_image']['tmp_name'])) { $errors['profile_image'] = 'File upload error (invalid temp file).'; error_log('[HVAC REG DEBUG] Profile image upload error: Not an uploaded file.'); } else { $allowed_types = ['image/jpeg', 'image/png', 'image/gif']; // Use wp_check_filetype on the actual file name for extension check // Use finfo_file or getimagesize on tmp_name for actual MIME type check for better security $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime_type = finfo_file($finfo, $_FILES['profile_image']['tmp_name']); finfo_close($finfo); if (!in_array($mime_type, $allowed_types)) { $errors['profile_image'] = 'Invalid file type detected (' . esc_html($mime_type) . '). Please upload a JPG, PNG, or GIF.'; error_log('[HVAC REG DEBUG] Profile image upload error: Invalid MIME type - ' . $mime_type); } else { $profile_image_data = $_FILES['profile_image']; // Store the whole $_FILES entry error_log('[HVAC REG DEBUG] Profile image seems valid.'); } } error_log('[HVAC REG DEBUG] process_registration_submission: Errors after validation merge: ' . print_r($errors, true)); } else { $errors['profile_image'] = 'There was an error uploading the profile image. Code: ' . $_FILES['profile_image']['error']; error_log('[HVAC REG DEBUG] Profile image upload error code: ' . $_FILES['profile_image']['error']); error_log('[HVAC REG DEBUG] process_registration_submission: Checking if errors is empty. Result: ' . (empty($errors) ? 'Yes' : 'No')); } } // --- End File Upload Handling --- // Validate the rest of the form data $validation_errors = $this->validate_registration($submitted_data); $errors = array_merge($errors, $validation_errors); // Combine file errors and validation errors // --- Process if No Errors --- if (empty($errors)) { error_log('[HVAC REG DEBUG] Validation passed in admin-post. Attempting account creation...'); $user_id = $this->create_trainer_account($submitted_data, $profile_image_data); if (is_wp_error($user_id)) { $errors['account'] = $user_id->get_error_message(); error_log('[HVAC REG DEBUG] Account creation WP_Error in admin-post: ' . $user_id->get_error_message()); $this->redirect_with_errors($errors, $submitted_data, $registration_page_url); // No need for return/exit here } elseif ($user_id) { error_log('[HVAC REG DEBUG] Account creation SUCCESS in admin-post. User ID: ' . $user_id); error_log('[HVAC REG DEBUG] Sending admin notification...'); $this->send_admin_notification($user_id, $submitted_data); // $this->send_user_pending_notification($user_id); // TODO // --- Success Redirect --- $success_redirect_url = home_url('/registration-pending/'); // URL from E2E test error_log('[HVAC REG DEBUG] Redirecting to success page: ' . $success_redirect_url); wp_safe_redirect($success_redirect_url); exit; // Important after redirect } else { // This case should ideally not happen if wp_insert_user works correctly $errors['account'] = 'An unknown error occurred during registration. Please contact support.'; error_log('[HVAC REG DEBUG] Account creation failed silently in admin-post (returned false/0).'); $this->redirect_with_errors($errors, $submitted_data, $registration_page_url); // No need for return/exit here } } else { error_log('[HVAC REG DEBUG] Validation errors found in admin-post: ' . print_r($errors, true)); $this->redirect_with_errors($errors, $submitted_data, $registration_page_url); // No need for return/exit here } } /** * Helper function to store errors/data in transient and redirect back to the form page. error_log('[HVAC REG DEBUG] redirect_with_errors: Preparing transient. Key: ' . $transient_key . ' Data: ' . print_r($transient_data, true)); * * @param array $errors Array of error messages. * @param array $data Submitted form data. * @param string $redirect_url The URL to redirect back to. */ private function redirect_with_errors($errors, $data, $redirect_url) { $transient_id = uniqid(); // Generate unique ID for transient key $transient_key = self::TRANSIENT_PREFIX . $transient_id; $transient_data = [ 'errors' => $errors, 'data' => $data, // Store submitted data to repopulate form ]; // Store for 5 minutes set_transient($transient_key, $transient_data, MINUTE_IN_SECONDS * 5); // Add query arguments to the redirect URL $redirect_url = add_query_arg([ 'reg_error' => '1', 'tid' => $transient_id, ], $redirect_url); error_log('[HVAC REG DEBUG] Redirecting back with errors. URL: ' . $redirect_url . ' Transient Key: ' . $transient_key); wp_safe_redirect($redirect_url); exit; // Stop execution after redirect } /** * Displays the actual form HTML. * Receives submitted data and errors as arguments (potentially retrieved from transient). */ private function display_form_html($data = [], $errors = []) { // Ensure $data and $errors are arrays, even if transient failed $data = is_array($data) ? $data : []; $errors = is_array($errors) ? $errors : []; ?>By submitting this form, you will be creating an account in the Upskill HVAC online event system. Once approved, you will be able to login to the trainer portal to manage your profile and event listings.
Please log in to edit your profile.
'; } // We don't need to do anything here since the template file already handles the form rendering // Just set up the shortcode to point to the template ob_start(); include HVAC_CE_PLUGIN_DIR . 'templates/template-edit-profile.php'; return ob_get_clean(); } /** * Process profile update submission from the edit profile form */ public function process_profile_update() { // Only logged-in users can update profiles if (!is_user_logged_in()) { wp_redirect(home_url('/community-login/')); exit; } $user_id = get_current_user_id(); $user = get_userdata($user_id); $errors = []; $submitted_data = $_POST; $profile_page_url = home_url('/edit-profile/'); // Verify nonce if (!isset($_POST['hvac_profile_nonce']) || !wp_verify_nonce($_POST['hvac_profile_nonce'], 'hvac_update_profile')) { $errors['nonce'] = 'Security check failed. Please try submitting the form again.'; error_log('[HVAC PROFILE DEBUG] Nonce check failed in profile update.'); $this->redirect_with_profile_errors($errors, $profile_page_url); // No need for return/exit here, redirect_with_errors exits. } error_log('[HVAC PROFILE DEBUG] Nonce check passed in profile update.'); // File Upload Handling $profile_image_data = null; if (isset($_FILES['profile_image']) && $_FILES['profile_image']['error'] !== UPLOAD_ERR_NO_FILE) { if ($_FILES['profile_image']['error'] === UPLOAD_ERR_OK) { // Check if it's actually an uploaded file if (!is_uploaded_file($_FILES['profile_image']['tmp_name'])) { $errors['profile_image'] = 'File upload error (invalid temp file).'; error_log('[HVAC PROFILE DEBUG] Profile image upload error: Not an uploaded file.'); } else { $allowed_types = ['image/jpeg', 'image/png', 'image/gif']; $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime_type = finfo_file($finfo, $_FILES['profile_image']['tmp_name']); finfo_close($finfo); if (!in_array($mime_type, $allowed_types)) { $errors['profile_image'] = 'Invalid file type detected (' . esc_html($mime_type) . '). Please upload a JPG, PNG, or GIF.'; error_log('[HVAC PROFILE DEBUG] Profile image upload error: Invalid MIME type - ' . $mime_type); } else { $profile_image_data = $_FILES['profile_image']; // Store the whole $_FILES entry error_log('[HVAC PROFILE DEBUG] Profile image seems valid.'); } } } else { $errors['profile_image'] = 'There was an error uploading the profile image. Code: ' . $_FILES['profile_image']['error']; error_log('[HVAC PROFILE DEBUG] Profile image upload error code: ' . $_FILES['profile_image']['error']); } } // Validate form data $validation_errors = $this->validate_profile_update($submitted_data, $user); $errors = array_merge($errors, $validation_errors); // Process if no errors if (empty($errors)) { error_log('[HVAC PROFILE DEBUG] Validation passed in profile update.'); $update_success = $this->update_user_profile($user_id, $submitted_data, $profile_image_data); if (is_wp_error($update_success)) { $errors['account'] = $update_success->get_error_message(); error_log('[HVAC PROFILE DEBUG] Profile update WP_Error: ' . $update_success->get_error_message()); $this->redirect_with_profile_errors($errors, $profile_page_url); } elseif ($update_success) { error_log('[HVAC PROFILE DEBUG] Profile update SUCCESS for user ID: ' . $user_id); // Redirect to the profile page with success message wp_safe_redirect(add_query_arg('updated', '1', $profile_page_url)); exit; } else { $errors['account'] = 'An unknown error occurred during profile update. Please try again.'; error_log('[HVAC PROFILE DEBUG] Profile update failed silently (returned false).'); $this->redirect_with_profile_errors($errors, $profile_page_url); } } else { error_log('[HVAC PROFILE DEBUG] Validation errors found in profile update: ' . print_r($errors, true)); $this->redirect_with_profile_errors($errors, $profile_page_url); } } /** * Helper function to store profile errors in transient and redirect back to the form page. * * @param array $errors Array of error messages. * @param string $redirect_url The URL to redirect back to. */ private function redirect_with_profile_errors($errors, $redirect_url) { $transient_id = uniqid(); // Generate unique ID for transient key $transient_key = self::PROFILE_TRANSIENT_PREFIX . $transient_id; $transient_data = [ 'errors' => $errors, ]; // Store for 5 minutes set_transient($transient_key, $transient_data, MINUTE_IN_SECONDS * 5); // Add query arguments to the redirect URL $redirect_url = add_query_arg([ 'prof_error' => '1', 'tid' => $transient_id, ], $redirect_url); error_log('[HVAC PROFILE DEBUG] Redirecting back with errors. URL: ' . $redirect_url . ' Transient Key: ' . $transient_key); wp_safe_redirect($redirect_url); exit; // Stop execution after redirect } /** * Validate profile update data * * @param array $data Submitted form data ($_POST). * @param WP_User $user Current user object. * @return array Array of errors, empty if valid. */ public function validate_profile_update($data, $user) { error_log('[HVAC PROFILE DEBUG] validate_profile_update: Validating data'); $errors = array(); // Required field validation $required_fields = [ 'first_name' => 'First Name', 'last_name' => 'Last Name', 'display_name' => 'Display Name', 'user_email' => 'Email', 'description' => 'Biographical Info', 'business_name' => 'Business Name', 'business_phone' => 'Business Phone', 'business_email' => 'Business Email', 'business_description' => 'Business Description', 'user_country' => 'Country', 'user_state' => 'State/Province', 'user_city' => 'City', 'user_zip' => 'Zip/Postal Code', 'business_type' => 'Business Type', ]; foreach ($required_fields as $field => $label) { // Use trim to catch spaces-only input if (empty($data[$field]) || trim($data[$field]) === '') { $errors[$field] = $label . ' is required.'; } } // Required checkbox groups $required_checkboxes = [ 'training_audience' => 'Training Audience', 'training_formats' => 'Training Formats', 'training_locations' => 'Training Locations', 'training_resources' => 'Training Resources', ]; foreach ($required_checkboxes as $field => $label) { // Check if the key exists and is a non-empty array if (empty($data[$field]) || !is_array($data[$field])) { $errors[$field] = 'Please select at least one option for ' . $label . '.'; } } // Email validation if (!empty($data['user_email']) && !is_email($data['user_email'])) { $errors['user_email'] = 'Please enter a valid email address.'; } if (!empty($data['business_email']) && !is_email($data['business_email'])) { $errors['business_email'] = 'Please enter a valid business email address.'; } // Email exists validation (only if email is changed and valid) if (empty($errors['user_email']) && !empty($data['user_email']) && $data['user_email'] !== $user->user_email && email_exists($data['user_email'])) { $errors['user_email'] = 'This email address is already registered to another account.'; } // URL validation (optional fields) if (!empty($data['user_url']) && !filter_var($data['user_url'], FILTER_VALIDATE_URL)) { $errors['user_url'] = 'Please enter a valid URL for your personal website.'; } if (!empty($data['user_linkedin']) && !filter_var($data['user_linkedin'], FILTER_VALIDATE_URL)) { $errors['user_linkedin'] = 'Please enter a valid URL for your LinkedIn profile.'; } if (!empty($data['business_website']) && !filter_var($data['business_website'], FILTER_VALIDATE_URL)) { $errors['business_website'] = 'Please enter a valid URL for your business website.'; } // State/Province 'Other' validation if (!empty($data['user_country'])) { if ($data['user_country'] !== 'United States' && $data['user_country'] !== 'Canada') { // If country is not US/CA, state *must* be 'Other' if (empty($data['user_state']) || $data['user_state'] !== 'Other') { $errors['user_state'] = 'Please select "Other" for State/Province if your country is not US or Canada.'; } elseif (empty($data['user_state_other']) || trim($data['user_state_other']) === '') { // If state is 'Other', the text input must not be empty $errors['user_state_other'] = 'Please enter your state/province.'; } } elseif (!empty($data['user_state'])) { // If country is US/CA if ($data['user_state'] === 'Other') { // State cannot be 'Other' if country is US/CA $errors['user_state'] = 'Please select your state/province from the list.'; } } } // Password validation (only if user is attempting to change password) if (!empty($data['current_password']) || !empty($data['new_password']) || !empty($data['confirm_new_password'])) { // Verify current password if (empty($data['current_password'])) { $errors['current_password'] = 'Please enter your current password.'; } elseif (!wp_check_password($data['current_password'], $user->user_pass, $user->ID)) { $errors['current_password'] = 'Current password is incorrect.'; } // Validate new password if (empty($data['new_password'])) { $errors['new_password'] = 'Please enter a new password.'; } elseif (strlen($data['new_password']) < 8) { $errors['new_password'] = 'Password must be at least 8 characters long.'; } elseif (!preg_match('/[A-Z]/', $data['new_password'])) { $errors['new_password'] = 'Password must contain at least one uppercase letter.'; } elseif (!preg_match('/[a-z]/', $data['new_password'])) { $errors['new_password'] = 'Password must contain at least one lowercase letter.'; } elseif (!preg_match('/[0-9]/', $data['new_password'])) { $errors['new_password'] = 'Password must contain at least one number.'; } // Confirm new password if (empty($data['confirm_new_password'])) { $errors['confirm_new_password'] = 'Please confirm your new password.'; } elseif ($data['new_password'] !== $data['confirm_new_password']) { $errors['confirm_new_password'] = 'New passwords do not match.'; } } error_log('[HVAC PROFILE DEBUG] validate_profile_update: Validation result - ' . (empty($errors) ? 'VALID' : 'INVALID')); return $errors; } /** * Update the user profile with submitted data * * @param int $user_id The user ID to update. * @param array $data The submitted form data. * @param array|null $profile_image_data The profile image file data, if any. * @return bool|WP_Error True on success, WP_Error on failure. */ private function update_user_profile($user_id, $data, $profile_image_data = null) { // Sanitize and prepare user data for update $userdata = array( 'ID' => $user_id, 'first_name' => sanitize_text_field($data['first_name']), 'last_name' => sanitize_text_field($data['last_name']), 'display_name' => sanitize_text_field($data['display_name']), 'user_email' => sanitize_email($data['user_email']), 'user_url' => !empty($data['user_url']) ? esc_url_raw($data['user_url']) : '', 'description' => wp_kses_post($data['description']), ); // Handle password update if provided if (!empty($data['new_password']) && !empty($data['current_password']) && !empty($data['confirm_new_password'])) { $userdata['user_pass'] = $data['new_password']; // wp_update_user will hash the password } // Update user data $update_result = wp_update_user($userdata); if (is_wp_error($update_result)) { error_log('[HVAC PROFILE DEBUG] wp_update_user failed: ' . $update_result->get_error_message()); return $update_result; } // Update user meta $meta_fields = [ 'user_linkedin' => !empty($data['user_linkedin']) ? esc_url_raw($data['user_linkedin']) : '', 'personal_accreditation' => !empty($data['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' => !empty($data['business_website']) ? esc_url_raw($data['business_website']) : '', 'business_description' => wp_kses_post($data['business_description']), 'user_country' => sanitize_text_field($data['user_country']), 'user_state' => ($data['user_state'] === 'Other' && isset($data['user_state_other'])) ? sanitize_text_field($data['user_state_other']) : sanitize_text_field($data['user_state']), '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' => (!empty($data['training_audience']) && is_array($data['training_audience'])) ? array_map('sanitize_text_field', $data['training_audience']) : [], 'training_formats' => (!empty($data['training_formats']) && is_array($data['training_formats'])) ? array_map('sanitize_text_field', $data['training_formats']) : [], 'training_locations' => (!empty($data['training_locations']) && is_array($data['training_locations'])) ? array_map('sanitize_text_field', $data['training_locations']) : [], 'training_resources' => (!empty($data['training_resources']) && is_array($data['training_resources'])) ? array_map('sanitize_text_field', $data['training_resources']) : [], 'annual_revenue_target' => !empty($data['annual_revenue_target']) ? intval($data['annual_revenue_target']) : '', ]; foreach ($meta_fields as $key => $value) { update_user_meta($user_id, $key, $value); } error_log('[HVAC PROFILE DEBUG] User meta updated for user ID: ' . $user_id); // Handle profile image upload if provided if ($profile_image_data) { error_log('[HVAC PROFILE DEBUG] Attempting profile image upload for user ID: ' . $user_id); // We don't need the return value here unless we want to report specific upload errors $this->handle_profile_image_upload($user_id, $profile_image_data); } // Update organizer profile if it exists $organizer_id = get_user_meta($user_id, 'hvac_organizer_id', true); if ($organizer_id && get_post_type($organizer_id) === Tribe__Events__Main::ORGANIZER_POST_TYPE) { error_log('[HVAC PROFILE DEBUG] Updating organizer profile for user ID: ' . $user_id); $this->create_organizer_profile($user_id, $meta_fields); } // Update venue profile if it exists $venue_id = get_user_meta($user_id, 'hvac_venue_id', true); if ($venue_id && get_post_type($venue_id) === Tribe__Events__Main::VENUE_POST_TYPE) { error_log('[HVAC PROFILE DEBUG] Updating venue profile for user ID: ' . $user_id); $this->create_training_venue($user_id, $meta_fields); } return true; } /** * Get list of countries (simplified) */ private function get_country_list() { // In a real application, use a more comprehensive list or library return array( 'US' => 'United States', 'CA' => 'Canada', // Add more countries as needed 'GB' => 'United Kingdom', 'AU' => 'Australia', // ... ); } /** * Get list of US states */ private function get_us_states() { // Use state abbreviations as keys if preferred by JS/validation 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' ); } /** * Get list of Canadian provinces */ private function get_canadian_provinces() { // Use province abbreviations as keys if preferred by JS/validation 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_Registration