From 20a5be3e6510e5be69b64fa10f85a1bfb75b9ed0 Mon Sep 17 00:00:00 2001 From: bengizmo Date: Tue, 20 May 2025 09:47:22 -0300 Subject: [PATCH] feat: Add trainer profile editing functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add profile edit form template - Implement profile update handler with validation - Add profile update shortcode registration - Create E2E test for profile editing - Support updating personal & business info, location info and training preferences - Add password change functionality with validation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../tests/e2e/trainer-profile-edit.spec.ts | 162 ++++ .../includes/class-hvac-community-events.php | 11 + .../includes/class-hvac-registration.php | 341 ++++++++ .../templates/template-edit-profile.php | 727 ++++++++++++++++++ 4 files changed, 1241 insertions(+) create mode 100644 wordpress-dev/tests/e2e/trainer-profile-edit.spec.ts create mode 100644 wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/template-edit-profile.php diff --git a/wordpress-dev/tests/e2e/trainer-profile-edit.spec.ts b/wordpress-dev/tests/e2e/trainer-profile-edit.spec.ts new file mode 100644 index 00000000..756f65b5 --- /dev/null +++ b/wordpress-dev/tests/e2e/trainer-profile-edit.spec.ts @@ -0,0 +1,162 @@ +/** + * @fileoverview E2E Tests for trainer profile editing functionality. + * This test suite verifies that trainers can view, edit, and update their profiles. + */ + +import { test, expect } from '@playwright/test'; +import { LoginPage } from './pages/LoginPage'; +import { DashboardPage } from './pages/DashboardPage'; + +// Test data +const TEST_USER = { + username: process.env.TEST_TRAINER_USERNAME || 'test_trainer', + password: process.env.TEST_TRAINER_PASSWORD || 'Test_password123' +}; + +const UPDATED_INFO = { + firstName: 'Updated', + lastName: 'Trainer', + displayName: 'Updated Trainer Profile', + bio: 'This is an updated bio from the automated test.', + businessName: 'Updated Training Co.', + businessPhone: '555-123-4567', + businessEmail: 'updated@example.com', + businessDesc: 'This is an updated business description from the automated test.' +}; + +test.describe('Trainer Profile Editing @trainer-profile', () => { + test.beforeEach(async ({ page }) => { + // Login before each test + const loginPage = new LoginPage(page); + await loginPage.goto(); + await loginPage.login(TEST_USER.username, TEST_USER.password); + + // Verify we're logged in by checking if we're on the dashboard + const dashboardPage = new DashboardPage(page); + await expect(page).toHaveURL(/.*hvac-dashboard.*/); + await expect(dashboardPage.welcomeMessage).toBeVisible(); + }); + + test('should navigate to edit profile page', async ({ page }) => { + // Navigate to trainer profile page first + await page.goto('/trainer-profile/'); + await expect(page).toHaveURL(/.*trainer-profile.*/); + await expect(page.locator('h1')).toContainText('Trainer Profile'); + + // Click the edit profile button + await page.click('text=Edit Profile'); + + // Verify we're on the edit profile page + await expect(page).toHaveURL(/.*edit-profile.*/); + await expect(page.locator('h1')).toContainText('Edit Trainer Profile'); + + // Verify form is loaded with user's current info + await expect(page.locator('#first_name')).toBeVisible(); + await expect(page.locator('#last_name')).toBeVisible(); + await expect(page.locator('#display_name')).toBeVisible(); + }); + + test('should update basic profile information', async ({ page }) => { + // Go directly to edit profile page + await page.goto('/edit-profile/'); + + // Clear and update form fields + await page.locator('#first_name').fill(UPDATED_INFO.firstName); + await page.locator('#last_name').fill(UPDATED_INFO.lastName); + await page.locator('#display_name').fill(UPDATED_INFO.displayName); + await page.locator('#description').fill(UPDATED_INFO.bio); + + // Submit the form + await page.click('button[name="hvac_update_profile"]'); + + // Verify success message + await expect(page.locator('.hvac-message-success')).toBeVisible(); + await expect(page.locator('.hvac-message-success')).toContainText('Your profile has been updated successfully'); + + // Navigate to profile page to verify changes + await page.goto('/trainer-profile/'); + + // Verify updated information is displayed + await expect(page.locator('.trainer-name')).toContainText(UPDATED_INFO.displayName); + await expect(page.locator('.trainer-bio')).toContainText(UPDATED_INFO.bio); + }); + + test('should update business information', async ({ page }) => { + // Go to edit profile page + await page.goto('/edit-profile/'); + + // Update business information + await page.locator('#business_name').fill(UPDATED_INFO.businessName); + await page.locator('#business_phone').fill(UPDATED_INFO.businessPhone); + await page.locator('#business_email').fill(UPDATED_INFO.businessEmail); + await page.locator('#business_description').fill(UPDATED_INFO.businessDesc); + + // Submit the form + await page.click('button[name="hvac_update_profile"]'); + + // Verify success message + await expect(page.locator('.hvac-message-success')).toBeVisible(); + + // Navigate to profile page to verify changes + await page.goto('/trainer-profile/'); + + // Verify updated business information is displayed + await expect(page.locator('.business-name')).toContainText(UPDATED_INFO.businessName); + await expect(page.locator('.business-info')).toContainText(UPDATED_INFO.businessPhone); + await expect(page.locator('.business-info')).toContainText(UPDATED_INFO.businessEmail); + await expect(page.locator('.business-description')).toContainText(UPDATED_INFO.businessDesc); + }); + + test('should display appropriate validation errors', async ({ page }) => { + // Go to edit profile page + await page.goto('/edit-profile/'); + + // Clear required fields + await page.locator('#first_name').fill(''); + await page.locator('#last_name').fill(''); + await page.locator('#user_email').fill(''); + + // Submit the form + await page.click('button[name="hvac_update_profile"]'); + + // Verify validation errors + await expect(page.locator('#first_name_error')).toBeVisible(); + await expect(page.locator('#last_name_error')).toBeVisible(); + await expect(page.locator('#user_email_error')).toBeVisible(); + + // Invalid email format + await page.locator('#user_email').fill('not-an-email'); + await page.click('button[name="hvac_update_profile"]'); + await expect(page.locator('#user_email_error')).toBeVisible(); + await expect(page.locator('#user_email_error')).toContainText('valid email'); + }); + + test('should validate password change fields', async ({ page }) => { + // Go to edit profile page + await page.goto('/edit-profile/'); + + // Enter incorrect current password + await page.locator('#current_password').fill('wrong-password'); + await page.locator('#new_password').fill('NewPassword123'); + await page.locator('#confirm_new_password').fill('NewPassword123'); + + // Submit the form + await page.click('button[name="hvac_update_profile"]'); + + // Verify error message for incorrect current password + await expect(page.locator('#current_password_error')).toBeVisible(); + await expect(page.locator('#current_password_error')).toContainText('incorrect'); + + // Test password mismatch + await page.locator('#current_password').fill(TEST_USER.password); + await page.locator('#new_password').fill('NewPassword123'); + await page.locator('#confirm_new_password').fill('DifferentPassword123'); + + // Submit the form + await page.click('button[name="hvac_update_profile"]'); + + // Verify error message for password mismatch + await expect(page.locator('#confirm_new_password_error')).toBeVisible(); + await expect(page.locator('#confirm_new_password_error')).toContainText('do not match'); + }); +}); \ No newline at end of file diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-community-events.php b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-community-events.php index fc160017..165803f6 100644 --- a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-community-events.php +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-community-events.php @@ -231,6 +231,9 @@ class HVAC_Community_Events { // Add trainer profile shortcode add_shortcode('hvac_trainer_profile', array($this, 'render_trainer_profile')); + + // Add edit profile shortcode + add_shortcode('hvac_edit_profile', array('HVAC_Registration', 'render_edit_profile_form')); // Remove the event form shortcode as we're using TEC's shortcode instead // add_shortcode('hvac_event_form', array('HVAC_Community_Event_Handler', 'render_event_form')); @@ -343,6 +346,14 @@ class HVAC_Community_Events { return $custom_template; } } + + // Check for edit-profile page + if (is_page('edit-profile')) { + $custom_template = HVAC_CE_PLUGIN_DIR . 'templates/template-edit-profile.php'; + if (file_exists($custom_template)) { + return $custom_template; + } + } // Check for single event view (temporary) if ( is_singular( 'tribe_events' ) ) { diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-registration.php b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-registration.php index c0b90722..f6f94317 100644 --- a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-registration.php +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-registration.php @@ -10,7 +10,9 @@ if (!defined('ABSPATH')) { class HVAC_Registration { const TRANSIENT_PREFIX = 'hvac_reg_'; // Prefix for transients + const PROFILE_TRANSIENT_PREFIX = 'hvac_prof_'; // Prefix for profile transients const REGISTRATION_ACTION = 'hvac_register'; // Action name for admin-post + const PROFILE_UPDATE_ACTION = 'hvac_update_profile'; // Action name for profile update /** * Constructor @@ -18,6 +20,9 @@ class HVAC_Registration { public function __construct() { // Register shortcode for registration form add_shortcode('hvac_trainer_registration', array($this, 'render_registration_form')); + + // Register shortcode for edit profile form + add_shortcode('hvac_edit_profile', array($this, 'render_edit_profile_form')); // Enqueue styles and scripts add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts')); @@ -25,6 +30,9 @@ class HVAC_Registration { // Hook the processing function to admin-post actions add_action('admin_post_nopriv_' . self::REGISTRATION_ACTION, array($this, 'process_registration_submission')); add_action('admin_post_' . self::REGISTRATION_ACTION, array($this, 'process_registration_submission')); // Allow logged-in users? Maybe not needed but harmless. + + // Hook the profile update function to admin-post actions (only for logged in users) + add_action('admin_post_' . self::PROFILE_UPDATE_ACTION, array($this, 'process_profile_update')); } /** @@ -1016,6 +1024,339 @@ class HVAC_Registration { // TODO: Add send_user_approved_notification() // TODO: Add send_user_denied_notification() + /** + * Renders the edit profile form shortcode output + */ + public function render_edit_profile_form() { + if (!is_user_logged_in()) { + return '

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) diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/template-edit-profile.php b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/template-edit-profile.php new file mode 100644 index 00000000..29a09bda --- /dev/null +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/template-edit-profile.php @@ -0,0 +1,727 @@ + get_user_meta($user_id, 'personal_accreditation', true), + 'business_name' => get_user_meta($user_id, 'business_name', true), + 'business_phone' => get_user_meta($user_id, 'business_phone', true), + 'business_email' => get_user_meta($user_id, 'business_email', true), + 'business_website' => get_user_meta($user_id, 'business_website', true), + 'business_description' => get_user_meta($user_id, 'business_description', true), + 'user_country' => get_user_meta($user_id, 'user_country', true), + 'user_state' => get_user_meta($user_id, 'user_state', true), + 'user_city' => get_user_meta($user_id, 'user_city', true), + 'user_zip' => get_user_meta($user_id, 'user_zip', true), + 'user_linkedin' => get_user_meta($user_id, 'user_linkedin', true), + 'business_type' => get_user_meta($user_id, 'business_type', true), + 'training_audience' => get_user_meta($user_id, 'training_audience', true), + 'training_formats' => get_user_meta($user_id, 'training_formats', true), + 'training_locations' => get_user_meta($user_id, 'training_locations', true), + 'training_resources' => get_user_meta($user_id, 'training_resources', true), + 'annual_revenue_target' => get_user_meta($user_id, 'annual_revenue_target', true), +); + +// Get profile image +$profile_image_id = get_user_meta($user_id, 'profile_image_id', true); +$profile_image_url = ''; +if ($profile_image_id) { + $profile_image_url = wp_get_attachment_url($profile_image_id); +} + +// Get messages (success, error) from query parameters if present +$message = ''; +$message_type = ''; +if (isset($_GET['updated']) && $_GET['updated'] === '1') { + $message = 'Your profile has been updated successfully.'; + $message_type = 'success'; +} elseif (isset($_GET['updated']) && $_GET['updated'] === '0') { + $message = 'There was an error updating your profile. Please try again.'; + $message_type = 'error'; +} + +// Check if we have form errors from a previous submission via transient +$errors = []; +$transient_key = null; + +if (isset($_GET['prof_error']) && $_GET['prof_error'] === '1' && isset($_GET['tid'])) { + $transient_key = 'hvac_prof_' . sanitize_key($_GET['tid']); + $transient_data = get_transient($transient_key); + + if ($transient_data && is_array($transient_data)) { + $errors = $transient_data['errors'] ?? []; + // Delete the transient immediately after retrieving + delete_transient($transient_key); + } +} + +// Define country, state, and province options +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' + ); +} + +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' + ); +} + +function get_country_list() { + return array( + 'US' => 'United States', + 'CA' => 'Canada', + 'GB' => 'United Kingdom', + 'AU' => 'Australia', + 'NZ' => 'New Zealand', + 'DE' => 'Germany', + 'FR' => 'France', + 'IT' => 'Italy', + 'ES' => 'Spain', + 'JP' => 'Japan', + 'CN' => 'China', + 'IN' => 'India', + 'BR' => 'Brazil', + 'MX' => 'Mexico', + // Add more countries as needed + ); +} + +// --- Template Start --- + +get_header(); // Use theme's header +?> + +
+
+ + +
+

