diff --git a/.gitignore b/.gitignore index d170683a..ce7896f5 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,19 @@ !/scripts/ /scripts/* !/scripts/*.sh + +# Plugin files +!hvac-community-events.php +!/includes/ +/includes/* +!/includes/**/*.php +!/templates/ +/templates/* +!/templates/**/*.php +!/assets/ +/assets/* +!/assets/css/*.css +!/assets/js/*.js !/wordpress-dev/tests/ /wordpress-dev/tests/* !/wordpress-dev/tests/e2e/ diff --git a/hvac-community-events.php b/hvac-community-events.php new file mode 100644 index 00000000..6cafcf5d --- /dev/null +++ b/hvac-community-events.php @@ -0,0 +1,1029 @@ +init_secure_download(); + HVAC_Logger::info('Certificate security initialized during activation', 'Activation'); + } + + // Define hierarchical page structure + $parent_pages = [ + 'trainer' => [ + 'title' => 'Trainer', + 'content' => '', + 'children' => [ + 'dashboard' => [ + 'title' => 'Trainer Dashboard', + 'content' => '[hvac_dashboard]', + ], + 'registration' => [ + 'title' => 'Trainer Registration', + 'content' => '[hvac_trainer_registration]', + ], + 'my-profile' => [ + 'title' => 'Trainer Profile', + 'content' => '[hvac_trainer_profile]', + ], + 'email-attendees' => [ + 'title' => 'Email Attendees', + 'content' => '[hvac_email_attendees]', + ], + 'certificate-reports' => [ + 'title' => 'Certificate Reports', + 'content' => '[hvac_certificate_reports]', + ], + 'generate-certificates' => [ + 'title' => 'Generate Certificates', + 'content' => '[hvac_generate_certificates]', + ], + 'documentation' => [ + 'title' => 'Trainer Documentation', + 'content' => '[hvac_documentation]', + ], + 'attendee-profile' => [ + 'title' => 'Attendee Profile', + 'content' => '[hvac_attendee_profile]', + ], + 'communication-templates' => [ + 'title' => 'Communication Templates', + 'content' => '[hvac_communication_templates]', + ], + 'communication-schedules' => [ + 'title' => 'Communication Schedules', + 'content' => '[hvac_communication_schedules]', + ], + 'event' => [ + 'title' => 'Event', + 'content' => '', + 'children' => [ + 'manage' => [ + 'title' => 'Manage Event', + 'content' => ' +
+

Create Event

+
+ Dashboard + Certificate Reports + Generate Certificates +
+
+ +
+

Create and Manage Your HVAC Training Events

+

Use this form to create new training events or edit existing ones. Please fill out all required fields to ensure your event is properly listed and attendees receive accurate information.

+
+

Event Creation Tips:

+ +
+
+
+ + + [tribe_community_events view="submission_form"]', + ], + 'summary' => [ + 'title' => 'Event Summary', + 'content' => '[hvac_event_summary]', + ], + ] + ] + ] + ], + 'master-trainer' => [ + 'title' => 'Master Trainer', + 'content' => '', + 'children' => [ + 'dashboard' => [ + 'title' => 'Master Dashboard', + 'content' => '[hvac_master_dashboard]', + ], + 'certificate-fix' => [ + 'title' => 'Certificate System Diagnostics', + 'content' => '[hvac_certificate_fix]', + ], + 'google-sheets' => [ + 'title' => 'Google Sheets Integration', + 'content' => '[hvac_google_sheets]', + ], + ] + ] + ]; + + // Define root pages (flat structure) + $root_pages = [ + 'training-login' => [ + 'title' => 'Trainer Login', + 'content' => '[hvac_community_login]', + 'template' => 'page-community-login.php', + ], + 'registration-pending' => [ + 'title' => 'Registration Pending', + 'content' => '
+

Registration Submitted Successfully

+

Thank you for registering as an HVAC trainer. Your registration has been submitted and is currently being reviewed.

+

You will receive an email notification once your account has been approved and activated.

+

If you have any questions, please contact our support team.

+

Return to Login

+
', + ], + ]; + + $created_pages = []; + + // Create root pages first + HVAC_Logger::info('Creating root pages...', 'Activation'); + foreach ($root_pages as $slug => $page_data) { + $existing = get_page_by_path($slug); + if (!$existing) { + $page_args = [ + 'post_title' => $page_data['title'], + 'post_name' => $slug, + 'post_content' => $page_data['content'], + 'post_status' => 'publish', + 'post_type' => 'page', + 'comment_status' => 'closed', + 'ping_status' => 'closed', + ]; + + if (!empty($page_data['template'])) { + $page_args['page_template'] = $page_data['template']; + } + + $page_id = wp_insert_post($page_args); + if (!is_wp_error($page_id)) { + $created_pages[$slug] = $page_id; + HVAC_Logger::info("Created root page: {$page_data['title']} (/{$slug}/)", 'Activation'); + } else { + HVAC_Logger::error("Failed to create root page: {$slug} - " . $page_id->get_error_message(), 'Activation'); + } + } else { + HVAC_Logger::info("Root page exists: {$page_data['title']} (/{$slug}/)", 'Activation'); + } + } + + // Create hierarchical pages + HVAC_Logger::info('Creating hierarchical pages...', 'Activation'); + foreach ($parent_pages as $parent_slug => $parent_data) { + // Create parent page + $existing_parent = get_page_by_path($parent_slug); + $parent_id = null; + + if (!$existing_parent) { + $parent_args = [ + 'post_title' => $parent_data['title'], + 'post_name' => $parent_slug, + 'post_content' => $parent_data['content'], + 'post_status' => 'publish', + 'post_type' => 'page', + 'comment_status' => 'closed', + 'ping_status' => 'closed', + ]; + + $parent_id = wp_insert_post($parent_args); + if (!is_wp_error($parent_id)) { + $created_pages[$parent_slug] = $parent_id; + HVAC_Logger::info("Created parent page: {$parent_data['title']} (/{$parent_slug}/)", 'Activation'); + } else { + HVAC_Logger::error("Failed to create parent page: {$parent_slug} - " . $parent_id->get_error_message(), 'Activation'); + continue; + } + } else { + $parent_id = $existing_parent->ID; + HVAC_Logger::info("Parent page exists: {$parent_data['title']} (/{$parent_slug}/)", 'Activation'); + } + + // Create child pages + if ($parent_id && isset($parent_data['children'])) { + foreach ($parent_data['children'] as $child_slug => $child_data) { + $full_path = $parent_slug . '/' . $child_slug; + $existing_child = get_page_by_path($full_path); + + if (!$existing_child) { + $child_args = [ + 'post_title' => $child_data['title'], + 'post_name' => $child_slug, + 'post_content' => $child_data['content'], + 'post_status' => 'publish', + 'post_type' => 'page', + 'post_parent' => $parent_id, + 'comment_status' => 'closed', + 'ping_status' => 'closed', + ]; + + $child_id = wp_insert_post($child_args); + if (!is_wp_error($child_id)) { + $created_pages[$full_path] = $child_id; + HVAC_Logger::info("Created child page: {$child_data['title']} (/{$full_path}/)", 'Activation'); + + // Handle grandchildren (like event/manage, event/summary) + if (isset($child_data['children'])) { + foreach ($child_data['children'] as $grandchild_slug => $grandchild_data) { + $grandchild_path = $parent_slug . '/' . $child_slug . '/' . $grandchild_slug; + $existing_grandchild = get_page_by_path($grandchild_path); + + if (!$existing_grandchild) { + $grandchild_args = [ + 'post_title' => $grandchild_data['title'], + 'post_name' => $grandchild_slug, + 'post_content' => $grandchild_data['content'], + 'post_status' => 'publish', + 'post_type' => 'page', + 'post_parent' => $child_id, + 'comment_status' => 'closed', + 'ping_status' => 'closed', + ]; + + $grandchild_id = wp_insert_post($grandchild_args); + if (!is_wp_error($grandchild_id)) { + $created_pages[$grandchild_path] = $grandchild_id; + HVAC_Logger::info("Created grandchild page: {$grandchild_data['title']} (/{$grandchild_path}/)", 'Activation'); + } else { + HVAC_Logger::error("Failed to create grandchild page: {$grandchild_path} - " . $grandchild_id->get_error_message(), 'Activation'); + } + } else { + HVAC_Logger::info("Grandchild page exists: {$grandchild_data['title']} (/{$grandchild_path}/)", 'Activation'); + } + } + } + } else { + HVAC_Logger::error("Failed to create child page: {$full_path} - " . $child_id->get_error_message(), 'Activation'); + } + } else { + HVAC_Logger::info("Child page exists: {$child_data['title']} (/{$full_path}/)", 'Activation'); + } + } + } + } + + // Store created pages in WordPress option + update_option('hvac_ce_created_pages', $created_pages); + HVAC_Logger::info('Page creation completed. Created ' . count($created_pages) . ' pages', 'Activation'); + + // Create the custom roles + $roles_manager = new HVAC_Roles(); + + // Create trainer role + $result = $roles_manager->create_trainer_role(); + if ($result) { + HVAC_Logger::info('Successfully created hvac_trainer role.', 'Activation'); + } else { + HVAC_Logger::error('Failed to create hvac_trainer role.', 'Activation'); + } + + // Create master trainer role + $master_result = $roles_manager->create_master_trainer_role(); + if ($master_result) { + HVAC_Logger::info('Successfully created hvac_master_trainer role.', 'Activation'); + } else { + HVAC_Logger::error('Failed to create hvac_master_trainer role.', 'Activation'); + } + + // Grant administrators access to dashboard to prevent redirect loops + $admin_access = $roles_manager->grant_admin_dashboard_access(); + if ($admin_access) { + HVAC_Logger::info('Successfully granted admin dashboard access.', 'Activation'); + } else { + HVAC_Logger::error('Failed to grant admin dashboard access.', 'Activation'); + } + + // Flush rewrite rules to ensure new URLs work + flush_rewrite_rules(); + HVAC_Logger::info('Rewrite rules flushed', 'Activation'); + + HVAC_Logger::info('Completed hierarchical page creation and role setup process', 'Activation'); + +} // End hvac_ce_create_required_pages +register_activation_hook(__FILE__, 'hvac_ce_create_required_pages'); + +/** + * Handle backward compatibility redirects for old URLs. + * + * This function redirects old page URLs to their new hierarchical structure + * to maintain compatibility for existing bookmarks and external links. + */ +function hvac_ce_handle_legacy_redirects() { + // Legacy URL to new URL mapping + $legacy_redirects = [ + 'community-login' => 'training-login', + 'hvac-dashboard' => 'trainer/dashboard', + 'master-dashboard' => 'master-trainer/dashboard', + 'manage-event' => 'trainer/event/manage', + 'trainer-profile' => 'trainer/my-profile', + 'event-summary' => 'trainer/event/summary', + 'email-attendees' => 'trainer/email-attendees', + 'certificate-reports' => 'trainer/certificate-reports', + 'generate-certificates' => 'trainer/generate-certificates', + 'certificate-fix' => 'master-trainer/certificate-fix', + 'hvac-documentation' => 'trainer/documentation', + 'attendee-profile' => 'trainer/attendee-profile', + 'google-sheets' => 'master-trainer/google-sheets', + 'communication-templates' => 'trainer/communication-templates', + 'communication-schedules' => 'trainer/communication-schedules', + 'trainer-registration' => 'trainer/registration', + ]; + + // Get current page slug + global $post; + if (!is_page() || !$post) { + return; + } + + $current_slug = $post->post_name; + + // Check if current page is a legacy URL that needs redirecting + if (isset($legacy_redirects[$current_slug])) { + // Get current URL path to prevent redirect loops + $current_path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/'); + $target_path = $legacy_redirects[$current_slug]; + + // Skip redirect if we are already on the target path + if ($current_path === $target_path) { + return; + } + + $new_url = home_url('/' . $legacy_redirects[$current_slug] . '/'); + + // Preserve query parameters + if (!empty($_SERVER['QUERY_STRING'])) { + $new_url .= '?' . $_SERVER['QUERY_STRING']; + } + + // Perform 301 redirect + wp_redirect($new_url, 301); + exit; + } +} +add_action('template_redirect', 'hvac_ce_handle_legacy_redirects'); + +/** + * Remove custom roles upon plugin deactivation. + */ +function hvac_ce_remove_roles() { + require_once HVAC_CE_PLUGIN_DIR . 'includes/class-hvac-roles.php'; + $roles_manager = new HVAC_Roles(); + $roles_manager->remove_trainer_role(); + $roles_manager->remove_master_trainer_role(); + $roles_manager->revoke_admin_dashboard_access(); + + // Flush rewrite rules to clean up certificate download URLs + flush_rewrite_rules(); + + HVAC_Logger::info('Deactivation hook fired, removed hvac_trainer role and admin dashboard access, flushed rewrite rules.', 'Deactivation'); +} +register_deactivation_hook(__FILE__, 'hvac_ce_remove_roles'); + + + +/** + * Enqueue common styles and scripts for HVAC Community Events pages + */ +function hvac_ce_enqueue_common_assets() { + // Add debug logging to see if function is being called + + // Early return if not on HVAC pages to prevent loading on home page + if (is_front_page() || is_home()) { + return; + } + + // Check if we're on an HVAC plugin page - include both hierarchical and flat page names + $hvac_pages = [ + // Hierarchical page paths + 'trainer/dashboard', 'trainer/registration', 'trainer/my-profile', + 'trainer/event/manage', 'trainer/event/summary', 'trainer/email-attendees', 'trainer/certificate-reports', + 'trainer/generate-certificates', 'master-trainer/certificate-fix', 'trainer/documentation', 'trainer/attendee-profile', + 'master-trainer/dashboard', 'master-trainer/google-sheets', 'trainer/communication-templates', 'trainer/communication-schedules', + // Flat page names (legacy and backup) + 'training-login', 'trainer-dashboard', 'trainer-registration', 'trainer-my-profile', + 'trainer-event-manage', 'trainer-event-summary', 'trainer-email-attendees', 'trainer-certificate-reports', + 'trainer-generate-certificates', 'master-trainer-certificate-fix', 'trainer-documentation', 'trainer-attendee-profile', + 'master-trainer-dashboard', 'master-trainer-google-sheets', 'trainer-communication-templates', 'trainer-communication-schedules', + // Child page names only + 'dashboard', 'registration', 'my-profile', 'manage', 'summary', 'email-attendees', 'certificate-reports', + 'generate-certificates', 'certificate-fix', 'documentation', 'attendee-profile', 'google-sheets', 'communication-templates', 'communication-schedules' + ]; + + // Check if we're on an HVAC page using multiple methods + $is_hvac_page = false; + + // Method 1: Check by page slug/path + if (is_page($hvac_pages)) { + $is_hvac_page = true; + } + + // Method 2: Check by post content containing HVAC shortcodes + global $post; + if ($post && !$is_hvac_page) { + $content = $post->post_content; + $hvac_shortcodes = ['hvac_dashboard', 'hvac_master_dashboard', 'hvac_community_login', 'hvac_google_sheets', 'hvac_certificate_reports', 'hvac_generate_certificates']; + foreach ($hvac_shortcodes as $shortcode) { + if (strpos($content, $shortcode) !== false) { + $is_hvac_page = true; + break; + } + } + } + + // Method 3: Force enable for testing - check if URL contains known HVAC paths + if (!$is_hvac_page && isset($_SERVER['REQUEST_URI'])) { + $uri = $_SERVER['REQUEST_URI']; + if (strpos($uri, '/trainer/') !== false || strpos($uri, '/master-trainer/') !== false || strpos($uri, '/training-login/') !== false) { + $is_hvac_page = true; + } + } + + // Method 4: For debugging - force enable if admin is logged in and URL suggests HVAC content + if (!$is_hvac_page && current_user_can('administrator')) { + if (isset($_SERVER['REQUEST_URI'])) { + $uri = $_SERVER['REQUEST_URI']; + if (strpos($uri, 'hvac') !== false || + strpos($uri, 'certificate') !== false || + strpos($uri, 'dashboard') !== false || + strpos($uri, 'google-sheets') !== false) { + $is_hvac_page = true; + } + } + } + + // Method 5: Temporary fix - always load on pages with specific names + if ($post && !$is_hvac_page) { + $post_name = $post->post_name; + $debug_keywords = ['dashboard', 'google-sheets', 'certificate', 'trainer']; + foreach ($debug_keywords as $keyword) { + if (strpos($post_name, $keyword) !== false) { + $is_hvac_page = true; + break; + } + } + } + + + // For now, let's be more permissive to debug the issue + if (!$is_hvac_page) { + // Temporary: Always load CSS if we're on any non-home page for debugging + if (!is_front_page() && !is_home()) { + $is_hvac_page = true; + } + } + + // Only proceed if we're on an HVAC page + if (!$is_hvac_page) { + return; + } + + // Enqueue admin bar hiding script for trainers + if (is_user_logged_in()) { + $user = wp_get_current_user(); + $has_trainer_role = in_array('hvac_trainer', $user->roles) || in_array('hvac_master_trainer', $user->roles); + $has_admin_role = in_array('administrator', $user->roles); + + if ($has_trainer_role && !$has_admin_role) { + wp_enqueue_script( + 'hvac-admin-bar-hide', + HVAC_CE_PLUGIN_URL . 'assets/js/hvac-admin-bar-hide.js', + array(), + HVAC_CE_VERSION, + false // Load in header for immediate effect + ); + } + } + + + // Enqueue the harmonized framework first - this provides the base styling + wp_enqueue_style( + 'hvac-harmonized-framework', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-harmonized.css', + [], // No dependencies - this is the foundation + HVAC_CE_VERSION . '-v3.0.0' + ); + + // Enqueue the legacy common CSS file for backward compatibility + wp_enqueue_style( + 'hvac-common-style', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-common.css', + ['hvac-harmonized-framework'], // Depends on harmonized framework + HVAC_CE_VERSION + ); + + // Enqueue animations CSS file (ONLY on HVAC pages) + wp_enqueue_style( + 'hvac-animations', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-animations.css', + ['hvac-harmonized-framework'], // Depends on harmonized framework + HVAC_CE_VERSION + ); + + // Enqueue mobile navigation CSS file (ONLY on HVAC pages) + wp_enqueue_style( + 'hvac-mobile-nav', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-mobile-nav.css', + ['hvac-harmonized-framework'], // Depends on harmonized framework + HVAC_CE_VERSION + ); + + // Enqueue print stylesheet + wp_enqueue_style( + 'hvac-print-style', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-print.css', + ['hvac-harmonized-framework'], // Depends on harmonized framework + HVAC_CE_VERSION, + 'print' // Print media only + ); + + // Enqueue the accessibility helper JS (ONLY on HVAC pages) + wp_enqueue_script( + 'hvac-accessibility-js', + HVAC_CE_PLUGIN_URL . 'assets/js/hvac-accessibility.js', + [], // No dependencies + HVAC_CE_VERSION, + true // Load in footer + ); + + // Enqueue animations JS (ONLY on HVAC pages) + wp_enqueue_script( + 'hvac-animations-js', + HVAC_CE_PLUGIN_URL . 'assets/js/hvac-animations.js', + [], // No dependencies + HVAC_CE_VERSION, + true // Load in footer + ); + + // Enqueue mobile navigation JS (ONLY on HVAC pages) + wp_enqueue_script( + 'hvac-mobile-nav-js', + HVAC_CE_PLUGIN_URL . 'assets/js/hvac-mobile-nav.js', + [], // No dependencies + HVAC_CE_VERSION, + true // Load in footer + ); + + // Enqueue page-specific enhanced styles based on current page + if (is_page('trainer/dashboard')) { + wp_enqueue_style( + 'hvac-dashboard-enhanced', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-dashboard-enhanced.css', + ['hvac-harmonized-framework'], // Depends on harmonized framework + HVAC_CE_VERSION . '-v3.0.0' + ); + // Keep legacy for compatibility + wp_enqueue_style( + 'hvac-dashboard-style', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-dashboard.css', + ['hvac-dashboard-enhanced'], // Load after enhanced + HVAC_CE_VERSION + ); + } + + if (is_page('master-trainer/dashboard')) { + // Master dashboard uses same styling as regular dashboard + wp_enqueue_style( + 'hvac-dashboard-enhanced', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-dashboard-enhanced.css', + ['hvac-harmonized-framework'], // Depends on harmonized framework + HVAC_CE_VERSION . '-v3.0.0' + ); + wp_enqueue_style( + 'hvac-dashboard-style', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-dashboard.css', + ['hvac-dashboard-enhanced'], // Load after enhanced + HVAC_CE_VERSION + ); + } + + if (is_page('training-login')) { + wp_enqueue_style( + 'hvac-community-login-enhanced', + HVAC_CE_PLUGIN_URL . 'assets/css/community-login-enhanced.css', + ['hvac-harmonized-framework'], // Depends on harmonized framework + HVAC_CE_VERSION . '-v3.0.0' + ); + // Keep legacy for compatibility + wp_enqueue_style( + 'hvac-community-login-style', + HVAC_CE_PLUGIN_URL . 'assets/css/community-login.css', + ['hvac-community-login-enhanced'], // Load after enhanced + HVAC_CE_VERSION + ); + } + + if (is_page('trainer/registration')) { + wp_enqueue_style( + 'hvac-registration-style', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-registration.css', + ['hvac-common-style'], // Depends on common styles + HVAC_CE_VERSION + ); + } + + if (is_page('trainer/email-attendees')) { + wp_enqueue_style( + 'hvac-email-attendees-style', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-email-attendees.css', + ['hvac-harmonized-framework'], // Depends on harmonized framework + HVAC_CE_VERSION + ); + } + + if (is_singular(Tribe__Events__Main::POSTTYPE) || is_page('trainer/event/summary')) { + wp_enqueue_style( + 'hvac-event-summary-style', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-event-summary.css', + ['hvac-common-style'], // Depends on common styles + HVAC_CE_VERSION + ); + + // Enqueue event summary JS for certificate actions + wp_enqueue_script( + 'hvac-event-summary-js', + HVAC_CE_PLUGIN_URL . 'assets/js/hvac-event-summary.js', + ['jquery'], // jQuery dependency + HVAC_CE_VERSION, + true // Load in footer + ); + + // Localize script with AJAX data + wp_localize_script('hvac-event-summary-js', 'hvacEventSummary', [ + 'ajaxUrl' => admin_url('admin-ajax.php'), + 'certificateNonce' => wp_create_nonce('hvac_certificate_actions') + ]); + } + + // Enqueue styles for event management page + // Force load CSS on event manage page regardless of user status + $current_url = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; + $is_event_manage_url = strpos($current_url, '/trainer/event/manage') !== false || + strpos($current_url, 'event/manage') !== false || + strpos($current_url, 'event%2Fmanage') !== false; + + // Check by multiple methods to ensure we catch the page + $is_event_manage = $is_event_manage_url || + is_page('trainer/event/manage') || + is_page('manage') || + is_page(5344) || // Direct page ID from staging + is_page('trainer-event-manage'); // Legacy slug + + // Also check if this is ANY page with Community Events content + $has_community_events = false; + if (!$is_event_manage && function_exists('tribe_is_community_edit_event_page')) { + $has_community_events = tribe_is_community_edit_event_page() || tribe_is_community_my_events_page(); + } + + // Force CSS for event manage page - this ensures it loads for ALL users + if ($is_event_manage || $has_community_events || $is_event_manage_url) { + // Enqueue dedicated event management CSS + wp_enqueue_style( + 'hvac-event-manage-style', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-event-manage.css', + ['hvac-common-style'], + HVAC_CE_VERSION + ); + + // Also enqueue dashboard styles for consistency + wp_enqueue_style( + 'hvac-dashboard-style', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-dashboard.css', + ['hvac-common-style'], + HVAC_CE_VERSION + ); + + // CSS is now enqueued for event management pages + } + + // Enqueue certificate-related styles + if (is_page('trainer/certificate-reports') || is_page('trainer/generate-certificates') || is_page('master-trainer/certificate-fix')) { + wp_enqueue_style( + 'hvac-certificates-enhanced', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-certificates-enhanced.css', + ['hvac-harmonized-framework'], // Depends on harmonized framework + HVAC_CE_VERSION . '-v3.0.0' + ); + // Keep legacy for compatibility + wp_enqueue_style( + 'hvac-certificates-admin-style', + HVAC_CE_PLUGIN_URL . 'assets/css/hvac-certificates-admin.css', + ['hvac-certificates-enhanced'], // Load after enhanced + HVAC_CE_VERSION + ); + + // Enqueue certificate JS + wp_enqueue_script( + 'hvac-certificate-admin-js', + HVAC_CE_PLUGIN_URL . 'assets/js/hvac-certificate-admin.js', + ['jquery', 'wp-color-picker'], // jQuery dependency + HVAC_CE_VERSION, + true // Load in footer + ); + + // Add WordPress color picker if needed + wp_enqueue_style('wp-color-picker'); + wp_enqueue_script('wp-color-picker'); + + // Localize script with AJAX data + wp_localize_script('hvac-certificate-admin-js', 'hvacCertificateData', [ + 'ajaxUrl' => admin_url('admin-ajax.php'), + 'previewNonce' => wp_create_nonce('hvac_certificate_preview') + ]); + } +} +add_action('wp_enqueue_scripts', 'hvac_ce_enqueue_common_assets'); + +/** + * Enqueue styles and scripts for admin dashboard + */ +function hvac_ce_enqueue_admin_assets($hook) { + // Only load on our dashboard page + if ($hook !== 'hvac-community-events_page_hvac-ce-dashboard') { + return; + } + + // Enqueue dashboard CSS + wp_enqueue_style( + 'hvac-admin-dashboard-style', + HVAC_CE_PLUGIN_URL . 'assets/css/admin-dashboard.css', + array('wp-admin'), + HVAC_CE_VERSION + ); + + // Enqueue dashboard JS + wp_enqueue_script( + 'hvac-admin-dashboard-script', + HVAC_CE_PLUGIN_URL . 'assets/js/admin-dashboard.js', + array('jquery', 'wp-util'), + HVAC_CE_VERSION, + true + ); + + // Localize script with AJAX data + wp_localize_script('hvac-admin-dashboard-script', 'hvac_admin_dashboard', array( + 'ajax_url' => admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('hvac_admin_nonce') + )); +} + + + +// Include the main plugin class +require_once HVAC_CE_PLUGIN_DIR . 'includes/class-hvac-community-events.php'; + +// Include the help system +require_once HVAC_CE_PLUGIN_DIR . 'includes/class-hvac-help-system.php'; + +// Initialize the plugin +function hvac_community_events_init() { + HVAC_Logger::info('Initializing HVAC Community Events plugin', 'Core'); + return HVAC_Community_Events::instance(); +} +add_action('plugins_loaded', 'hvac_community_events_init'); + +// Redirect /trainer/ to /trainer/dashboard/ +add_action('template_redirect', 'hvac_ce_redirect_trainer_parent_page'); +function hvac_ce_redirect_trainer_parent_page() { + // Get the current URL path + $current_path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/'); + + // Check if we're on the trainer parent page (not a child page) + if ($current_path === 'trainer' || $current_path === 'trainer/') { + // Redirect to the dashboard + wp_redirect(home_url('/trainer/dashboard/'), 301); + exit; + } + + // Also redirect master-trainer to master-trainer/dashboard + if ($current_path === 'master-trainer' || $current_path === 'master-trainer/') { + wp_redirect(home_url('/master-trainer/dashboard/'), 301); + exit; + } +} + + +// Initialize certificate URL handler very early to catch URLs before 404 +function hvac_init_certificate_url_handler() { + // Load the certificate URL handler class if not already loaded + if (!class_exists('HVAC_Certificate_URL_Handler')) { + $handler_file = HVAC_CE_PLUGIN_DIR . 'includes/certificates/class-certificate-url-handler.php'; + if (file_exists($handler_file)) { + require_once $handler_file; + } + } + + // Initialize the handler if class exists + if (class_exists('HVAC_Certificate_URL_Handler')) { + HVAC_Certificate_URL_Handler::instance(); + } +} +// Hook very early - before WordPress decides it's a 404 +add_action('muplugins_loaded', 'hvac_init_certificate_url_handler', 1); +add_action('plugins_loaded', 'hvac_init_certificate_url_handler', 1); + + +/** + * Include custom template for single event summary page. + * + * @param string $template The path of the template to include. + * @return string The path of the template file. + */ +function hvac_ce_include_event_summary_template( $template ) { + // Check if it's a single event post type view + if ( is_singular( Tribe__Events__Main::POSTTYPE ) ) { + // Check if the custom template exists in the plugin's template directory + $custom_template = HVAC_CE_PLUGIN_DIR . 'templates/single-hvac-event-summary.php'; + if ( file_exists( $custom_template ) ) { + // Return the path to the custom template + return $custom_template; + } + } + // Return the original template if not a single event or custom template doesn't exist + return $template; +} + +/** + * Template routing for Order Summary Page. + */ +function hvac_ce_include_order_summary_template( $template ) { + if ( is_page( 'order-summary' ) && isset( $_GET['order_id'] ) && absint( $_GET['order_id'] ) > 0 ) { + $custom_template = HVAC_CE_PLUGIN_DIR . 'templates/single-hvac-order-summary.php'; + if ( file_exists( $custom_template ) ) { + return $custom_template; + } + } + return $template; +} +// Removed - template handling is now in the main class +// add_filter( 'template_include', 'hvac_ce_include_event_summary_template', 99 ); + +/** + * Initialize attendee profile handler + */ +function hvac_init_attendee_profile() { + // Load the attendee profile class if not already loaded + if (!class_exists('HVAC_Attendee_Profile')) { + $profile_file = HVAC_CE_PLUGIN_DIR . 'includes/class-attendee-profile.php'; + if (file_exists($profile_file)) { + require_once $profile_file; + } + } + + // Initialize the handler if class exists + if (class_exists('HVAC_Attendee_Profile')) { + HVAC_Attendee_Profile::instance(); + } +} +// Initialize on plugins_loaded +add_action('plugins_loaded', 'hvac_init_attendee_profile', 10); + +// Include attendee profile helper functions +require_once HVAC_CE_PLUGIN_DIR . 'includes/helpers/attendee-profile-link.php'; + +/** + * Handle AJAX request for master dashboard events table + */ +function hvac_ajax_master_dashboard_events() { + // Verify nonce + if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_master_dashboard_nonce')) { + wp_die('Security check failed'); + } + + // Check permissions + if (!current_user_can('view_master_dashboard') && !current_user_can('view_all_trainer_data') && !current_user_can('manage_options')) { + wp_send_json_error('Insufficient permissions'); + } + + // Load master dashboard data class if needed + if (!class_exists('HVAC_Master_Dashboard_Data')) { + require_once HVAC_CE_PLUGIN_DIR . 'includes/class-hvac-master-dashboard-data.php'; + } + + // Initialize data handler + $master_data = new HVAC_Master_Dashboard_Data(); + + // Get table data with filters + $args = array( + 'status' => sanitize_text_field($_POST['status'] ?? 'all'), + 'search' => sanitize_text_field($_POST['search'] ?? ''), + 'orderby' => sanitize_text_field($_POST['orderby'] ?? 'date'), + 'order' => sanitize_text_field($_POST['order'] ?? 'DESC'), + 'page' => absint($_POST['page'] ?? 1), + 'per_page' => absint($_POST['per_page'] ?? 10), + 'date_from' => sanitize_text_field($_POST['date_from'] ?? ''), + 'date_to' => sanitize_text_field($_POST['date_to'] ?? ''), + 'trainer_id' => absint($_POST['trainer_id'] ?? 0), + ); + + $table_data = $master_data->get_events_table_data($args); + wp_send_json_success($table_data); +} +add_action('wp_ajax_hvac_master_dashboard_events', 'hvac_ajax_master_dashboard_events'); +add_action('wp_ajax_nopriv_hvac_master_dashboard_events', 'hvac_ajax_master_dashboard_events'); diff --git a/includes/admin/class-zoho-admin.php b/includes/admin/class-zoho-admin.php new file mode 100644 index 00000000..76bbb874 --- /dev/null +++ b/includes/admin/class-zoho-admin.php @@ -0,0 +1,924 @@ + admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('hvac_zoho_nonce') + )); + + // Add inline test script for debugging + wp_add_inline_script('hvac-zoho-admin', ' + console.log("Zoho admin script loaded"); + jQuery(document).ready(function($) { + console.log("DOM ready, setting up click handler"); + $(document).on("click", "#test-zoho-connection", function() { + console.log("Test button clicked - inline script"); + }); + }); + '); + + wp_enqueue_style( + 'hvac-zoho-admin', + HVAC_CE_PLUGIN_URL . 'assets/css/zoho-admin.css', + array(), + HVAC_CE_VERSION + ); + } + + /** + * Render admin page + */ + public function render_admin_page() { + $site_url = get_site_url(); + + // Debug logging + + // More robust production detection + $parsed_url = parse_url($site_url); + $host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; + + // Remove www prefix for comparison + $clean_host = preg_replace('/^www\./', '', $host); + + // Check if this is production + $is_production = ($clean_host === 'upskillhvac.com'); + + // Double-check with string comparison as fallback + if (!$is_production) { + $is_production = (strpos($site_url, 'upskillhvac.com') !== false && + strpos($site_url, 'staging') === false && + strpos($site_url, 'test') === false && + strpos($site_url, 'dev') === false && + strpos($site_url, 'cloudwaysapps.com') === false); + } + + // Set staging as opposite of production + $is_staging = !$is_production; + + error_log('[HVAC Zoho] Is Production: ' . ($is_production ? 'YES' : 'NO')); + error_log('[HVAC Zoho] Is Staging: ' . ($is_staging ? 'YES' : 'NO')); + + // Get stored credentials + $client_id = get_option('hvac_zoho_client_id', ''); + $client_secret = get_option('hvac_zoho_client_secret', ''); + $stored_refresh_token = get_option('hvac_zoho_refresh_token', ''); + $has_credentials = !empty($client_id) && !empty($client_secret); + + // Handle form submission + if (isset($_GET['credentials_saved'])) { + echo '

Zoho CRM credentials saved successfully!

'; + } + if (isset($_GET['oauth_success'])) { + echo '

OAuth authorization completed successfully!

'; + } + if (isset($_GET['oauth_error'])) { + echo '

OAuth authorization failed. Please try again.

'; + } + + ?> +
+

Zoho CRM Sync

+ + +
+

🔧 STAGING MODE ACTIVE

+

Current site:

+

Staging mode is active. Data sync will be simulated only. No actual data will be sent to Zoho CRM.

+

Production sync is only enabled on upskillhvac.com

+

OAuth Redirect URI:

+

Use this redirect URI in your Zoho OAuth app configuration.

+
+ + + +
+

🔑 Zoho CRM Credentials

+
+ + + + + + + + + + + + + + + + + + + +
+ + + +

+ Your Zoho OAuth Client ID from the Zoho Developer Console. + Get your Client ID +

+
+ + + +

+ Your Zoho OAuth Client Secret from the Zoho Developer Console. + +

+
OAuth Redirect URI + +

+ Use this exact URL in your Zoho OAuth app configuration. + + +

+ +

+ OAuth rewrite rule: +

+
Connection Status + + + No credentials configured + + + Credentials set, OAuth authorization required + + + Connected and authorized + +
+ +

+ + + + +

+
+
+ + +
+

Connection Test

+

Test your Zoho CRM connection to ensure everything is working properly.

+ + +
+

⚠️ OAuth Authorization Required

+

You have saved credentials but need to authorize the application with Zoho CRM.

+

Click "Authorize with Zoho" above to complete the setup.

+
+ + +
+ +
+ +
+

Data Sync

+ +
+

Events → Campaigns

+

Sync events from The Events Calendar to Zoho CRM Campaigns

+ +
+
+ +
+

Users → Contacts

+

Sync trainers and attendees to Zoho CRM Contacts

+ +
+
+ +
+

Purchases → Invoices

+

Sync ticket purchases to Zoho CRM Invoices

+ +
+
+
+ +
+

Sync Settings

+
+ +

+ +

+ +
+
+ +
+ + + 'Simple test works!')); + } + + /** + * Save Zoho CRM credentials + */ + public function save_credentials() { + if (!current_user_can('manage_options')) { + wp_send_json_error(array('message' => 'Unauthorized access')); + return; + } + + if (!check_ajax_referer('hvac_zoho_credentials', 'nonce', false)) { + wp_send_json_error(array('message' => 'Invalid nonce')); + return; + } + + $client_id = sanitize_text_field($_POST['zoho_client_id']); + $client_secret = sanitize_text_field($_POST['zoho_client_secret']); + + if (empty($client_id) || empty($client_secret)) { + wp_send_json_error(array('message' => 'Client ID and Client Secret are required')); + return; + } + + // Validate Client ID format (should start with "1000.") + if (!preg_match('/^1000\.[A-Z0-9]+$/', $client_id)) { + wp_send_json_error(array('message' => 'Invalid Client ID format. Should start with "1000."')); + return; + } + + // Save credentials + update_option('hvac_zoho_client_id', $client_id); + update_option('hvac_zoho_client_secret', $client_secret); + + // Clear any existing refresh token since credentials changed + delete_option('hvac_zoho_refresh_token'); + + wp_send_json_success(array( + 'message' => 'Credentials saved successfully', + 'client_id_preview' => substr($client_id, 0, 10) . '...' + )); + } + + /** + * Flush rewrite rules via AJAX + */ + public function flush_rewrite_rules_ajax() { + if (!current_user_can('manage_options')) { + wp_send_json_error(array('message' => 'Unauthorized access')); + return; + } + + // Clear any cached rules first + wp_cache_delete('rewrite_rules', 'options'); + + // Add OAuth rewrite rules multiple ways + add_rewrite_rule('^oauth/callback/?$', 'index.php?hvac_oauth_callback=1', 'top'); + add_rewrite_rule('oauth/callback/?$', 'index.php?hvac_oauth_callback=1', 'top'); + + // Force hard flush + flush_rewrite_rules(true); + + // Clear cache again + wp_cache_delete('rewrite_rules', 'options'); + + // Add rules again and soft flush + $this->add_oauth_rewrite_rule(); + flush_rewrite_rules(false); + + // Force WordPress to regenerate rules + delete_option('rewrite_rules'); + wp_cache_delete('rewrite_rules', 'options'); + $wp_rewrite = $GLOBALS['wp_rewrite']; + $wp_rewrite->flush_rules(true); + + // Verify the rule exists after flush + $rewrite_rules = get_option('rewrite_rules', array()); + $oauth_rule_exists = isset($rewrite_rules['^oauth/callback/?$']) || isset($rewrite_rules['oauth/callback/?$']); + + // Log for debugging + error_log('[HVAC Zoho] Flush rewrite rules result: ' . ($oauth_rule_exists ? 'SUCCESS' : 'FAILED')); + error_log('[HVAC Zoho] Total rules after flush: ' . count($rewrite_rules)); + + wp_send_json_success(array( + 'message' => 'Rewrite rules flushed successfully', + 'oauth_rule_exists' => $oauth_rule_exists, + 'total_rules' => count($rewrite_rules), + 'rules_sample' => array_slice(array_keys($rewrite_rules), 0, 5) + )); + } + + /** + * Flush rewrite rules on plugin activation + */ + public function flush_rewrite_rules_on_activation() { + $this->add_oauth_rewrite_rule(); + flush_rewrite_rules(); + } + + /** + * Add OAuth query vars + */ + public function add_oauth_query_vars($vars) { + // Only add if not already present to avoid duplicates + if (!in_array('hvac_oauth_callback', $vars)) { + $vars[] = 'hvac_oauth_callback'; + } + return $vars; + } + + /** + * Add OAuth query vars to public query vars + */ + public function add_public_query_vars() { + global $wp; + // Check if already added to avoid duplicates + if (!in_array('hvac_oauth_callback', $wp->public_query_vars)) { + $wp->add_query_var('hvac_oauth_callback'); + } + } + + /** + * Parse OAuth request using parse_request hook + */ + public function parse_oauth_request($wp) { + + // Check if this is an OAuth callback request + if (preg_match('#^/oauth/callback/?#', $_SERVER['REQUEST_URI'])) { + + // Check if we have the code parameter + if (isset($_GET['code'])) { + error_log('Processing OAuth callback directly from parse_request'); + $this->process_oauth_callback(); + exit; + } else { + + wp_die('OAuth callback missing authorization code'); + } + } + } + + /** + * Add OAuth callback rewrite rule + */ + public function add_oauth_rewrite_rule() { + add_rewrite_rule('^oauth/callback/?$', 'index.php?hvac_oauth_callback=1', 'top'); + + // Also add alternative rule patterns + add_rewrite_rule('oauth/callback/?$', 'index.php?hvac_oauth_callback=1', 'top'); + + // Force flush if the rule doesn't exist + $rewrite_rules = get_option('rewrite_rules'); + if (!$rewrite_rules || !isset($rewrite_rules['^oauth/callback/?$'])) { + // Set a flag to flush rules on next page load + update_option('hvac_oauth_rules_need_flush', true); + + // Also try to flush immediately if we're in admin + if (is_admin()) { + flush_rewrite_rules(false); + } + } + + // Check if we need to flush based on flag + if (get_option('hvac_oauth_rules_need_flush')) { + flush_rewrite_rules(false); + delete_option('hvac_oauth_rules_need_flush'); + } + } + + /** + * Handle OAuth template redirect + */ + public function handle_oauth_template_redirect() { + if (get_query_var('hvac_oauth_callback')) { + $this->process_oauth_callback(); + } + } + + /** + * Process OAuth callback from Zoho + */ + public function process_oauth_callback() { + + if (!isset($_GET['code'])) { + + wp_die('OAuth callback missing authorization code'); + } + + error_log('OAuth callback received with code: ' . substr($_GET['code'], 0, 20) . '...'); + + // Get credentials from WordPress options + $client_id = get_option('hvac_zoho_client_id', ''); + $client_secret = get_option('hvac_zoho_client_secret', ''); + + if (empty($client_id) || empty($client_secret)) { + wp_die('OAuth callback error: Missing client credentials. Please configure your Zoho CRM credentials first.'); + } + + // Exchange authorization code for tokens + $token_url = 'https://accounts.zoho.com/oauth/v2/token'; + $redirect_uri = get_site_url() . '/oauth/callback'; + + $token_params = array( + 'grant_type' => 'authorization_code', + 'client_id' => $client_id, + 'client_secret' => $client_secret, + 'redirect_uri' => $redirect_uri, + 'code' => $_GET['code'] + ); + + error_log('OAuth token exchange params: ' . json_encode(array( + 'client_id' => substr($client_id, 0, 20) . '...', + 'redirect_uri' => $redirect_uri, + 'code' => substr($_GET['code'], 0, 20) . '...' + ))); + + $response = wp_remote_post($token_url, array( + 'body' => $token_params, + 'timeout' => 30 + )); + + if (is_wp_error($response)) { + error_log('OAuth token exchange error: ' . $response->get_error_message()); + wp_redirect(admin_url('admin.php?page=hvac-zoho-sync&oauth_error=1&error_msg=' . urlencode($response->get_error_message()))); + exit; + } + + $body = wp_remote_retrieve_body($response); + $token_data = json_decode($body, true); + + // Check for errors in response + if (isset($token_data['error'])) { + error_log('OAuth error: ' . $token_data['error'] . ' - ' . ($token_data['error_description'] ?? '')); + wp_redirect(admin_url('admin.php?page=hvac-zoho-sync&oauth_error=1&error_msg=' . urlencode($token_data['error']))); + exit; + } + + if (!isset($token_data['access_token'])) { + + wp_redirect(admin_url('admin.php?page=hvac-zoho-sync&oauth_error=1&error_msg=' . urlencode('No access token received'))); + exit; + } + + // Save tokens + update_option('hvac_zoho_access_token', $token_data['access_token']); + update_option('hvac_zoho_token_expires', time() + ($token_data['expires_in'] ?? 3600)); + + // Refresh token might not be returned on subsequent authorizations + if (isset($token_data['refresh_token']) && !empty($token_data['refresh_token'])) { + update_option('hvac_zoho_refresh_token', $token_data['refresh_token']); + error_log('Refresh token saved successfully'); + } else { + error_log('No refresh token in response - checking for existing token'); + $existing_refresh = get_option('hvac_zoho_refresh_token'); + if (empty($existing_refresh)) { + error_log('WARNING: No refresh token received and no existing token found'); + // This is critical - we need a refresh token for long-term access + // Store a warning but still complete the flow + update_option('hvac_zoho_missing_refresh_token', true); + } else { + error_log('Using existing refresh token'); + } + } + + // Success - redirect to admin page with success message + wp_redirect(admin_url('admin.php?page=hvac-zoho-sync&oauth_success=1')); + exit; + } + + /** + * Handle OAuth callback from Zoho (legacy method) + */ + public function handle_oauth_callback() { + // This method is kept for backwards compatibility + // The main handling is now done in template_redirect + return; + } + + /** + * Test Zoho connection + */ + public function test_connection() { + error_log('test_connection method called'); + + try { + check_ajax_referer('hvac_zoho_nonce', 'nonce'); + + if (!current_user_can('manage_options')) { + wp_send_json_error(array('message' => 'Unauthorized access')); + return; + } + + // Get credentials from WordPress options + $client_id = get_option('hvac_zoho_client_id', ''); + $client_secret = get_option('hvac_zoho_client_secret', ''); + + // Check configuration before attempting connection + if (empty($client_id)) { + wp_send_json_error(array( + 'message' => 'Configuration Error', + 'error' => 'Client ID is not configured', + 'details' => 'Please enter your Zoho CRM Client ID in the form above', + 'help' => 'Get your Client ID from the Zoho Developer Console' + )); + return; + } + + if (empty($client_secret)) { + wp_send_json_error(array( + 'message' => 'Configuration Error', + 'error' => 'Client Secret is not configured', + 'details' => 'Please enter your Zoho CRM Client Secret in the form above', + 'help' => 'Get your Client Secret from the Zoho Developer Console' + )); + return; + } + + // Check if we have stored refresh token from previous OAuth + $stored_refresh_token = get_option('hvac_zoho_refresh_token'); + + if (empty($stored_refresh_token)) { + error_log('No stored refresh token found, triggering OAuth authorization'); + + $site_url = get_site_url(); + $redirect_uri = $site_url . '/oauth/callback'; + $scopes = 'ZohoCRM.settings.ALL,ZohoCRM.modules.ALL,ZohoCRM.users.ALL,ZohoCRM.org.ALL,ZohoCRM.bulk.READ'; + $auth_url = 'https://accounts.zoho.com/oauth/v2/auth?' . http_build_query(array( + 'scope' => $scopes, + 'client_id' => $client_id, + 'response_type' => 'code', + 'access_type' => 'offline', + 'redirect_uri' => $redirect_uri, + 'prompt' => 'consent' + )); + + wp_send_json_error(array( + 'message' => 'OAuth Authorization Required', + 'error' => 'No stored refresh token found', + 'details' => 'You have valid credentials but need to complete OAuth authorization to get a fresh token', + 'help' => 'Click the "Authorize with Zoho" button above to complete setup', + 'next_steps' => array( + '1. Click the "Authorize with Zoho" button above', + '2. Sign in to your Zoho account', + '3. Grant permissions to the application', + '4. You will be redirected back and the refresh token will be saved automatically' + ), + 'auth_url' => $auth_url, + 'credentials_status' => array( + 'client_id' => substr($client_id, 0, 10) . '...', + 'client_secret_exists' => true, + 'refresh_token_exists' => false + ) + )); + return; + } + + // We have a refresh token - test the actual API connection + require_once HVAC_CE_PLUGIN_DIR . 'includes/zoho/class-zoho-crm-auth.php'; + $auth = new HVAC_Zoho_CRM_Auth(); + + error_log('Testing API with refresh token: ' . substr($stored_refresh_token, 0, 10) . '...'); + + // Test API call + $response = $auth->make_api_request('/settings/modules', 'GET'); + + if (is_wp_error($response)) { + error_log('WordPress HTTP error: ' . $response->get_error_message()); + wp_send_json_error(array( + 'message' => 'API Connection Failed', + 'error' => $response->get_error_message(), + 'details' => 'WordPress HTTP error occurred' + )); + return; + } + + if (isset($response['error'])) { + error_log('Zoho API error: ' . $response['error']); + + // Check if it's an invalid token error + if (strpos($response['error'], 'invalid') !== false || strpos($response['error'], 'expired') !== false) { + + // Clear the invalid token and trigger OAuth + delete_option('hvac_zoho_refresh_token'); + + $auth_url = $auth->get_authorization_url(); + $site_url = get_site_url(); + $redirect_uri = $site_url . '/oauth/callback'; + $scopes = 'ZohoCRM.settings.ALL,ZohoCRM.modules.ALL,ZohoCRM.users.ALL,ZohoCRM.org.ALL,ZohoCRM.bulk.READ'; + $new_auth_url = 'https://accounts.zoho.com/oauth/v2/auth?' . http_build_query(array( + 'scope' => $scopes, + 'client_id' => $client_id, + 'response_type' => 'code', + 'access_type' => 'offline', + 'redirect_uri' => $redirect_uri, + 'prompt' => 'consent' + )); + + wp_send_json_error(array( + 'message' => 'OAuth Authorization Required', + 'error' => 'Refresh token expired or invalid', + 'details' => 'The stored refresh token is no longer valid. Please re-authorize.', + 'help' => 'Refresh the page and click "Authorize with Zoho" again', + 'next_steps' => array( + '1. Refresh this page', + '2. Click the "Authorize with Zoho" button', + '3. Sign in to your Zoho account', + '4. Grant permissions to the application', + '5. You will be redirected back and the refresh token will be saved automatically' + ), + 'auth_url' => $new_auth_url, + 'credentials_status' => array( + 'client_id' => substr($client_id, 0, 10) . '...', + 'client_secret_exists' => true, + 'refresh_token_exists' => false + ) + )); + return; + } + + wp_send_json_error(array( + 'message' => 'Zoho API Error', + 'error' => $response['error'], + 'details' => isset($response['details']) ? $response['details'] : 'No additional details' + )); + return; + } + + // Success! + wp_send_json_success(array( + 'message' => 'Connection successful!', + 'modules' => isset($response['modules']) ? count($response['modules']) . ' modules available' : 'API connected', + 'credentials_status' => array( + 'client_id' => substr($client_id, 0, 10) . '...', + 'client_secret_exists' => true, + 'refresh_token_exists' => true, + 'api_working' => true + ) + )); + } catch (Exception $e) { + error_log('Exception in test_connection: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine()); + wp_send_json_error(array( + 'message' => 'Connection test failed due to exception', + 'error' => $e->getMessage(), + 'file' => $e->getFile() . ':' . $e->getLine() + )); + } catch (Error $e) { + error_log('PHP Error in test_connection: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine()); + wp_send_json_error(array( + 'message' => 'Connection test failed due to PHP error', + 'error' => $e->getMessage(), + 'file' => $e->getFile() . ':' . $e->getLine() + )); + } catch (Throwable $e) { + error_log('Fatal error in test_connection: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine()); + wp_send_json_error(array( + 'message' => 'Connection test failed due to fatal error', + 'error' => $e->getMessage(), + 'file' => $e->getFile() . ':' . $e->getLine() + )); + } + } + + /** + * Sync data to Zoho + */ + public function sync_data() { + check_ajax_referer('hvac_zoho_nonce', 'nonce'); + + if (!current_user_can('manage_options')) { + wp_die('Unauthorized'); + } + + $type = sanitize_text_field($_POST['type']); + + try { + require_once HVAC_CE_PLUGIN_DIR . 'includes/zoho/class-zoho-sync.php'; + $sync = new HVAC_Zoho_Sync(); + + switch ($type) { + case 'events': + $result = $sync->sync_events(); + break; + case 'users': + $result = $sync->sync_users(); + break; + case 'purchases': + $result = $sync->sync_purchases(); + break; + default: + throw new Exception('Invalid sync type'); + } + + wp_send_json_success($result); + } catch (Exception $e) { + wp_send_json_error(array( + 'message' => 'Sync failed', + 'error' => $e->getMessage() + )); + } + } +} +?> \ No newline at end of file