| | |
| | | |
| | | class GoogleMyBusiness extends Integrations |
| | | { |
| | | private string $access_token; |
| | | private ?string $access_token = null; |
| | | protected string $readMask = 'name,title,storefrontAddress,metadata,openInfo,storeCode,categories,phoneNumbers,labels,specialHours'; |
| | | private ?string $location = null; |
| | | private ?string $refresh_token = null; |
| | | private ?string $client_id = null; |
| | | private ?string $client_secret = null; |
| | | private ?string $account_id = null; |
| | | |
| | | public function __construct(?int $userID = null) |
| | |
| | | 'label' => 'OAuth Client Secret', |
| | | 'required' => true, |
| | | ], |
| | | 'access_token' => [ |
| | | 'type' => 'text', |
| | | 'subtype' => 'password', |
| | | 'label' => 'Access Token', |
| | | 'hint' => 'Generated automagically after OAuth authorization.' |
| | | ], |
| | | 'refresh_token' => [ |
| | | 'type' => 'text', |
| | | 'subtype' => 'password', |
| | | 'label' => 'Refresh Token', |
| | | 'hint' => 'Generated automagically after OAuth authorization.' |
| | | ] |
| | | // 'access_token' => [ |
| | | // 'type' => 'text', |
| | | // 'subtype' => 'password', |
| | | // 'label' => 'Access Token', |
| | | // 'hint' => 'Generated automagically after OAuth authorization.' |
| | | // ], |
| | | // 'refresh_token' => [ |
| | | // 'type' => 'text', |
| | | // 'subtype' => 'password', |
| | | // 'label' => 'Refresh Token', |
| | | // 'hint' => 'Generated automagically after OAuth authorization.' |
| | | // ] |
| | | ]; |
| | | |
| | | $this->advanced = [ |
| | |
| | | if (empty($this->credentials)) { |
| | | $this->loadCredentials(); |
| | | } |
| | | $this->access_token = $this->credentials['access_token'] ?? ''; |
| | | $this->refresh_token = $this->credentials['refresh_token'] ?? ''; |
| | | $this->client_id = $this->credentials['client_id'] ?? ''; |
| | | $this->client_secret = $this->credentials['client_secret'] ?? ''; |
| | | $this->location = $this->credentials['location'] ?? null; |
| | | $this->account_id = $this->credentials['account'] ?? null; |
| | | $this->access_token = (array_key_exists('access_token', $this->credentials)) ? $this->credentials['access_token'] : null; |
| | | $this->refresh_token = (array_key_exists('refresh_token', $this->credentials)) ? $this->credentials['refresh_token'] : null; |
| | | $this->client_id = (array_key_exists('client_id', $this->credentials)) ? $this->credentials['client_id'] : null; |
| | | $this->client_secret = (array_key_exists('client_secret', $this->credentials)) ? $this->credentials['client_secret'] : null; |
| | | $this->location = (array_key_exists('location', $this->credentials)) ? $this->credentials['location'] : null; |
| | | $this->account_id = (array_key_exists('account', $this->credentials)) ? $this->credentials['account'] : null; |
| | | |
| | | if ($this->account_id) { |
| | | $this->apiEndpoints[] = "/v1/{$this->account_id}/locations"; |
| | |
| | | $type = $settings['content_type']??'post'; //can be 'post', 'offer', 'event', 'hours', 'info', |
| | | $initial = $settings['initial']?? false; |
| | | $syncOnUpdate = $settings['update']??false; |
| | | error_log('Handling GMB Save Post with settings: '.print_r($settings, true)); |
| | | if ($update && !$syncOnUpdate) { |
| | | return; |
| | | } |
| | |
| | | return; |
| | | } |
| | | |
| | | error_log('Continuing on...'); |
| | | $options = $data = []; |
| | | switch ($type) { |
| | | case 'menu_item': |
| | |
| | | } |
| | | $data['post_id'] = $postID; |
| | | $operation = ($update) ? 'update_' : 'create_'; |
| | | error_log('[GMB]Queuing sync to service:'.print_r($data, true)); |
| | | error_log('Options: '.print_r($options, true)); |
| | | |
| | | $this->queueOperation( |
| | | $operation.$type, |
| | | $data, |
| | |
| | | // Build the complete menu structure |
| | | $menu_data = $gmb->collectMenu($menu_items); |
| | | |
| | | error_log('Menu Data: '.print_r($menu_data, true)); |
| | | |
| | | // Send to Google My Business API |
| | | $result = $this->updateFoodMenus($menu_data); |
| | | return [ |
| | |
| | | $locations = $this->getLocations($account['name']); |
| | | |
| | | foreach ($locations as $location) { |
| | | error_log('Fetched Location: '.print_r($location, true)); |
| | | if ($location['storeCode'] === $this->credentials['location']) { |
| | | // Auto-migrate: update stored location to use full resource name |
| | | $this->setSelectedLocation($location['name']); |
| | |
| | | |
| | | // Filter to only allowed fields |
| | | $patch_data = array_intersect_key($updates,$allowed_fields); |
| | | error_log('Updates: '.print_r($updates, true)); |
| | | |
| | | $location_name = $this->getSelectedLocationResourceName(); |
| | | if (empty($patch_data)) { |
| | |
| | | if(!$this->isSetUp()) { |
| | | return []; |
| | | } |
| | | error_log('[GMB] updateBusinessHours called with hours: ' . print_r($hours, true)); |
| | | $location_name = $this->credentials['location']; |
| | | |
| | | if (empty($location_name)) { |
| | |
| | | ] |
| | | ]; |
| | | |
| | | error_log('[GMB] Complete update data: ' . print_r($update_data, true)); |
| | | |
| | | $endpoint = "/v1/{$location_name}?updateMask=regularHours"; |
| | | error_log('[GMB] API endpoint: ' . $endpoint); |
| | | error_log('[GMB] Using API base: ' . 'base'); |
| | | |
| | | // Make the API request |
| | | $response = $this->makeRequest( |
| | |
| | | 'base' |
| | | ); |
| | | |
| | | error_log('[GMB] API response: ' . print_r($response, true)); |
| | | |
| | | $success = $response !== null; |
| | | error_log('[GMB] updateBusinessHours result: ' . ($success ? 'SUCCESS' : 'FAILED')); |
| | | |
| | | // Additional validation - check if the response contains the updated hours |
| | | if ($success && $response) { |
| | | if (isset($response['regularHours'])) { |
| | | error_log('[GMB] SUCCESS: Updated hours confirmed in response: ' . print_r($response['regularHours'], true)); |
| | | // error_log('[GMB] SUCCESS: Updated hours confirmed in response: ' . print_r($response['regularHours'], true)); |
| | | } else { |
| | | error_log('[GMB] WARNING: No regularHours in response, but API call succeeded'); |
| | | error_log('[GMB] Full response keys: ' . implode(', ', array_keys($response))); |
| | | // error_log('[GMB] WARNING: No regularHours in response, but API call succeeded'); |
| | | // error_log('[GMB] Full response keys: ' . implode(', ', array_keys($response))); |
| | | } |
| | | } |
| | | |
| | |
| | | ]; |
| | | |
| | | } catch (\Exception $e) { |
| | | error_log('[GMB] Exception in updateBusinessHours: ' . $e->getMessage()); |
| | | error_log('[GMB] Exception trace: ' . $e->getTraceAsString()); |
| | | |
| | | $this->logError($e->getMessage(), [ |
| | | 'method' => 'updateBusinessHours' |
| | | ]); |
| | |
| | | |
| | | private function validateAndFormatHours(array $hours): array |
| | | { |
| | | error_log('[GMB] Validating hours format...'); |
| | | |
| | | $valid_days = ['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY']; |
| | | $periods = []; |
| | | |
| | |
| | | continue; |
| | | } |
| | | |
| | | error_log('[GMB] Processing ' . $formatted_day . ' - times data: ' . print_r($times, true)); |
| | | |
| | | // Check if day is open with flexible comparison |
| | | $is_open = false; |
| | | if (isset($times['open'])) { |
| | |
| | | ]; |
| | | |
| | | $periods[] = $period; |
| | | error_log('[GMB] Valid period for ' . $formatted_day . ': ' . print_r($period, true)); |
| | | } |
| | | |
| | | error_log('[GMB] Total valid periods: ' . count($periods)); |
| | | return $periods; |
| | | } |
| | | |
| | |
| | | ] |
| | | ]; |
| | | |
| | | error_log('[GMB] Updating special hours with corrected structure: ' . json_encode($update_data, JSON_PRETTY_PRINT)); |
| | | |
| | | // Try the PATCH request |
| | | $response = $this->makeRequest( |
| | | 'PATCH', |
| | |
| | | ]; |
| | | |
| | | } catch (\Exception $e) { |
| | | error_log('[GMB] setSpecialHours Exception: ' . $e->getMessage()); |
| | | |
| | | $this->logError($e->getMessage(), [ |
| | | 'method' => 'setSpecialHours' |
| | | ]); |
| | |
| | | $ttl = 7 * 24 * 60 * 60; // week in seconds |
| | | $response = $this->getRequest('/v1/accounts', [], 'accounts', $ttl, $force)??[]; |
| | | |
| | | // Log the raw response for debugging |
| | | error_log('[GMB] Raw accounts response: ' . print_r($response, true)); |
| | | |
| | | if (isset($response['accounts']) && is_array($response['accounts'])) { |
| | | return $response['accounts']; |
| | | } |
| | |
| | | 'v4' |
| | | ); |
| | | |
| | | error_log('Review response: '.print_r($response, true)); |
| | | $reviews = $response ?? []; |
| | | |
| | | // Cache for 1 week (604800 seconds) |
| | |
| | | if (empty($account_name) || !str_starts_with($account_name, 'accounts/')) { |
| | | return []; |
| | | } |
| | | error_log('[GMB] getLocations() called for: ' . $account_name . ' (force: ' . ($force ? 'true' : 'false') . ')'); |
| | | $params = ['readMask' => $this->readMask]; |
| | | $ttl = 7 * 24 * 60 * 60; // Week in seconds |
| | | |
| | |
| | | */ |
| | | public function refreshStoredData(): array |
| | | { |
| | | error_log('[GMB] Manually refreshing accounts and locations data'); |
| | | |
| | | try { |
| | | // Fetch fresh accounts data from API |
| | | $accounts = $this->getAccounts(true); |
| | |
| | | ]; |
| | | $endpoint = "/v1/{$location_name}?".http_build_query($params); |
| | | |
| | | error_log('[GMB] Fetching location: ' . $location_name); |
| | | $location = $this->getRequest($endpoint, [], 'base', 'moderate', $force); |
| | | |
| | | return $location??null; |
| | |
| | | $text = preg_replace('/\s+/', ' ', $text); |
| | | |
| | | // Trim |
| | | $text = trim($text); |
| | | |
| | | return $text; |
| | | return trim($text); |
| | | } |
| | | /** |
| | | * Format date for GMB API |
| | |
| | | |
| | | // Validate hour and minute ranges |
| | | if ($hour >= 0 && $hour <= 23 && $minute >= 0 && $minute <= 59) { |
| | | $result = [ |
| | | return [ |
| | | 'hours' => $hour, |
| | | 'minutes' => $minute |
| | | ]; |
| | | |
| | | error_log('[GMB] Converted time "' . $time . '" to Google format: ' . print_r($result, true)); |
| | | return $result; |
| | | } |
| | | } |
| | | |
| | |
| | | [], |
| | | 'posts' |
| | | ); |
| | | $result = $response['foodMenus'] ?? []; |
| | | |
| | | return $result; |
| | | return $response['foodMenus'] ?? []; |
| | | } |
| | | |
| | | /** |
| | |
| | | public function handleGetAllLocations():WP_Error|array |
| | | { |
| | | try { |
| | | error_log('[GMB] AJAX getAllLocations called'); |
| | | |
| | | // Check if we should force refresh |
| | | $force = isset($_POST['force']) && $_POST['force'] === 'true'; |
| | | |
| | |
| | | $all_locations = array_merge($all_locations, $locations); |
| | | } |
| | | |
| | | error_log('[GMB] AJAX returning ' . count($all_locations) . ' locations'); |
| | | return [ |
| | | 'success' => true, |
| | | 'locations' => $all_locations, |
| | |
| | | ]; |
| | | |
| | | } catch (\Exception $e) { |
| | | error_log('[GMB] AJAX getAllLocations exception: ' . $e->getMessage()); |
| | | return new WP_Error('failure', 'Something went wrong: '.$e->getMessage()); |
| | | } |
| | | } |
| | |
| | | // Handle both AJAX and REST API calls |
| | | $selected_account = null; |
| | | |
| | | error_log('handle Update Location: '.print_r($data, true)); |
| | | if (is_array($data)) { |
| | | $selected_account = $data['account'] ?? $data['location'] ?? null; |
| | | } elseif (isset($_POST['account'])) { |
| | | $selected_account = sanitize_text_field($_POST['account']); |
| | | } |
| | | |
| | | error_log('[GMB] updateLocation received: ' . print_r($selected_account, true)); |
| | | |
| | | if (empty($selected_account)) { |
| | | throw new \Exception('No account selected'); |
| | | } |
| | |
| | | { |
| | | try { |
| | | // Use the static method to clear the entire cache group |
| | | CacheManager::invalidateGroup('integrations_'.$this->cacheName); |
| | | |
| | | error_log('[GMB] Cleared all stored data for cache group: ' . $this->cacheName); |
| | | $this->cache->clear(); |
| | | return true; |
| | | |
| | | } catch (\Exception $e) { |
| | |
| | | 'dailyRange.endDate.day' => date('j', strtotime($end_date)) |
| | | ]; |
| | | |
| | | $response = $this->getRequest( |
| | | "/v1/{$location_name}:fetchMultiDailyMetricsTimeSeries?" . http_build_query($params), |
| | | $params, |
| | | 'performance' |
| | | ); |
| | | return $response; |
| | | return $this->getRequest( |
| | | "/v1/{$location_name}:fetchMultiDailyMetricsTimeSeries?" . http_build_query($params), |
| | | $params, |
| | | 'performance' |
| | | ); |
| | | } |
| | | |
| | | /** |
| | |
| | | ] |
| | | ]; |
| | | } |
| | | |
| | | |
| | | } |