Edit Trainer Profile

+ +
+ + +
+

+
+ + +
+
+ + + + +
+

Personal Information

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

'; ?> +
+
+ + + ' . esc_html($errors['last_name']) . '

'; ?> +
+
+
+ + + This will be the name displayed to other users on the site. + ' . esc_html($errors['display_name']) . '

'; ?> +
+
+
+ + + ' . esc_html($errors['user_email']) . '

'; ?> +
+
+ + + ' . esc_html($errors['user_url']) . '

'; ?> +
+
+
+ + + ' . esc_html($errors['user_linkedin']) . '

'; ?> +
+
+ + + Enter your abbreviated accreditations separated by commas. +
+
+ + + A short bio about yourself. This will be displayed on your profile page. + ' . esc_html($errors['description']) . '

'; ?> +
+
+ +
+ + <?php echo esc_attr($user->display_name); ?> + +
+ first_name, 0, 1) . substr($user->last_name, 0, 1)); ?> +
+ +
+
+ + To update your profile image, select a new .jpg, .png, or .gif file. Leave empty to keep your current image. + ' . esc_html($errors['profile_image']) . '

'; ?> +
+
+
+ + +
+

Business Information

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

'; ?> +
+
+
+ + + ' . esc_html($errors['business_phone']) . '

'; ?> +
+
+ + + ' . esc_html($errors['business_email']) . '

