upskill-event-manager/includes/google-sheets/class-google-sheets-auth.php
bengizmo 37f4180e1c feat: Add massive missing plugin infrastructure to repository
🚨 CRITICAL: Fixed deployment blockers by adding missing core directories:

**Community System (CRITICAL)**
- includes/community/ - Login_Handler and all community classes
- templates/community/ - Community login forms

**Certificate System (CRITICAL)**
- includes/certificates/ - 8+ certificate classes and handlers
- templates/certificates/ - Certificate reports and generation templates

**Core Individual Classes (CRITICAL)**
- includes/class-hvac-event-summary.php
- includes/class-hvac-trainer-profile-manager.php
- includes/class-hvac-master-dashboard-data.php
- Plus 40+ other individual HVAC classes

**Major Feature Systems (HIGH)**
- includes/database/ - Training leads database tables
- includes/find-trainer/ - Find trainer directory and MapGeo integration
- includes/google-sheets/ - Google Sheets integration system
- includes/zoho/ - Complete Zoho CRM integration
- includes/communication/ - Communication templates system

**Template Infrastructure**
- templates/attendee/, templates/email-attendees/
- templates/event-summary/, templates/status/
- templates/template-parts/ - Shared template components

**Impact:**
- 70+ files added covering 10+ missing directories
- Resolves ALL deployment blockers and feature breakdowns
- Plugin activation should now work correctly
- Multi-machine deployment fully supported

🔧 Generated with Claude Code

Co-Authored-By: Ben Reed <ben@tealmaker.com>
2025-08-11 13:30:11 -03:00

435 lines
No EOL
16 KiB
PHP

