#!/bin/bash # # Deploy Zoho CRM integration fixes to staging server # This script uploads the modified PHP files and applies the changes # # Set variables STAGING_HOST="wordpress-974670-5399585.cloudwaysapps.com" STAGING_USER="uberrxmprk" # Default Cloudways username REMOTE_DIR="/home/974670.cloudwaysapps.com/uberrxmprk/public_html" PLUGIN_DIR="/wp-content/plugins/hvac-community-events" PLUGIN_PATH="${REMOTE_DIR}${PLUGIN_DIR}" # Colors for output GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Make script executable chmod +x $0 echo -e "${YELLOW}========================================" echo -e "Deploying Zoho CRM integration fixes" echo -e "========================================${NC}" # Check if SSH key authentication is set up echo -e "${YELLOW}Checking SSH connection...${NC}" ssh -o BatchMode=yes -o ConnectTimeout=5 ${STAGING_USER}@${STAGING_HOST} "echo Connected" >/dev/null 2>&1 if [ $? -ne 0 ]; then echo -e "${RED}Error: Cannot connect to staging server using SSH key authentication." echo -e "Please make sure your SSH key is added to the staging server.${NC}" echo -e "You may need to run: ssh-copy-id ${STAGING_USER}@${STAGING_HOST}" echo -e "Or use password authentication by modifying this script.${NC}" exit 1 fi echo -e "${GREEN}SSH connection successful.${NC}" # Create backup of original files echo -e "${YELLOW}1. Creating backups of original files${NC}" BACKUP_CMD="mkdir -p ${PLUGIN_PATH}/includes/backup-$(date +%Y%m%d) && \ cp ${PLUGIN_PATH}/includes/zoho/class-zoho-crm-auth.php ${PLUGIN_PATH}/includes/backup-$(date +%Y%m%d)/class-zoho-crm-auth.php.bak && \ cp ${PLUGIN_PATH}/includes/admin/class-zoho-admin.php ${PLUGIN_PATH}/includes/backup-$(date +%Y%m%d)/class-zoho-admin.php.bak && \ echo 'Backups created successfully'" ssh ${STAGING_USER}@${STAGING_HOST} "${BACKUP_CMD}" if [ $? -ne 0 ]; then echo -e "${RED}Error: Failed to create backups on the server${NC}" exit 1 fi echo -e "${GREEN}Backups created successfully.${NC}" # Create local files with fixes echo -e "${YELLOW}2. Creating fixed PHP files locally${NC}" # Create a temporary directory for our fixes TMP_DIR=$(mktemp -d) mkdir -p ${TMP_DIR}/includes/zoho mkdir -p ${TMP_DIR}/includes/admin # Create the fixed zoho-crm-auth.php file cat > ${TMP_DIR}/includes/zoho/class-zoho-crm-auth.php << 'EOF' load_env_variables(); // Load configuration if available $config_file = plugin_dir_path(__FILE__) . 'zoho-config.php'; if (file_exists($config_file)) { require_once $config_file; $this->client_id = defined('ZOHO_CLIENT_ID') ? ZOHO_CLIENT_ID : ''; $this->client_secret = defined('ZOHO_CLIENT_SECRET') ? ZOHO_CLIENT_SECRET : ''; $this->refresh_token = defined('ZOHO_REFRESH_TOKEN') ? ZOHO_REFRESH_TOKEN : ''; $this->redirect_uri = defined('ZOHO_REDIRECT_URI') ? ZOHO_REDIRECT_URI : 'http://localhost:8080/callback'; } // Load stored access token from WordPress options $this->load_access_token(); } /** * Load environment variables from .env file * This helps with local development and staging environments * * @return array Array of loaded environment variables */ public function load_env_variables() { $env_vars = array(); $env_file = ABSPATH . '../.env'; if (file_exists($env_file)) { $this->log_debug('Loading environment variables from .env file'); $env_contents = file_get_contents($env_file); $env_lines = explode("\n", $env_contents); foreach ($env_lines as $line) { $line = trim($line); // Skip comments and empty lines if (empty($line) || strpos($line, '#') === 0) { continue; } // Parse variable assignments if (strpos($line, '=') !== false) { list($key, $value) = explode('=', $line, 2); $key = trim($key); $value = trim($value); // Remove quotes if present if (strpos($value, '"') === 0 && strrpos($value, '"') === strlen($value) - 1) { $value = substr($value, 1, -1); } elseif (strpos($value, "'") === 0 && strrpos($value, "'") === strlen($value) - 1) { $value = substr($value, 1, -1); } $env_vars[$key] = $value; // Define constants for Zoho configuration if (strpos($key, 'ZOHO_') === 0 && !defined($key)) { define($key, $value); $this->log_debug("Defined $key from .env file"); } } } $zoho_vars_count = 0; foreach ($env_vars as $key => $value) { if (strpos($key, 'ZOHO_') === 0) { $zoho_vars_count++; } } $this->log_debug("Loaded $zoho_vars_count Zoho-related environment variables"); } else { $this->log_debug('No .env file found at: ' . $env_file); } return $env_vars; } /** * Generate authorization URL for initial setup */ public function get_authorization_url() { $params = array( 'scope' => defined('ZOHO_SCOPES') ? ZOHO_SCOPES : 'ZohoCRM.settings.all,ZohoCRM.modules.all,ZohoCRM.users.all,ZohoCRM.org.all', 'client_id' => $this->client_id, 'response_type' => 'code', 'access_type' => 'offline', 'redirect_uri' => $this->redirect_uri, 'prompt' => 'consent' ); $accounts_url = defined('ZOHO_ACCOUNTS_URL') ? ZOHO_ACCOUNTS_URL : 'https://accounts.zoho.com'; return $accounts_url . '/oauth/v2/auth?' . http_build_query($params); } /** * Exchange authorization code for tokens */ public function exchange_code_for_tokens($auth_code) { $accounts_url = defined('ZOHO_ACCOUNTS_URL') ? ZOHO_ACCOUNTS_URL : 'https://accounts.zoho.com'; $url = $accounts_url . '/oauth/v2/token'; $params = array( 'grant_type' => 'authorization_code', 'client_id' => $this->client_id, 'client_secret' => $this->client_secret, 'redirect_uri' => $this->redirect_uri, 'code' => $auth_code ); $response = wp_remote_post($url, array( 'body' => $params, 'headers' => array( 'Content-Type' => 'application/x-www-form-urlencoded' ) )); if (is_wp_error($response)) { $this->log_error('Failed to exchange code: ' . $response->get_error_message()); return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (isset($data['access_token']) && isset($data['refresh_token'])) { $this->access_token = $data['access_token']; $this->refresh_token = $data['refresh_token']; $this->token_expiry = time() + $data['expires_in']; // Save tokens $this->save_tokens(); return true; } $this->log_error('Invalid token response: ' . $body); return false; } /** * Get valid access token (refresh if needed) */ public function get_access_token() { // Check if token is expired or will expire soon (5 mins buffer) if (!$this->access_token || (time() + 300) >= $this->token_expiry) { $this->refresh_access_token(); } return $this->access_token; } /** * Refresh access token using refresh token */ private function refresh_access_token() { $accounts_url = defined('ZOHO_ACCOUNTS_URL') ? ZOHO_ACCOUNTS_URL : 'https://accounts.zoho.com'; $url = $accounts_url . '/oauth/v2/token'; $params = array( 'refresh_token' => $this->refresh_token, 'client_id' => $this->client_id, 'client_secret' => $this->client_secret, 'grant_type' => 'refresh_token' ); $response = wp_remote_post($url, array( 'body' => $params, 'headers' => array( 'Content-Type' => 'application/x-www-form-urlencoded' ) )); if (is_wp_error($response)) { $this->log_error('Failed to refresh token: ' . $response->get_error_message()); return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (isset($data['access_token'])) { $this->access_token = $data['access_token']; $this->token_expiry = time() + $data['expires_in']; $this->save_access_token(); return true; } $this->log_error('Failed to refresh token: ' . $body); return false; } /** * Make authenticated API request */ public function make_api_request($endpoint, $method = 'GET', $data = null) { // Check if we're in staging mode $site_url = get_site_url(); $is_staging = strpos($site_url, 'upskillhvac.com') === false; // In staging mode, only allow read operations, no writes if ($is_staging && in_array($method, array('POST', 'PUT', 'DELETE', 'PATCH'))) { $this->log_debug('STAGING MODE: Simulating ' . $method . ' request to ' . $endpoint); return array( 'data' => array( array( 'code' => 'STAGING_MODE', 'details' => array( 'message' => 'Staging mode active. Write operations are disabled.' ), 'message' => 'This would have been a ' . $method . ' request to: ' . $endpoint, 'status' => 'success' ) ) ); } // Debug logging of config status if (defined('ZOHO_DEBUG_MODE') && ZOHO_DEBUG_MODE) { $config_status = $this->get_configuration_status(); $this->log_debug('Configuration status: ' . json_encode($config_status)); if (!$config_status['client_id_exists']) { $this->log_error('Client ID is missing or empty'); } if (!$config_status['client_secret_exists']) { $this->log_error('Client Secret is missing or empty'); } if (!$config_status['refresh_token_exists']) { $this->log_error('Refresh Token is missing or empty'); } if ($config_status['token_expired']) { $this->log_debug('Access token is expired, will attempt to refresh'); } } $access_token = $this->get_access_token(); if (!$access_token) { $error_message = 'No valid access token available'; $this->log_error($error_message); return new WP_Error('no_token', $error_message); } $api_base_url = defined('ZOHO_API_BASE_URL') ? ZOHO_API_BASE_URL : 'https://www.zohoapis.com/crm/v2'; $url = $api_base_url . $endpoint; // Log the request details $this->log_debug('Making ' . $method . ' request to: ' . $url); $args = array( 'method' => $method, 'headers' => array( 'Authorization' => 'Zoho-oauthtoken ' . $access_token, 'Content-Type' => 'application/json' ), 'timeout' => 30 // Increase timeout to 30 seconds for potentially slow responses ); if ($data && in_array($method, array('POST', 'PUT', 'PATCH'))) { $args['body'] = json_encode($data); $this->log_debug('Request payload: ' . json_encode($data)); } // Execute the request $this->log_debug('Executing request to Zoho API'); $response = wp_remote_request($url, $args); // Handle WordPress errors if (is_wp_error($response)) { $error_message = 'API request failed: ' . $response->get_error_message(); $error_data = $response->get_error_data(); $this->log_error($error_message); $this->log_debug('Error details: ' . json_encode($error_data)); return $response; } // Get response code and body $status_code = wp_remote_retrieve_response_code($response); $headers = wp_remote_retrieve_headers($response); $body = wp_remote_retrieve_body($response); $this->log_debug('Response code: ' . $status_code); // Log headers for debugging if (defined('ZOHO_DEBUG_MODE') && ZOHO_DEBUG_MODE) { $this->log_debug('Response headers: ' . json_encode($headers->getAll())); } // Handle empty responses if (empty($body)) { $error_message = 'Empty response received from Zoho API'; $this->log_error($error_message); return array( 'error' => $error_message, 'code' => $status_code, 'details' => 'No response body received' ); } // Parse the JSON response $data = json_decode($body, true); // Check for JSON parsing errors if ($data === null && json_last_error() !== JSON_ERROR_NONE) { $error_message = 'Invalid JSON response: ' . json_last_error_msg(); $this->log_error($error_message); $this->log_debug('Raw response: ' . $body); return array( 'error' => $error_message, 'code' => 'JSON_PARSE_ERROR', 'details' => 'Raw response: ' . substr($body, 0, 255) . (strlen($body) > 255 ? '...' : '') ); } // Log response for debugging if (defined('ZOHO_DEBUG_MODE') && ZOHO_DEBUG_MODE) { $this->log_debug('API Response: ' . $body); } // Check for API errors if ($status_code >= 400) { $error_message = isset($data['message']) ? $data['message'] : 'API error with status code ' . $status_code; $this->log_error($error_message); // Add HTTP error information to the response $data['http_status'] = $status_code; $data['error'] = $error_message; // Extract more detailed error information if available if (isset($data['code'])) { $this->log_debug('Error code: ' . $data['code']); } if (isset($data['details'])) { $this->log_debug('Error details: ' . json_encode($data['details'])); } } return $data; } /** * Save tokens to WordPress options */ private function save_tokens() { update_option('hvac_zoho_refresh_token', $this->refresh_token); $this->save_access_token(); } /** * Save access token */ private function save_access_token() { update_option('hvac_zoho_access_token', $this->access_token); update_option('hvac_zoho_token_expiry', $this->token_expiry); } /** * Load access token from WordPress options */ private function load_access_token() { $this->access_token = get_option('hvac_zoho_access_token'); $this->token_expiry = get_option('hvac_zoho_token_expiry', 0); // Load refresh token if not set if (!$this->refresh_token) { $this->refresh_token = get_option('hvac_zoho_refresh_token'); } } /** * Log error messages */ private function log_error($message) { $this->last_error = $message; if (defined('ZOHO_LOG_FILE')) { error_log('[' . date('Y-m-d H:i:s') . '] ERROR: ' . $message . PHP_EOL, 3, ZOHO_LOG_FILE); } // Also log to WordPress debug log if available if (defined('WP_DEBUG') && WP_DEBUG && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) { error_log('[ZOHO CRM] ' . $message); } } /** * Log debug messages */ private function log_debug($message) { if (defined('ZOHO_DEBUG_MODE') && ZOHO_DEBUG_MODE && defined('ZOHO_LOG_FILE')) { error_log('[' . date('Y-m-d H:i:s') . '] DEBUG: ' . $message . PHP_EOL, 3, ZOHO_LOG_FILE); } // Also log to WordPress debug log if available if (defined('ZOHO_DEBUG_MODE') && ZOHO_DEBUG_MODE && defined('WP_DEBUG') && WP_DEBUG && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) { error_log('[ZOHO CRM DEBUG] ' . $message); } } /** * Get the last error message * * @return string|null */ public function get_last_error() { return $this->last_error; } /** * Get client ID (for debugging only) * * @return string */ public function get_client_id() { return $this->client_id; } /** * Check if client secret exists (for debugging only) * * @return bool */ public function get_client_secret() { return !empty($this->client_secret); } /** * Check if refresh token exists (for debugging only) * * @return bool */ public function get_refresh_token() { return !empty($this->refresh_token); } /** * Get configuration status (for debugging) * * @return array */ public function get_configuration_status() { return array( 'client_id_exists' => !empty($this->client_id), 'client_secret_exists' => !empty($this->client_secret), 'refresh_token_exists' => !empty($this->refresh_token), 'access_token_exists' => !empty($this->access_token), 'token_expired' => $this->token_expiry < time(), 'config_loaded' => file_exists(plugin_dir_path(__FILE__) . 'zoho-config.php') ); } } EOF # Create the enhanced zoho-admin.php file cat > ${TMP_DIR}/includes/admin/class-zoho-admin.php << 'EOF' admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('hvac_zoho_nonce') )); 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() { $config_file = HVAC_CE_PLUGIN_DIR . 'includes/zoho/zoho-config.php'; $is_configured = file_exists($config_file); $site_url = get_site_url(); $is_staging = strpos($site_url, 'upskillhvac.com') === false; ?>
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
Zoho CRM is not configured. Please complete the OAuth setup first.
Run: ./bin/zoho-setup-complete.sh
Sync events from The Events Calendar to Zoho CRM Campaigns
Sync trainers and attendees to Zoho CRM Contacts
Sync ticket purchases to Zoho CRM Invoices