'; ?> +
+
+
+ + + ' . esc_html($errors['business_website']) . '

'; ?> +
+
+ + + ' . esc_html($errors['business_description']) . '

'; ?> +
+
+ + +
+

Address Information

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

'; ?> +
+ +
+
+ + + + ' . esc_html($errors['user_state']) . '

'; ?> + ' . esc_html($errors['user_state_other']) . '

'; ?> +
+
+ + + ' . esc_html($errors['user_city']) . '

'; ?> +
+
+ +
+ + + ' . esc_html($errors['user_zip']) . '

'; ?> +
+
+ + +
+

Training Information

+
+ + What type of business are you? +
+ ' . esc_html($type) . ''; + } + ?> +
+ ' . esc_html($errors['business_type']) . '

'; ?> +
+ +
+ + Who do you offer training to? (Select all that apply) +
+ "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 = $user_meta['training_audience']; + if (!is_array($selected_audience)) $selected_audience = []; // Ensure it's an array + foreach ($audience_options as $value => $label) { + echo ''; + } + ?> +
+ ' . esc_html($errors['training_audience']) . '

'; ?> +
+ +
+ + What formats of training do you offer? +
+ ' . esc_html($format) . ''; + } + ?> +
+ ' . esc_html($errors['training_formats']) . '

'; ?> +
+ +
+ + Where are you willing to provide training? (Select all that apply) +
+ ' . esc_html($location) . ''; + } + ?> +
+ ' . esc_html($errors['training_locations']) . '

'; ?> +
+ +
+ + What training resources do you have access to? (Select all that apply) +
+ ' . esc_html($resource) . ''; + } + ?> +
+ ' . esc_html($errors['training_resources']) . '

'; ?> +
+
+ + +
+

Revenue Information

+
+ + It's our goal to help you generate revenue through your training. How much revenue are you looking to generate annually though your training on Upskill HVAC? + +
+
+ + +
+

Change Password (optional)

+

Leave these fields blank if you do not wish to change your password.

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

'; ?> +
+
+ + + Password must be at least 8 characters long, and include at least one uppercase letter, one lowercase letter, and one number. + ' . esc_html($errors['new_password']) . '

'; ?> +
+
+
+ + + ' . esc_html($errors['confirm_new_password']) . '

'; ?> +
+
+ +
+ +
+
+
+ +
+
+ + + + + +