From ccf5e61a075c35d24befc175994b5cb8957ca322 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 26 Aug 2026 21:45:53 +0000
Subject: [PATCH] =Extra JVB() calls replaced, minor tweaks

---
 inc/integrations/services/GoogleMyBusiness.php |  361 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 355 insertions(+), 6 deletions(-)

diff --git a/inc/integrations/services/GoogleMyBusiness.php b/inc/integrations/services/GoogleMyBusiness.php
index 25e98bb..8d374ea 100644
--- a/inc/integrations/services/GoogleMyBusiness.php
+++ b/inc/integrations/services/GoogleMyBusiness.php
@@ -9,7 +9,9 @@
 use JVBase\integrations\OAuthURLs;
 use JVBase\integrations\Requests;
 use JVBase\integrations\SyncTo;
+use JVBase\integrations\SyncToPost;
 use JVBase\integrations\UserConnection;
+use WP_Error;
 
 
 if (!defined('ABSPATH')) {
@@ -18,10 +20,10 @@
 
 class GoogleMyBusiness extends Integrations
 {
-	use Admin, Instance, OAuth, Requests, UserConnection, SyncTo;
+	use Admin, Instance, OAuth, Requests, UserConnection, SyncToPost;
 
 	protected string $readMask = 'name,title,storefrontAddress,metadata,openInfo,storeCode,categories,phoneNumbers,labels,specialHours';
-
+	protected string $businessV4;
 	protected function __construct()
 	{
 		$this->service_name = 'gmb';
@@ -35,6 +37,9 @@
 		];
 		$this->supportsWebp = false;
 
+		$this->baseUrl = 'https://mybusinessbusinessinformation.googleapis.com';
+		$this->businessV4 = 'https://mybusiness.googleapis.com/v4';
+
 		$this->defineFields();
 		$this->defineInstructions();
 		$this->defineOAuth();
@@ -140,7 +145,12 @@
 
 	protected function getRequestHeaders(): array
 	{
-		// TODO: Implement getRequestHeaders() method.
+
+		return [
+			'Authorization' => 'Bearer ' . $this->getAccessToken(),
+			'Content-Type' => 'application/json',
+			'Accept' => 'application/json'
+		];
 	}
 
 	protected function handleResponse(\WP_Error|array $response, string $method, string $endpoint): array
@@ -151,14 +161,16 @@
 
 		return match($endpoint) {
 			'https://mybusinessaccountmanagement.googleapis.com/v1/accounts' => $this->handleGetAccountsResponse($response, $method),
+			sprintf('%s/%s/%s/reviews', $this->businessV4, $this->getAccountID()??'', $this->getLocationID()??'') => $this->handleGetReviewResponse($response, $method),
 			sprintf('https://mybusinessbusinessinformation.googleapis.com/v1/%s/locations', $this->getAccountID()??'') => $this->handleGetLocationsResponse($response, $method),
+			sprintf('%s/v1/%s', $this->baseUrl,$this->getLocationID()??'') => $this->handleGetLocationResponse($response, $method),
 			default => $this->response(false, 'Unhandled response for '.$endpoint.' endpoint, with method: '.$method)
 		};
 	}
 
 	protected function refreshAccessToken(OAuthCredentials $credentials): array
 	{
-		// TODO: Implement refreshAccessToken() method.
+
 	}
 
 	/*********************************************************
@@ -177,6 +189,31 @@
 		return $stored;
 	}
 
+	protected function getLocation(bool $force = false):?array
+	{
+		$location_name = $this->getLocationID();
+		if (empty($location_name)) {
+			return null;
+		}
+		$params = [
+			'readMask'	=> $this->readMask
+		];
+
+		return $this->getRequest(
+			sprintf(
+				'%s/v1/%s',
+				$this->baseUrl,
+				$location_name,
+			),
+			$params,
+			$force
+		);
+
+	}
+		protected function handleGetLocationResponse(WP_Error|array $response, string $method):?array
+		{
+			return $response??null;
+		}
 	protected function getLocationID():string|false
 	{
 		return $this->loadCredentials()['location_id']??false;
@@ -254,11 +291,323 @@
 
 	protected function setContentTypes(): void
 	{
-		// TODO: Implement setContentTypes() method.
+		$this->contentTypes = [
+			'menu_item'	=> $this->buildMenuItemContentType(),
+			'post'		=> $this->buildPostContentType(),
+			'event'		=> $this->buildEventContentType(),
+			'offer'		=> $this->buildOfferContentType(),
+		];
 	}
-
+		protected function buildMenuItemContentType():array
+		{
+			return [
+				'nutritionFacts'	=> [
+					'type'	=> 'repeater',
+					'label'	=> 'Nutrition',
+					'fields'	=> [
+						'name'	=> [
+							'type'	=> 'select',
+							'label'	=> 'Name',
+							'options'	=> [
+								'calories' => 'Calories',
+								'totalFat'	=> 'Total Fat',
+								'cholesterol' => 'Cholesterol',
+								'sodium'	=> 'Sodium',
+								'totalCarbohydrate' => 'Total Carbohydrates',
+								'protein'	=> 'Protein'
+							]
+						],
+						'amount'	=> [
+							'type'	=> 'number',
+							'label'	=> 'Amount',
+						],
+						'unit'	=> [
+							'type' => 'select',
+							'label'	=> 'Unit',
+							'options'	=> [
+								'MILLIGRAM' => 'Milligram',
+								'GRAM'	=> 'Gram',
+								'CALORIE'	=> 'Calorie'
+							]
+						]
+					],
+					'description'	=> 'Recommended. Example: Total Fat: 3g',
+					'section'	=> 'gmb',
+				],
+				'servesNumPeople'	=> [
+					'type'	=> 'number',
+					'label'	=> 'Number of People this Serves',
+					'bulkEdit'	=> true,
+					'section'	=> 'gmb',
+				],
+				'portionSize'	=> [
+					'type'	=> 'text',
+					'label'	=> 'Portion size',
+					'description' => 'Example: 8-piece of nuggets',
+					'bulkEdit'	=> true,
+					'section'	=> 'gmb',
+				],
+				'preparationMethods' => [
+					'type'	=> 'textarea',
+					'label'	=> 'Preparation Methods',
+					'description'	=> 'Specific methods that the food can be prepared in',
+					'bulkEdit'	=> true,
+					'section'	=> 'gmb',
+				],
+				'cuisines'	=> [
+					'type'	=> 'text',
+					'label'	=> 'Cuisines',
+					'description'	=> 'Recommended. The specific cuisine of the food item',
+					'bulkEdit'	=> true,
+					'section'	=> 'gmb',
+				],
+				'spiciness'	=> [
+					'type'	=> 'select',
+					'label'	=> 'Spiciness',
+					'options'	=> [
+						'none'	=> 'None',
+						'mild'	=> 'Mild',
+						'medium'	=> 'Medium',
+						'hot'	=> 'Hot',
+					],
+					'bulkEdit'	=> true,
+					'section'	=> 'gmb',
+				],
+				'allergen'	=> [
+					'type'	=> 'checkbox',
+					'label'	=> 'Allergens',
+					'options'	=> [
+						'dairy'	=> 'Dairy',
+						'egg'	=> 'Egg',
+						'fish'	=> 'Fish',
+						'peanut'	=> 'Peanut',
+						'shellfish'	=> 'Shellfish',
+						'soy'		=> 'Soy',
+						'tree nut'	=> 'Tree Nut',
+						'wheat'		=> 'Wheat'
+					],
+					'description'	=> 'Recommended',
+					'bulkEdit'	=> true,
+					'section'	=> 'gmb',
+				],
+				'dietaryRestriction'	=> [
+					'type'	=> 'checkbox',
+					'label'	=> 'Dietary Restrictions',
+					'description' => 'Recommended.',
+					'options'	=> [
+						'halal' => 'Halal',
+						'kosher'	=> 'Kosher',
+						'organic'	=> 'Organic',
+						'vegan'		=> 'Vegan',
+						'vegetarian'	=> 'Vegetarian',
+						'gluten-free'	=> 'Gluten Free'
+					],
+					'bulkEdit'	=> true,
+					'section'	=> 'gmb',
+				]
+			];
+		}
+		protected function buildPostContentType():array
+		{
+			return [
+				'summary' => [
+					'type' => 'textarea',
+					'label' => 'Post Summary',
+					'required' => true
+				],
+				'language_code' => [
+					'type' => 'text',
+					'label' => 'Language',
+					'default' => 'en-CA'
+				],
+				'media' => [
+					'type' => 'image',
+					'label' => 'Photo',
+				],
+				'call_to_action' => [
+					'type' => 'group',
+					'fields' => [
+						'type' => [
+							'type' => 'select',
+							'options' =>
+								[
+									'BOOK' => 'Book Now',
+									'ORDER' => 'Order Now',
+									'SHOP' => 'Shop Now',
+									'LEARN_MORE' => 'Learn More',
+									'SIGN_UP' => 'Sign up',
+									'CALL' => 'Call'
+								]
+						],
+						'url' => [
+							'type' => 'url',
+							'label' => 'Action URL'
+						]
+					]
+				]
+			];
+		}
+		protected function buildEventContentType():array
+		{
+			return [
+				'summary' => [
+					'type' => 'textarea',
+					'label' => 'Event Description',
+					'required' => true
+				],
+				'event' => [
+					'title' => [
+						'type' => 'text',
+						'label' => 'Event Title',
+						'required' => true
+					],
+					'start_date' => [
+						'type' => 'date',
+						'label' => 'Start Date',
+						'required' => true
+					],
+					'end_date' => [
+						'type' => 'date',
+						'label' => 'End Date'
+					],
+					'start_time' => [
+						'type' => 'time',
+						'label' => 'Start Time'
+					],
+					'end_time' => [
+						'type' => 'time',
+						'label' => 'End Time'
+					]
+				],
+				'media' => [
+					'type' => 'image',
+					'label' => 'Event Photo'
+				]
+			];
+		}
+		protected function buildOfferContentType():array
+		{
+			return [
+				'summary' => [
+					'type' => 'textarea',
+					'label' => 'Offer Description',
+					'required' => true
+				],
+				'offer' => [
+					'coupon_code' => [
+						'type' => 'text',
+						'label' => 'Coupon Code'
+					],
+					'redeem_online_url' => [
+						'type' => 'url',
+						'label' => 'Redemption URL'
+					],
+					'terms_conditions' => [
+						'type' => 'textarea',
+						'label' => 'Terms & Conditions']
+				],
+				'media' => ['type' => 'image', 'label' => 'Offer Photo']
+			];
+		}
 	protected function formatForService(int $itemID, string $type = 'post'): array
 	{
 		// TODO: Implement formatForService() method.
 	}
+
+
+	public function getReviews(int $number = 5):array
+	{
+		if (!$this->getLocationID() || !$this->getAccountID()){
+			return [];
+		}
+		return $this->cache->remember(
+			['reviews', $this->getLocationID(), $number],
+			function() use ($number) {
+				try {
+					return $this->getRequest(
+						sprintf(
+							"%s/{$this->getAccountID()}/{$this->getLocationID()}/reviews",
+							$this->businessV4
+						),
+						[
+							'orderBy'	=> 'updateTime desc'
+						],
+					);
+				} catch (Exception $e) {
+					$this->logError(
+						'getReviews',
+					'Failed to fetch Reviews',
+					[
+						'error'	=> $e->getMessage()
+					]);
+					return [];
+				}
+			}
+		);
+	}
+		protected function handleGetReviewResponse(WP_Error|array $response, string $method):array
+		{
+			return $response;
+		}
+
+		/**********************************************************************
+		 * REVIEW UTILITY
+		**********************************************************************/
+		/**
+		 * Get the URL to view all Google reviews for the current location
+		 * @return string|null The reviews viewing URL or null if not available
+		 */
+		public function getReviewsViewUrl():?string
+		{
+			$location = $this->getLocation();
+			if (!$location) {
+				return null;
+			}
+
+			if (!empty($location['metadata']['mapsUrl']??'')) {
+				return $location['metadata']['mapsUrl'];
+			}
+
+			if (!empty($location['metadata']['placeId']??'')) {
+				return sprintf(
+					'https://search.google.com/local/reviews?placeid=%s',
+					urlencode($location['metadata']['placeId'])
+				);
+			}
+
+			return null;
+		}
+
+	/**
+	 * Get the URL to leave a review for the current location
+	 * @return string|null The review URL or null if not available
+	 */
+		public function getReviewUrl():?string
+		{
+			$location = $this->getLocation();
+			if (!$location) {
+				return null;
+			}
+
+			if (!empty($location['metadata']['placeId']??'')) {
+				return sprintf(
+					'https://search.google.com/local/writereview?placeid=%s',
+					urlencode($location['metadata']['placeId'])
+				);
+			}
+
+			if (!empty($location['metadata']['mapsUrl']??'')) {
+				return sprintf(
+					'%s/reviews',
+					$location['metadata']['mapsUrl']
+				);
+			}
+
+			return null;
+		}
+
+
+	/**************************************************************************
+	 * FOOD MENU HELPERS
+	**************************************************************************/
 }

--
Gitblit v1.10.0