<?php
/**
* Google Sheets Authentication Handler
*
* Handles OAuth token management and Google Sheets API authentication
*
* @package HVAC_Community_Events
* @subpackage Google_Sheets_Integration
*/
if (!defined('ABSPATH')) {
exit;
}
class HVAC_Google_Sheets_Auth {
private $client_id;
private $client_secret;
private $refresh_token;
private $redirect_uri;
private $access_token;
private $token_expiry;
private $folder_id;
private $last_error = null;
// Google API endpoints
private $auth_url = 'https://accounts.google.com/o/oauth2/v2/auth';
private $token_url = 'https://oauth2.googleapis.com/token';
private $sheets_api_url = 'https://sheets.googleapis.com/v4/spreadsheets';
private $drive_api_url = 'https://www.googleapis.com/drive/v3/files';
public function __construct() {
// Load configuration if available
$config_file = plugin_dir_path(__FILE__) . 'google-sheets-config.php';
if (file_exists($config_file)) {
require_once $config_file;
$this->client_id = defined('GOOGLE_SHEETS_CLIENT_ID') ? GOOGLE_SHEETS_CLIENT_ID : '';
$this->client_secret = defined('GOOGLE_SHEETS_CLIENT_SECRET') ? GOOGLE_SHEETS_CLIENT_SECRET : '';
$this->refresh_token = defined('GOOGLE_SHEETS_REFRESH_TOKEN') ? GOOGLE_SHEETS_REFRESH_TOKEN : '';
$this->redirect_uri = defined('GOOGLE_SHEETS_REDIRECT_URI') ? GOOGLE_SHEETS_REDIRECT_URI : 'http://localhost:8080/callback';
$this->folder_id = defined('GOOGLE_SHEETS_FOLDER_ID') ? GOOGLE_SHEETS_FOLDER_ID : '';
}
// Load stored access token from WordPress options
$this->load_access_token();
// Register callback handler - use template_redirect to catch it before page rendering
add_action('template_redirect', array($this, 'handle_oauth_callback'));
}
/**
* Generate authorization URL for initial setup
*/
public function get_authorization_url() {
$params = array(
'client_id' => $this->client_id,
'redirect_uri' => $this->redirect_uri,
'scope' => 'https://www.googleapis.com/auth/spreadsheets https://www.googleapis.com/auth/drive.file',
'response_type' => 'code',
'access_type' => 'offline',
'prompt' => 'consent',
'include_granted_scopes' => 'true'
);
return $this->auth_url . '?' . http_build_query($params);
}
/**
* Exchange authorization code for tokens
*/
public function exchange_code_for_tokens($auth_code) {
$params = array(
'client_id' => $this->client_id,
'client_secret' => $this->client_secret,
'redirect_uri' => $this->redirect_uri,
'grant_type' => 'authorization_code',
'code' => $auth_code
);
if (class_exists('HVAC_Logger')) {
HVAC_Logger::info("Token exchange request params: " . json_encode(array(
'client_id' => substr($this->client_id, 0, 20) . '...',
'redirect_uri' => $this->redirect_uri,
'grant_type' => 'authorization_code',
'code' => substr($auth_code, 0, 20) . '...'
)), 'GoogleSheets');
}
$response = wp_remote_post($this->token_url, array(
'body' => $params,
'headers' => array(
'Content-Type' => 'application/x-www-form-urlencoded'
),
'timeout' => 30
));
if (is_wp_error($response)) {
$this->log_error('Failed to exchange code: ' . $response->get_error_message());
return false;
}
$response_code = wp_remote_retrieve_response_code($response);
$body = wp_remote_retrieve_body($response);
if (class_exists('HVAC_Logger')) {
HVAC_Logger::info("Token exchange response code: " . $response_code, 'GoogleSheets');
HVAC_Logger::info("Token exchange response body: " . $body, 'GoogleSheets');
}
$data = json_decode($body, true);
if (isset($data['access_token'])) {
$this->access_token = $data['access_token'];
if (isset($data['refresh_token'])) {
$this->refresh_token = $data['refresh_token'];
}
$this->token_expiry = time() + (int)$data['expires_in'];
if (class_exists('HVAC_Logger')) {
HVAC_Logger::info("Successfully received tokens. Access token: " . substr($this->access_token, 0, 20) . "...", 'GoogleSheets');
HVAC_Logger::info("Refresh token: " . ($this->refresh_token ? 'RECEIVED' : 'NOT RECEIVED'), 'GoogleSheets');
HVAC_Logger::info("Token expires at: " . date('Y-m-d H:i:s', $this->token_expiry), 'GoogleSheets');
}
// Save tokens
$this->save_tokens();
return true;
}
$this->log_error('Invalid token response (status ' . $response_code . '): ' . $body);
return false;
}
/**
* Get valid access token (refresh if needed)
*/
public function get_access_token() {
// Check if token is expired or will expire in next 5 minutes
if ($this->token_expiry && ($this->token_expiry - 300) < time()) {
$this->refresh_access_token();
}
return $this->access_token;
}
/**
* Refresh access token using refresh token
*/
private function refresh_access_token() {
if (empty($this->refresh_token)) {
$this->log_error('No refresh token available');
return false;
}
$params = array(
'client_id' => $this->client_id,
'client_secret' => $this->client_secret,
'refresh_token' => $this->refresh_token,
'grant_type' => 'refresh_token'
);
$response = wp_remote_post($this->token_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'];
// Update refresh token if provided
if (isset($data['refresh_token'])) {
$this->refresh_token = $data['refresh_token'];
}
// Save updated tokens
$this->save_tokens();
return true;
}
$this->log_error('Failed to refresh token: ' . $body);
return false;
}
/**
* Make authenticated API request to Google Sheets/Drive
*/
public function make_api_request($method, $endpoint, $data = null, $api_type = 'sheets') {
$access_token = $this->get_access_token();
if (!$access_token) {
throw new Exception('No valid access token available');
}
$base_url = ($api_type === 'drive') ? $this->drive_api_url : $this->sheets_api_url;
$url = $base_url . $endpoint;
// Handle valueInputOption as query parameter for Sheets API
if ($data && isset($data['valueInputOption'])) {
$url .= '?valueInputOption=' . urlencode($data['valueInputOption']);
unset($data['valueInputOption']); // Remove from body data
}
$args = array(
'method' => $method,
'headers' => array(
'Authorization' => 'Bearer ' . $access_token,
'Content-Type' => 'application/json'
),
'timeout' => 30
);
if ($data && in_array($method, ['POST', 'PUT', 'PATCH'])) {
$args['body'] = json_encode($data);
}
$response = wp_remote_request($url, $args);
if (is_wp_error($response)) {
throw new Exception('API request failed: ' . $response->get_error_message());
}
$response_code = wp_remote_retrieve_response_code($response);
$body = wp_remote_retrieve_body($response);
if ($response_code >= 400) {
$error_data = json_decode($body, true);
$error_message = isset($error_data['error']['message']) ? $error_data['error']['message'] : 'Unknown API error';
throw new Exception("API error {$response_code}: {$error_message}");
}
return json_decode($body, true);
}
/**
* Test API connection
*/
public function test_connection() {
try {
// Try to create a test spreadsheet in the designated folder
$spreadsheet_data = array(
'properties' => array(
'title' => 'HVAC Test Connection - ' . date('Y-m-d H:i:s')
)
);
$response = $this->make_api_request('POST', '', $spreadsheet_data);
if (isset($response['spreadsheetId'])) {
// Move to designated folder if specified
if ($this->folder_id) {
$this->make_api_request(
'PATCH',
'/' . $response['spreadsheetId'] . '?addParents=' . $this->folder_id,
null,
'drive'
);
}
// Delete test spreadsheet
$this->make_api_request(
'DELETE',
'/' . $response['spreadsheetId'],
null,
'drive'
);
return array('success' => true, 'message' => 'Connection successful');
}
return array('success' => false, 'message' => 'Unexpected response format');
} catch (Exception $e) {
return array('success' => false, 'message' => $e->getMessage());
}
}
/**
* Load access token from WordPress options
*/
private function load_access_token() {
$token_data = get_option('hvac_google_sheets_tokens', array());
if (!empty($token_data)) {
$this->access_token = $token_data['access_token'] ?? '';
$this->refresh_token = $token_data['refresh_token'] ?? $this->refresh_token;
$this->token_expiry = $token_data['expires_at'] ?? 0;
}
}
/**
* Save tokens to WordPress options
*/
private function save_tokens() {
$token_data = array(
'access_token' => $this->access_token,
'refresh_token' => $this->refresh_token,
'expires_at' => $this->token_expiry,
'created_at' => time()
);
$result = update_option('hvac_google_sheets_tokens', $token_data);
if (class_exists('HVAC_Logger')) {
HVAC_Logger::info("Saving tokens to database: " . ($result ? 'SUCCESS' : 'FAILED'), 'GoogleSheets');
HVAC_Logger::info("Token data: " . json_encode(array(
'access_token' => substr($this->access_token, 0, 20) . '...',
'refresh_token' => $this->refresh_token ? 'SET' : 'NOT SET',
'expires_at' => date('Y-m-d H:i:s', $this->token_expiry),
'created_at' => date('Y-m-d H:i:s', time())
)), 'GoogleSheets');
}
}
/**
* Clear stored tokens
*/
public function clear_tokens() {
delete_option('hvac_google_sheets_tokens');
$this->access_token = '';
$this->refresh_token = '';
$this->token_expiry = 0;
}
/**
* Check if we have valid credentials
*/
public function has_valid_credentials() {
return !empty($this->client_id) && !empty($this->client_secret);
}
/**
* Check if we have an access token
*/
public function is_authenticated() {
return !empty($this->access_token) || !empty($this->refresh_token);
}
/**
* Get last error message
*/
public function get_last_error() {
return $this->last_error;
}
/**
* Log error message
*/
private function log_error($message) {
$this->last_error = $message;
if (class_exists('HVAC_Logger')) {
HVAC_Logger::error("Google Sheets Auth: {$message}", 'GoogleSheets');
}
error_log("HVAC Google Sheets Auth Error: {$message}");
}
/**
* Get configuration status
*/
public function get_config_status() {
return array(
'has_credentials' => $this->has_valid_credentials(),
'is_authenticated' => $this->is_authenticated(),
'client_id' => !empty($this->client_id) ? substr($this->client_id, 0, 10) . '...' : '',
'has_refresh_token' => !empty($this->refresh_token),
'token_expires' => $this->token_expiry ? date('Y-m-d H:i:s', $this->token_expiry) : 'Unknown',
'folder_id' => $this->folder_id
);
}
/**
* Handle OAuth callback from Google
*/
public function handle_oauth_callback() {
// Debug: Log all OAuth callback attempts
if (isset($_GET['code']) && isset($_GET['scope'])) {
if (class_exists('HVAC_Logger')) {
HVAC_Logger::info("OAuth callback detected - URI: " . $_SERVER['REQUEST_URI'], 'GoogleSheets');
HVAC_Logger::info("OAuth callback - code param: " . substr($_GET['code'], 0, 20) . "...", 'GoogleSheets');
}
error_log("HVAC Google OAuth callback detected - URI: " . $_SERVER['REQUEST_URI']);
error_log("HVAC Google OAuth callback - code: " . substr($_GET['code'], 0, 20) . "...");
}
// Check if this is an OAuth callback request to the Google Sheets page
if (isset($_GET['code']) && isset($_GET['scope']) &&
(strpos($_SERVER['REQUEST_URI'], '/google-sheets/') !== false ||
strpos($_SERVER['REQUEST_URI'], 'google-sheets') !== false)) {
$auth_code = sanitize_text_field($_GET['code']);
if (class_exists('HVAC_Logger')) {
HVAC_Logger::info("Processing OAuth callback with code: " . substr($auth_code, 0, 20) . "...", 'GoogleSheets');
HVAC_Logger::info("Current redirect URI: " . $this->redirect_uri, 'GoogleSheets');
}
// Exchange the authorization code for tokens
$success = $this->exchange_code_for_tokens($auth_code);
if (class_exists('HVAC_Logger')) {
HVAC_Logger::info("Token exchange result: " . ($success ? 'SUCCESS' : 'FAILED'), 'GoogleSheets');
if (!$success) {
HVAC_Logger::error("Token exchange error: " . $this->get_last_error(), 'GoogleSheets');
}
}
if ($success) {
// Redirect to Google Sheets admin page with success message (clean URL)
wp_redirect(add_query_arg(array(
'auth_success' => '1'
), home_url('/google-sheets/')));
exit;
} else {
// Redirect to Google Sheets admin page with error message
wp_redirect(add_query_arg(array(
'auth_error' => '1',
'message' => urlencode($this->get_last_error())
), home_url('/google-sheets/')));
exit;
}
}
}
}