From de317675a8069b747cb253ba3e2b5dc394ca36ef Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sat, 18 Jul 2026 21:44:51 +0000
Subject: [PATCH] =Base Integrations mainly done - doing a test with setting up the Square integration to follow

---
 inc/integrations/Integrations.php | 1656 +----------------------------------------------------------
 1 files changed, 32 insertions(+), 1,624 deletions(-)

diff --git a/inc/integrations/Integrations.php b/inc/integrations/Integrations.php
index dd8ede2..f9a34ef 100644
--- a/inc/integrations/Integrations.php
+++ b/inc/integrations/Integrations.php
@@ -3,17 +3,9 @@
 
 use Exception;
 use JVBase\managers\Cache;
-use JVBase\managers\ErrorHandler;
-use JVBase\managers\queue\executors\IntegrationExecutor;
-use JVBase\managers\queue\mergers\DefaultMerger;
-use JVBase\managers\queue\TypeConfig;
 use JVBase\managers\UploadManager;
-use JVBase\meta\Form;
-use JVBase\meta\Meta;
-use JVBase\registrar\helpers\AddIntegrationFields;
 use JVBase\registrar\Registrar;
 use WP_Error;
-use WP_Post;
 
 if (!defined('ABSPATH')) {
 	exit;
@@ -32,13 +24,6 @@
 {
 	use _Base;
 
-	//Flag to allow for custom settings (defaults, etc) for an integration in the dashboard
-	public bool $hasExtraOptions = false;
-	public bool $hasBatchCreate = false;
-	public bool $hasBatchUpdate = false;
-	public bool $hasBatchDelete = false;
-	public bool $canCreateOnUpdate = false;
-
 	/**
 	 * API Configuration
 	 * These properties define how the integration connects to external services
@@ -52,16 +37,11 @@
 	/**
 	 * Credentials & State
 	 */
-
-	protected string $defaultContent = 'post'; //Default integration content type, is none is set. MUST EXIST as array key in $this->>getContentTypes
-
 	protected array $allowedContent = [];
 
 	/**
 	 * Caching Configuration
 	 */
-	protected ?string $cacheName = null;
-	protected Cache $cache;
 	protected array $cacheStrategy = [
 		'aggressive' => 3600,  // 1 hour for stable data (e.g., profile info)
 		'moderate' => 300,     // 5 minutes for semi-dynamic data (e.g., posts)
@@ -69,26 +49,6 @@
 		'none' => 0           // No caching for real-time data
 	];
 
-	/**
-	 * Post Syncing Capabilities
-	 * Define what sync operations this integration supports
-	 */
-	protected array $canSync = [
-		'initial' => false,    // Can share new posts to the service
-		'update' => false,     // Can update already-shared posts
-		'delete' => false,     // Can remove posts from the service
-	];
-	protected array $syncPostTypes = []; // Post types that can be synced (e.g., ['artwork', 'tattoo']): usually built by Registrar.php if the integration name exists as a key in [ 'integrations' => []]
-	protected array $syncTaxonomies = []; // Post types that can be synced (e.g., ['artwork', 'tattoo']): usually built by Registrar.php if the integration name exists as a key in [ 'integrations' => []]
-	protected array $contentTypes = [];   // Integration's available content types. Set by child classes' getContentTypes
-	protected bool $has_content = false; // Whether integration has content that can sync
-	/**
-	 * Error Handling Configuration
-	 */
-	protected array $lastError = [];
-	protected array $retryDelays = [1, 2, 5]; // Exponential backoff in seconds
-
-
 
 	protected function __construct(?int $userID = null)
 	{
@@ -98,7 +58,11 @@
 
 		$this->getPostTypes();
 		$this->getTaxonomies();
-		$this->setContentTypes();
+		$this->getUsers();
+
+		if (method_exists($this, 'setContentTypes')) {
+			$this->setContentTypes();
+		}
 		$this->registerHooks();
 
 		if (method_exists($this, 'setQueueTypes')) {
@@ -106,193 +70,30 @@
 		}
 
 		$this->initializeRateLimiters();
+
 		if (method_exists($this, 'initializeActions')) {
 			$this->initializeActions();
 		}
 		if (method_exists($this, 'addOAuthActions')) {
 			$this->addOAuthActions();
 		}
-
-		add_filter('jvbShouldRenderMeta', [$this, 'checkRenderField'], 10, 4);
-	}
-		protected function initializeRateLimiters():void
-		{
-			$key = $this->service_name;
-			if (!is_null($this->userID)) {
-				$key .= '_'.$this->userID;
-			}
-
-			if ($this->hasRequests && !isset($this->requestLimiter) && !is_null($this->oauthLimiter)) {
-				$this->requestLimiter = new RateLimits($key);
-			}
-			if ($this->hasOAuth && !isset($this->oauthLimiter) && !is_null($this->oauthLimiter)) {
-				$key .= '_oauth';
-				$this->oauthLimiter = new RateLimits($key, ['s' => 1,'m' => 10, 'h' => 100]);
-			}
-		}
-
-
-	protected function setContentTypes():void
-	{
-
-	}
-	public function getContentTypes(string $type):array
-	{
-		return (array_key_exists($type, $this->contentTypes)) ? $this->contentTypes[$type] : $this->contentTypes;
-	}
-
-	public function checkRenderField($shouldRender, $name, $type, $objectType):bool
-	{
-		if ($type !== 'form') {
-			return $shouldRender;
-		}
-
-		if (!$this->isSetUp() && str_contains($name, $this->service_name)) {
-			return false;
-		}
-		return $shouldRender;
-	}
-
-	public function handleOAuthConnect(): array
-	{
-		if (!$this->hasOAuth) {
-			return ['success' => false, 'message' => 'Not an OAuth service'];
-		}
-
-		// This would typically redirect to OAuth provider
-		// Child classes can override for specific behavior
-		$auth_url = $this->getOAuthUrl();
-
-		if ($auth_url) {
-			return [
-				'success' => true,
-				'redirect' => $auth_url,
-			];
-		}
-
-		return ['success' => false, 'message' => 'Failed to generate OAuth URL'];
-	}
-
-	/**
-	 * Check if OAuth token is valid and not expired
-	 * @return bool
-	 */
-	public function isOAuthValid(): bool
-	{
-		if (!$this->isOAuthService) {
-			return false;
-		}
-
-		// Check if we have tokens
-		if (empty($this->credentials['access_token'])) {
-			return false;
-		}
-
-		// Check token expiry if stored
-		if (!empty($this->credentials['expires_at'])) {
-			$expires_at = intval($this->credentials['expires_at']);
-			if ($expires_at <= time()) {
-				return false;
-			}
-		}
-
-		// For services without expiry info, do a test API call
-		return true;
-//		return $this->testConnection();
-	}
-
-
-
-	public function getOAuthUrlAction(array $data = []): array
-	{
-		if (!$this->isOAuthService) {
-			return ['success' => false, 'message' => 'Not an OAuth service'];
-		}
-
-		$return_url = $data['return_url'] ?? null;
-		$auth_url = $this->getOAuthUrl($return_url);
-
-		if ($auth_url) {
-			return [
-				'success' => true,
-				'auth_url' => $auth_url,
-				'popup' => true
-			];
-		}
-
-		return ['success' => false, 'message' => 'Failed to generate OAuth URL'];
 	}
 
 	protected function getPostTypes(): void
 	{
-		$this->syncPostTypes = Registrar::withIntegration($this->service_name);
+		$this->syncPostTypes = Registrar::withIntegration($this->service_name, 'post');
+	}
+
+	protected function getUsers():void
+	{
+		$this->syncUsers = Registrar::withIntegration($this->service_name, 'user');
 	}
 
 	protected function getTaxonomies():void
 	{
-		$key = BASE . $this->service_name . '_sync_taxonomies';
-		$taxonomies = get_option($key, false);
-
-		if (!$taxonomies) {
-			// Combine both content and taxonomy filtering
-			$taxonomies = [];
-			foreach (Registrar::withFeature('is_content', 'term') as $type) {
-				$registrar = Registrar::getInstance($type);
-				if ($registrar->hasIntegration($this->service_name)) {
-					$taxonomies[] = $registrar->getSlug();
-				}
-			}
-
-			update_option($key, $taxonomies);
-		}
-
-		$this->syncTaxonomies = $taxonomies;
+		$this->syncTaxonomies = Registrar::withIntegration($this->service_name, 'term');
 	}
 
-	protected function getRedirectUri(): string
-	{
-//		if (!empty($this->oauth['redirect_uri'])) {
-//			return $this->oauth['redirect_uri'];
-//		}
-		if ($this->hasOAuth) {
-			return admin_url('admin-ajax.php?action=' . BASE . $this->service_name . '_oauth_callback');
-		}
-		// Changed from admin-ajax.php to REST endpoint
-		return rest_url('jvb/v1/oauth/callback?service=' . $this->service_name);
-	}
-
-	/**
-	 * Used by IntegrationsRoutes.php
-	 * @param string $code
-	 * @param string $state
-	 * @return array
-	 */
-	public function handleOAuthCode(string $code, string $state): array
-	{
-		try {
-			$this->loadCredentials();
-			$tokens = $this->exchangeOAuthCode($code);
-
-			if (!$tokens) {
-				return ['success' => false, 'message' => 'Failed to exchange authorization code'];
-			}
-
-			$credentials = array_merge($this->credentials, $tokens);
-			$credentials = $this->addCredentialData($credentials, $tokens);
-			$saved = $this->saveCredentials($credentials, false);
-
-			if ($saved) {
-				return ['success' => true, 'message' => 'Successfully connected'];
-			}
-
-			return ['success' => false, 'message' => 'Failed to save credentials'];
-		} catch (Exception $e) {
-			$this->logError('OAuth code exchange failed', ['error' => $e->getMessage()]);
-			return ['success' => false, 'message' => 'OAuth error: ' . $e->getMessage()];
-		}
-	}
-
-
 	/*********************************************************************
 	 * ABSTRACT METHODS - MUST BE IMPLEMENTED BY CHILD CLASSES
 	 *********************************************************************/
@@ -338,33 +139,20 @@
 		try {
 			// Process and validate credentials
 			if (!$this->validateCredentials($credentials)) {
-				$this->logError('Invalid credentials');
-				return [
-					'success'	=> false,
-					'message'	=> 'Credentials not formatted correctly'
-				];
+				$this->logError('saveCredentials', 'Invalid credentials');
+				return $this->response(false, 'Credentials not formatted correctly');
 			}
 
+			//Merge the new credentials with the old ones (new stuff overriding any old stuff)
 			$old = $this->getCredentials();
 			$credentials = array_merge($old, $credentials);
 
-			// Temporarily set credentials for testing
-			$this->credentials = $credentials;
-			$this->initialize();
-
 			// Test the connection before saving
 			if ($test) {
 				if (!$this->testConnection(true)) {
-					$this->logError('Connection test failed');
-					// Revert to old credentials
-					$this->credentials = $old;
-					$this->initialize();
+					$this->logError('saveCredentials', 'Connection test failed');
 
-					return [
-						'success' => false,
-						'message' => 'Connection failed. Please check your credentials.',
-						'test_failed' => true
-					];
+					return $this->response(false, 'Connection failed. Please check your credentials');
 				}
 			}
 
@@ -380,17 +168,10 @@
 				$this->updateLastTestedTime();
 				$this->clearCache();
 
-				return [
-					'success' => true,
-					'message' => 'Credentials validated and saved successfully',
-					'reload' => true
-				];
+				return $this->response(true, 'Credentials validated and saved successfully');
 			}
 
-			return [
-				'success' => false,
-				'message' => 'Failed to save credentials to database'
-			];
+			return $this->response(false, 'Failed to save credentials to database');
 
 		} catch (\Exception $e) {
 			// Revert to old credentials on any error
@@ -398,13 +179,7 @@
 				$this->service_name,
 				$this->userID
 			);
-			$this->credentials = $old;
-			$this->initialize();
-
-			return [
-				'success' => false,
-				'message' => 'Validation error: ' . $e->getMessage()
-			];
+			return $this->response(false, 'Validation error: '.print_r($e->getMessage(), true));
 		}
 	}
 
@@ -414,9 +189,7 @@
 	public function deleteCredentials():array
 	{
 		$success = Auth::getInstance()->deleteCredentials($this->service_name, $this->userID);
-		return [
-			'success'	=> $success
-		];
+		return $this->response(true, 'Deleted successfully');
 	}
 	/**
 	 * Validate credentials before storing
@@ -543,52 +316,6 @@
 	/******************************************************************
 		POST SYNC
 	 ******************************************************************/
-	/**
-	 * Handle post save for syncing
-	 *
-	 * Override to implement custom sync logic when posts are saved.
-	 * Check the $settings array for post type specific configuration.
-	 *
-	 * @param int $postID The post ID
-	 * @param WP_Post $post The post object
-	 * @param bool $update Whether this is an update
-	 * @param array $settings Post type integration settings
-	 * @return void
-	 */
-	protected function handleTheSavePost(int $postID, WP_Post $post, bool $update, array $settings): void
-	{
-		// Override in child classes that support post syncing
-		// Example implementation:
-		/*
-		$fields = $this->getSyncFields($postID, 'post', ['schedule_' . $this->service_name]);
-		$options = $fields['schedule_' . $this->service_name] !== ''
-			? ['scheduled' => strtotime($fields['schedule_' . $this->service_name])]
-			: [];
-
-		$this->queueOperation(
-			'sync_post',
-			['post_id' => $postID, 'is_update' => $update],
-			$options
-		);
-		*/
-	}
-
-	/*********************************************************************
-	 * API REQUEST METHODS
-	 *********************************************************************/
-
-
-
-
-
-
-
-
-
-
-
-
-
 
 	/*********************************************************************
 	 * SYNC METHODS
@@ -668,7 +395,8 @@
 		// Let child classes register additional hooks if needed
 		$this->registerAdditionalHooks();
 
-		if (!empty($this->syncPostTypes) && is_null($this->userID)) {
+
+		if (!empty($this->syncPostTypes)) {
 			$this->addSavePost();
 			add_action('transition_post_status', [$this, 'handlePostStatusTransition'], 10, 3);
 
@@ -676,64 +404,21 @@
 				add_action('before_delete_post', [$this, 'handleDeletePost'], 10, 1);
 			}
 		}
-		if (!empty($this->syncTaxonomies) && is_null($this->userID)) {
+		if (!empty($this->syncTaxonomies)) {
 			add_action('saved_term', [$this, 'handleSaveTerm'], 20, 5);
 			if ($this->canSync['delete']) {
 				add_action('pre_delete_term', [$this, 'handleDeleteTerm'], 10, 2);
 			}
 		}
 
-		add_action('init', [$this, 'registerQueueTypes'], 10);
+		if (method_exists($this, 'registerQueueTypes')) {
+			add_action('init', [$this, 'registerQueueTypes'], 10);
+		}
 	}
-		public function addSavePost():void
-		{
-			if (!has_action('save_post', [$this, 'handleSavePost'])) {
-				add_action('save_post', [$this, 'handleSavePost'], 20, 3);
-			}
-		}
-		public function removeSavePost():void
-		{
-			remove_action('save_post', [$this, 'handleSavePost'], 20, 3);
-		}
 
 
-		public function registerQueueTypes():void
-		{
-			if (empty($this->syncTaxonomies) && empty($this->syncPostTypes)) {
-				return;
-			}
-			$queue = JVB()->queue();
-			$executor = new IntegrationExecutor();
 
-			$queue->registry()->register(self::$syncTo, new TypeConfig(
-				mergeable: new DefaultMerger('items'),
-				executor: $executor,
-				chunkKey: 'items',
-				chunkSize: 50,
-				maxRetries: 3,
-			));
 
-			if ($this->canSync['delete']) {
-				$queue->registry()->register(self::$deleteFrom, new TypeConfig(
-					mergeable: new DefaultMerger('external_ids'),
-					executor: $executor,
-					chunkKey: 'external_ids',
-					chunkSize: 200,
-					maxRetries: 2
-				));
-			}
-
-			$queue->registry()->register(self::$syncFrom, new TypeConfig(
-				executor: $executor,
-				maxRetries: 3
-			));
-
-			$this->registerAdditionalQueueTypes($executor);
-		}
-			protected function registerAdditionalQueueTypes(IntegrationExecutor $executor):void
-			{
-				//Empty. Integration extensions can register additional operation types from here.
-			}
 
 	/**
 	 * Handle connection setup and credential storage
@@ -755,11 +440,11 @@
 			}
 
 			// Store credentials
-			$this->credentials = array_merge($this->credentials ?? [], $sanitized);
-			$this->credentials['last_updated'] = time();
+			$credentials = array_merge($this->loadCredentials() ?? [], $sanitized);
+			$credentials['last_updated'] = time();
 
 			// Save to database
-			$saved = $this->saveCredentials($this->credentials);
+			$saved = $this->saveCredentials($credentials);
 
 			if (!$saved) {
 				return [
@@ -810,6 +495,7 @@
 
 	/**
 	 * Ensure service is initialized
+	 * TODO: I don't think this is necessary anymore.
 	 */
 	protected function ensureInitialized(): void
 	{
@@ -819,404 +505,6 @@
 		$this->initialize();
 	}
 
-
-	/***************************************************************
-		ERROR HANDLING
-	 ***************************************************************/
-	/**
-	 * Handle error responses
-	 */
-	protected function handleApiError(int $code, string $body, string $endpoint): void
-	{
-		$message = "API Error ({$code}): ";
-		$decoded = json_decode($body, true);
-
-		// Extract error details
-		$error_details = $this->extractErrorDetails($decoded, $body);
-		$message .= $error_details['message'];
-
-		// Determine error severity based on HTTP code
-		$severity = $this->getErrorSeverity($code);
-
-		// Build comprehensive error context
-		$error_context = [
-			'service' => $this->service_name,
-			'endpoint' => $endpoint,
-			'http_code' => $code,
-			'error_type' => $this->categorizeApiError($code),
-			'error_details' => $error_details,
-			'user_id' => $this->userID,
-			'request_time' => time(),
-			'consecutive_errors' => $this->error_stats['consecutive_errors'],
-			'is_oauth' => $this->isOAuthService,
-			'api_version' => $this->apiVersion,
-			'integration_healthy' => $this->is_healthy
-		];
-
-		// Add rate limit information if present
-		if ($code === 429) {
-			$error_context['rate_limit'] = $this->extractRateLimitInfo($decoded, $body);
-		}
-
-		// Log to ErrorHandler with proper severity
-		$this->logError($message, $error_context, $severity);
-
-		// Update error statistics
-		$this->updateErrorStats($code, $endpoint);
-
-		// Store last error for debugging
-		$this->lastError = [
-			'code' => $code,
-			'message' => $message,
-			'endpoint' => $endpoint,
-			'timestamp' => time(),
-			'context' => $error_context
-		];
-	}
-
-
-	/*****************************************************************
-		OAUTH
-	 *****************************************************************/
-
-	/**
-	 * Get OAuth authorization URL
-	 */
-	public function getOAuthUrl(?string $return_url = null): string
-	{
-
-		if (!$this->hasOAuth) {
-			return '';
-		}
-
-		if (empty($this->credentials)) {
-			$this->ensureInitialized();
-		}
-
-		if (empty($this->oauth['authorize'])) {
-			$this->logError('OAuth authorize URL not configured');
-			return '';
-		}
-
-		// Build base parameters
-		$params = [
-			'client_id' => $this->credentials['client_id'] ?? $this->credentials['app_id'] ?? '',
-			'redirect_uri' => $this->getRedirectUri(),
-			'response_type' => 'code',
-			'scope' => implode(' ', $this->oauth['scopes'] ?? [])
-		];
-		$state_key = wp_generate_password(32, false);
-		$user_id = $this->userID??0;
-
-		// Store state data in transient (expires in 10 minutes)
-		set_transient(
-			'oauth_state_' . $state_key,
-			[
-				'service' => $this->service_name,
-				'user_id' => $user_id,
-				'created' => time()
-			],
-			600
-		);
-
-		$state_parts = [
-			$state_key,
-			$user_id
-		];
-
-		// Add return URL if provided
-		if ($return_url) {
-			$state_parts[] = base64_encode($return_url);
-		} else {
-			$state_parts[] = base64_encode(admin_url('admin.php?page=jvb-integrations'));
-		}
-
-		$params['state'] = implode('|', $state_parts);
-
-		// Allow child classes to modify params (they can override/remove as needed)
-		if (method_exists($this, 'addOAuthParams')) {
-			$params = $this->addOAuthParams($params);
-		}
-
-		return $this->oauth['authorize'] . '?' . http_build_query($params);
-	}
-
-	/**
-	 * Add service-specific OAuth parameters
-	 */
-	protected function addOAuthParams(array $params): array
-	{
-		// Override in child classes to add service-specific params
-		// e.g., access_type, prompt, etc.
-		return $params;
-	}
-
-
-
-	/**
-	 * Extract retry-after header from response
-	 *
-	 * @param array|WP_Error $response WordPress HTTP response
-	 * @return int Seconds to wait before retry
-	 */
-	protected function extractRetryAfter($response): int
-	{
-		if (is_wp_error($response)) {
-			return 5;
-		}
-
-		$headers = wp_remote_retrieve_headers($response);
-
-		if (isset($headers['retry-after'])) {
-			// Could be seconds or HTTP date
-			$retry_after = $headers['retry-after'];
-
-			if (is_numeric($retry_after)) {
-				return (int) $retry_after;
-			}
-
-			// Try to parse as date
-			$timestamp = strtotime($retry_after);
-			if ($timestamp !== false) {
-				return max(0, $timestamp - time());
-			}
-		}
-
-		return 5; // Default wait time
-	}
-
-	/**
-	 * Exchange OAuth code for tokens
-	 */
-	protected function exchangeOAuthCode(string $code): ?array
-	{
-		$this->ensureInitialized();
-
-		// Build request data
-		$request_data = [
-			'client_id' => $this->credentials['client_id'] ?? '',
-			'client_secret' => $this->credentials['client_secret'] ?? '',
-			'code' => $code,
-			'grant_type' => 'authorization_code',
-			'redirect_uri' => $this->getRedirectUri()
-		];
-
-		$oauth_endpoint = $this->oauth['token'];
-		$response = $this->makeOAuthRequest('POST', $oauth_endpoint, $request_data);
-
-		if (is_wp_error($response)) {
-			$this->logError('OAuth token exchange failed', [
-				'error' => $response->get_error_message(),
-				'code' => $response->get_error_code()
-			]);
-			return null;
-		}
-
-		// Parse response
-		if (isset($response['access_token'])) {
-			$expires_in = $response['expires_in'] ?? 2592000; // 30 days default
-
-			return [
-				'access_token' => $response['access_token'],
-				'refresh_token' => $response['refresh_token'] ?? '',
-				'expires_in' => $expires_in,
-				'expires_at' => time() + $expires_in, // Calculate expiry timestamp
-				'token_type' => $response['token_type'] ?? 'Bearer',
-				'merchant_id' => $response['merchant_id'] ?? '',
-				'scope' => $response['scope'] ?? ''
-			];
-		}
-
-		$this->logError('Failed to obtain access token', ['response' => $response]);
-		return null;
-	}
-
-	/**
-	 * Add service-specific credential data
-	 */
-	protected function addCredentialData(array $credentials, array $tokens): array
-	{
-		// Override in child classes to add service-specific data
-		return $credentials;
-	}
-
-	/**
-	 * Check if token should be proactively refreshed
-	 * Different from isOAuthValid() which checks if token is actually expired
-	 */
-	protected function shouldRefreshToken(): bool
-	{
-		if (!$this->hasOAuth || $this->refresh_interval === 0) {
-			return false;
-		}
-
-		// If no expiry info, we can't proactively refresh
-		if (empty($this->credentials['expires_at'])) {
-			return false;
-		}
-
-		$expires_at = intval($this->credentials['expires_at']);
-		$time_until_expiry = $expires_at - time();
-
-		// Refresh if we're within the refresh interval window
-		return $time_until_expiry > 0 && $time_until_expiry <= $this->refresh_interval;
-	}
-	/**
-	 * Get time until token refresh is recommended
-	 * Useful for displaying in admin UI
-	 */
-	public function getTimeUntilRefresh(): ?int
-	{
-		if ($this->refresh_interval === 0 || empty($this->credentials['expires_at'])) {
-			return null;
-		}
-
-		$expires_at = intval($this->credentials['expires_at']);
-		$refresh_at = $expires_at - $this->refresh_interval;
-		$time_until_refresh = $refresh_at - time();
-
-		return max(0, $time_until_refresh);
-	}
-
-	/**
-	 * Get token freshness status
-	 * Returns: 'fresh', 'should_refresh', 'expired', or 'no_expiry_info'
-	 */
-	public function getTokenStatus(): string
-	{
-		if (!$this->hasOAuth) {
-			return 'not_oauth';
-		}
-
-		if (empty($this->credentials['access_token'])) {
-			return 'no_token';
-		}
-
-		if (empty($this->credentials['expires_at'])) {
-			return 'no_expiry_info';
-		}
-
-		$expires_at = intval($this->credentials['expires_at']);
-		$now = time();
-
-		if ($expires_at <= $now) {
-			return 'expired';
-		}
-
-		if ($this->shouldRefreshToken()) {
-			return 'should_refresh';
-		}
-
-		return 'fresh';
-	}
-	/**
-	 * Refresh OAuth token
-	 */
-	protected function refreshOAuthToken(): bool
-	{
-		if (!$this->hasOAuth || empty($this->credentials['refresh_token'])) {
-			return false;
-		}
-
-		$request_data = [
-			'client_id' => $this->credentials['client_id'],
-			'client_secret' => $this->credentials['client_secret'],
-			'refresh_token' => $this->credentials['refresh_token'],
-			'grant_type' => 'refresh_token'
-		];
-
-		$response = $this->makeOAuthRequest('POST', $this->oauth['token'], $request_data);
-
-		if (is_wp_error($response)) {
-			$error_message = $response->get_error_message();
-
-			if (str_contains($error_message, 'invalid_grant')) {
-				$this->logError('OAuth refresh token is invalid - user must re-authorize', [
-					'error' => $error_message
-				], 'critical');
-
-				// Mark unhealthy immediately
-				$this->error_stats['consecutive_errors'] = $this->error_threshold;
-				$this->is_healthy = false;
-				$this->saveErrorStats();
-			}
-
-			$this->logError('Failed to refresh OAuth token for '.$this->service_name, [
-				'error' => $error_message
-			]);
-			return false;
-		}
-
-		if (isset($response['access_token'])) {
-			$this->credentials['access_token'] = $response['access_token'];
-			$this->credentials['expires_at'] = time() + ($response['expires_in'] ?? 2592000); // 30 days
-
-			// Note: Some services return the SAME refresh token
-			if (isset($response['refresh_token'])) {
-				$this->credentials['refresh_token'] = $response['refresh_token'];
-			}
-
-			$this->saveCredentials($this->credentials);
-			return true;
-		}
-
-		return false;
-	}
-
-	/**
-	 * Revoke OAuth access
-	 */
-	public function revokeOAuthAccess(): bool
-	{
-		if (!$this->hasOAuth || empty($this->oauth['revoke'])) {
-			return false;
-		}
-
-		if (!empty($this->credentials['access_token'])) {
-			wp_remote_post($this->oauth['revoke'], [
-				'body' => ['token' => $this->credentials['access_token']]
-			]);
-		}
-
-		return Auth::getInstance()->deleteCredentials($this->service_name, $this->userID);
-
-	}
-
-	public function handleOAuthDisconnect(): array
-	{
-		try {
-			// Revoke the token with Square using centralized request
-			if (!empty($this->credentials['access_token'])) {
-				$revoke_data = [
-					'client_id' => $this->credentials['client_id'] ?? '',
-					'access_token' => $this->credentials['access_token']
-				];
-
-				// Make revoke request (ignore response as revoke often returns empty)
-				$this->makeOAuthRequest('POST', $this->oauth['revoke'], $revoke_data);
-			}
-
-			// Clear stored credentials (preserve app credentials)
-			$this->credentials = [
-				'client_id' => $this->credentials['client_id'] ?? '',
-				'client_secret' => $this->credentials['client_secret'] ?? '',
-				'environment' => $this->credentials['environment'] ?? 'sandbox'
-			];
-
-			$this->saveCredentials($this->credentials);
-			$this->clearCache();
-
-			return [
-				'success' => true,
-				'message' => 'Successfully disconnected from Square'
-			];
-		} catch (Exception $e) {
-			return [
-				'success' => false,
-				'message' => 'Failed to disconnect: ' . $e->getMessage()
-			];
-		}
-	}
 	/**
 	 * Generate webhook signature key for services that require it
 	 * @return string
@@ -1225,268 +513,6 @@
 	{
 		return wp_generate_password(32, false);
 	}
-	/**
-	 * Generate OAuth state parameter
-	 */
-	protected function generateOAuthState(?int $user_id, ?string $return_url = null): string
-	{
-		$user_id = $user_id ?? 0;
-		$state = wp_create_nonce($this->service_name . '_oauth_' . $user_id) . '|' . $user_id;
-
-		if ($return_url) {
-			$state .= '|' . base64_encode($return_url);
-		}
-
-		return $state;
-	}
-
-	protected function getNonce(): string
-	{
-		 return wp_create_nonce($this->service_name . '_oauth_' . $this->userID);
-	}
-
-	/**
-	 * Determine return URL after OAuth
-	 */
-	protected function determineReturnUrl(?int $user_id): string
-	{
-		if ($user_id > 0) {
-			return home_url('/dash/integrations/#' . $this->service_name);
-		}
-
-		return admin_url('admin.php?page=jvb-integrations');
-	}
-
-
-	/****************************************************************
-		POST SYNC
-	 ****************************************************************/
-	/**
-	 * Get field mapping for a post type
-	 */
-	protected function getFieldMapping(string $post_type): array
-	{
-		// Apply filter for custom mapping
-		return apply_filters(
-			"jvb_{$this->service_name}_field_mapping",
-			[],
-			$post_type,
-			$this
-		);
-	}
-
-
-	/**
-	 * Map WordPress fields to service fields
-	 */
-	protected function mapFieldsToService(int $postID, array $mapping): array
-	{
-		$meta_manager = Meta::forPost($postID);
-		$service_data = [];
-
-		foreach ($mapping as $wp_field => $service_field) {
-			$value = $meta_manager->get($wp_field);
-
-			if ($value !== null && $value !== '') {
-				$this->setNestedValue($service_data, $service_field, $value);
-			}
-		}
-
-		return apply_filters(
-			"jvb_{$this->service_name}_mapped_data",
-			$service_data,
-			$postID,
-			$mapping
-		);
-	}
-
-	/**
-	 * Set nested array value using dot notation
-	 */
-	protected function setNestedValue(array &$array, string $path, $value): void
-	{
-		$keys = explode('.', $path);
-		$current = &$array;
-
-		foreach ($keys as $i => $key) {
-			if ($i === count($keys) - 1) {
-				$current[$key] = $value;
-			} else {
-				if (!isset($current[$key])) {
-					$current[$key] = [];
-				}
-				$current = &$current[$key];
-			}
-		}
-	}
-	/**
-	 * Handle post save
-	 */
-	public function handleSavePost(int $postID, WP_Post $post, bool $update): void
-	{
-		if (!is_null($this->userID)) {
-			return;
-		}
-		error_log('=== ['.$this->service_name.']::handleSavePost called');
-
-		if (!$postID || $postID === 0) {
-			return;
-		}
-		$postType = jvbNoBase($post->post_type);
-
-		if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
-		if (wp_is_post_revision($postID)) return;
-
-
-		if (empty($this->syncPostTypes) || !in_array(jvbNoBase($postType), $this->syncPostTypes)) {
-			error_log('Not handling save for '.$this->service_name.' because there are no syncPostTypes: '.print_r($this->syncPostTypes, true));
-			return;
-		}
-
-		$registrar = Registrar::getInstance($postType);
-		if (!$registrar){
-			return;
-		}
-
-		$settings = $registrar->hasIntegration($this->service_name)??null;
-		if (!$settings) {
-			error_log('Not handling save for '.$this->service_name.' because of no registrar settings');
-			return;
-		}
-
-		$settings = $registrar->getIntegrationConfig($this->service_name);
-		if (!$settings){
-			error_log('Not handling save for '.$this->service_name.' because of no integration config '.print_r($settings, true));
-			return;
-		}
-
-
-		$fields = $this->getSyncFields($postID, 'post', ['schedule_'.$this->service_name]);
-		if (!$fields['share_to_'.$this->service_name]) {
-			error_log('Not handling save for '.$this->service_name.' because of no share_to_'.$this->service_name.' '.print_r($fields, true));
-			return;
-		}
-
-		$isShared = isset($fields["_{$this->service_name}_item_id"]);
-		if ($update && $isShared && !$fields['_keep_synced_'.$this->service_name]) {
-			error_log('Not handling save for '.$this->service_name.' because it is already shared, and not set to keep synced. ');
-			return;
-		}
-
-		if ($post->post_status !== 'publish' && !$isShared) {
-			error_log('Not handling save for '.$this->service_name.' because post status is not publish, and it is not already shared.');
-			return;
-		}
-		error_log('==== Sending to integration\'s handleTheSavePost '.$this->service_name.' ====');
-		$this->removeSavePost();
-		$this->handleTheSavePost($postID, $post, $update, $settings);
-		$this->addSavePost();
-	}
-
-
-	protected function getSyncFields(int $postID, string $type, array $additional = []):array
-	{
-		$meta = new Meta($postID, $type);
-		$fieldsToCheck = [
-			'share_to_' . $this->service_name,
-			'_keep_synced_' . $this->service_name,
-			"_{$this->service_name}_item_id",
-			"_{$this->service_name}_last_sync",
-			"_{$this->service_name}_shared_at",
-			"_{$this->service_name}_sync_status",
-			... $additional
-		];
-		return $meta->getAll($fieldsToCheck);
-	}
-
-	/**
-	 * Handle post status transitions
-	 */
-	public function handlePostStatusTransition(string $new_status, string $old_status, WP_Post $post): void
-	{
-		if (empty($this->syncPostTypes)) {
-			return;
-		}
-
-		if (!in_array(jvbNoBase($post->post_type), $this->syncPostTypes)) {
-			return;
-		}
-
-		//Map fields from our custom post types to the fields expected by the integration
-		$mappedFields = $this->getFieldMapping($post->post_type);
-
-		$fields = $this->getSyncFields($post->ID, 'post', $mappedFields);
-
-		// Handle unpublish action
-		if ($old_status === 'publish' && $new_status !== 'publish') {
-
-			if ($fields["_{$this->service_name}_item_id"] && $this->canSync['delete']) {
-				try {
-					$this->queueOperation(
-						'delete_post',
-						[
-							$post,
-							$fields["_{$this->service_name}_item_id"]
-						]
-					);
-				} catch (Exception $e) {
-					$this->logError("Failed to handle unpublish for post {$post->ID}: " . $e->getMessage());
-				}
-			}
-		}
-	}
-
-	/**
-	 * Handle post deletion
-	 */
-	public function handleDeletePost(int $postID): void
-	{
-		if (!$this->canSync['delete']) {
-			return;
-		}
-
-		$post = get_post($postID);
-		if (!$post || !in_array($post->post_type, $this->syncPostTypes)) {
-			return;
-		}
-
-		$fields = $this->getSyncFields($postID, 'post');
-
-		if ($fields["_{$this->service_name}_item_id"] !== '') {
-
-			$this->queueOperation(
-				'delete_post',
-				[
-					'post_id'	=> $post->ID,
-				]
-			);
-		}
-	}
-
-	protected function handleSaveTerm($term_id, $tt_id, $taxonomy, $update, $args): void
-	{
-		$noBase = jvbNoBase($taxonomy);
-		if (!in_array($noBase, $this->syncTaxonomies)) {
-			return;
-		}
-		$registrar = Registrar::getInstance($noBase);
-		if (!$registrar->hasFeature('is_content')) {
-			return;
-		}
-
-
-		$settings = $registrar->getIntegrationConfig($this->service_name);
-		if (!$settings) {
-			return;
-		}
-
-		// Similar sync logic as handleSavePost but for terms
-		$this->handleTheTermSave($term_id, $taxonomy, $update, $settings);
-	}
-
-	protected function handleTheTermSave($term_id, $taxonomy, $update, $settings) {
-
-	}
 
 
 	/*******************************************************************
@@ -1494,183 +520,6 @@
 	 *******************************************************************/
 
 	/**
-	 * Get API URL for endpoint
-	 */
-	protected function getApiUrl(string $endpoint, ?string $baseKey = null): string|false
-	{
-		if ($this->hasOAuth && in_array($endpoint, $this->oauth)) {
-			return $endpoint;
-		}
-		if (is_array($this->apiBase)) {
-			if ($baseKey && array_key_exists($baseKey, $this->apiBase)) {
-				$base = $this->apiBase[$baseKey];
-			} else {
-				$base = ($this->apiBase['base'] ?? reset($this->apiBase));
-			}
-		} else {
-			$base = $this->apiBase;
-		}
-
-		if (!$base || $base === '') {
-			$this->logError('API base URL not configured for {$this->>service_name}');
-			return false;
-		}
-
-		// Handle named endpoints
-		if (!$this->isValidEndpoint($endpoint)) {
-			$this->logError("{$endpoint} is not a valid endpoint for {$this->service_name}");
-			return false;
-		}
-
-		// Build full URL
-		$base = rtrim($base, '/');
-		$endpoint = ltrim($endpoint, '/');
-
-		return "{$base}/{$endpoint}";
-	}
-
-	/**
-	 * Check if an endpoint is valid, supporting both exact matches and patterns
-	 *
-	 * @param string $endpoint
-	 * @return bool
-	 */
-	protected function isValidEndpoint(string $endpoint): bool
-	{
-		// Remove query parameters for validation
-		$endpointPath = parse_url($endpoint, PHP_URL_PATH);
-		if ($endpointPath === false) {
-			$endpointPath = $endpoint;
-		}
-
-		foreach ($this->apiEndpoints as $pattern) {
-			// Check for exact match first
-			if ($endpointPath === $pattern) {
-				return true;
-			}
-
-			if (str_starts_with($endpointPath, $pattern)) {
-				return true;
-			}
-
-			// Check if pattern contains wildcards (indicated by square brackets)
-			if (strpos($pattern, '[') !== false) {
-				// Convert the pattern to a regex
-				$regexPattern = '#^' . str_replace(['[^/]+'], ['[^/]+'], $pattern) . '(?:\?.*)?$#';
-				if (preg_match($regexPattern, $endpoint)) {
-					return true;
-				}
-			}
-		}
-
-		return false;
-	}
-
-
-
-	/**
-	 * Categorize API errors for better tracking
-	 */
-	protected function categorizeApiError(int $code): string
-	{
-		return match(true) {
-			$code >= 400 && $code < 404 => 'client_error',
-			$code === 404 => 'not_found',
-			$code === 401 => 'authentication',
-			$code === 403 => 'authorization',
-			$code === 429 => 'rate_limit',
-			$code >= 500 && $code < 600 => 'server_error',
-			default => 'unknown'
-		};
-	}
-
-	/**
-	 * Determine error severity based on HTTP code
-	 */
-	protected function getErrorSeverity(int $code): string
-	{
-		return match(true) {
-			$code === 429 => 'warning', // Rate limiting
-			$code >= 400 && $code < 500 => 'error', // Client errors
-			$code >= 500 => 'critical', // Server errors
-			default => 'error'
-		};
-	}
-
-	/**
-	 * Extract error details from response
-	 */
-	protected function extractErrorDetails($decoded, string $body): array
-	{
-		$details = [
-			'message' => 'Unknown error',
-			'code' => null,
-			'details' => null
-		];
-
-		if ($decoded && isset($decoded['error'])) {
-			if (is_array($decoded['error'])) {
-				$details['message'] = $decoded['error']['message'] ?? json_encode($decoded['error']);
-				$details['code'] = $decoded['error']['code'] ?? null;
-				$details['details'] = $decoded['error']['details'] ?? null;
-			} else {
-				$details['message'] = $decoded['error'];
-			}
-		} elseif ($decoded && isset($decoded['message'])) {
-			$details['message'] = $decoded['message'];
-		} elseif (!empty($body)) {
-			$details['message'] = $body;
-		}
-
-		return $details;
-	}
-
-	/**
-	 * Extract rate limit information from response
-	 */
-	protected function extractRateLimitInfo($decoded, string $body): array
-	{
-		$info = [
-			'retry_after' => null,
-			'limit' => null,
-			'remaining' => null,
-			'reset' => null
-		];
-
-		// Try to extract from decoded response
-		if ($decoded) {
-			$info['retry_after'] = $decoded['retry_after'] ?? $decoded['retry-after'] ?? null;
-			$info['limit'] = $decoded['x-rate-limit-limit'] ?? null;
-			$info['remaining'] = $decoded['x-rate-limit-remaining'] ?? null;
-			$info['reset'] = $decoded['x-rate-limit-reset'] ?? null;
-		}
-
-		return $info;
-	}
-
-	/**
-	 * Update error statistics
-	 */
-	protected function updateErrorStats(int $code, string $endpoint): void
-	{
-		$this->error_stats['total_errors']++;
-		$this->error_stats['consecutive_errors']++;
-
-		// Track error types
-		$error_type = $this->categorizeApiError($code);
-		if (!isset($this->error_stats['error_types'][$error_type])) {
-			$this->error_stats['error_types'][$error_type] = 0;
-		}
-		$this->error_stats['error_types'][$error_type]++;
-
-		// Save stats to cache
-		$this->saveErrorStats();
-	}
-
-
-
-
-	/**
 	 * Get service name
 	 */
 	public function getServiceName(): string
@@ -1697,24 +546,6 @@
 		return static::getInstance()::$hasExtraOptions;
 	}
 
-	/*********************************************************************
-		RENDERING
-	 *********************************************************************/
-//	public function renderAdditionalOptions()
-//	{
-//		//Default: nothing.
-//	}
-
-	/**
-	 * Render additional action buttons (optional)
-	 * Override in integration classes that need extra actions
-	 */
-//	public function renderAdditionalActions(): void
-//	{
-//		// Default: no additional actions
-//		// Override in extensions for service-specific actions
-//	}
-
 	/**
 	 * Get service description (optional)
 	 * Override in integration classes for custom descriptions
@@ -1746,236 +577,6 @@
 	}
 
 
-
-
-
-
-	public function renderConnection(bool $return = false):string
-	{
-
-		if ($this->userID && !JVB()->userCanConnect($this->service_name, $this->userID)) {
-			return '';
-		}
-
-		$meta = Meta::forOptions($this->userID.'_integrations');
-		$is_connected = $this->isSetUp();
-		$credentials = $this->getCredentials();
-
-		$admin_only = $this->isOAuthService ? [
-			'client_id',
-			'client_secret',
-		] : [];
-
-		ob_start();
-		?>
-		<form id="<?=$this->service_name?>" class="integration <?php echo $is_connected ? 'connected' : 'disconnected'; ?>"
-			 data-service="<?php echo esc_attr($this->service_name); ?>">
-			<div class="header row x-btw">
-				<h3><?php echo esc_html($this->title); ?></h3>
-				<div class="setup">
-					<?php if ($is_connected): ?>
-						<span class="indicator connected">●</span>
-						<span class="text">Set Up</span>
-					<?php else: ?>
-						<span class="indicator disconnected">●</span>
-						<span class="text">Not Set Up</span>
-					<?php endif; ?>
-				</div>
-			</div>
-
-			<?php if ($is_connected && array_key_exists('updated_at', $credentials) && $credentials['updated_at'] > 0): ?>
-				<div class="meta">
-					<small>Last updated: <?php echo human_time_diff($credentials['updated_at']) . ' ago'; ?></small>
-				</div>
-			<?php endif; ?>
-
-			<?php
-			if (!empty($this->instructions)) {
-				?>
-				<details>
-					<summary>
-						Instructions
-					</summary>
-					<ol>
-						<?php
-						foreach ($this->instructions as $instruction) {
-							echo '<li>'.$instruction.'</li>';
-						}
-						?>
-					</ol>
-				</details>
-				<?php
-			}
-?>
-			<details class="initial-setup"<?= $is_connected?'' : ' open'?>>
-				<summary>Initial Setup</summary>
-				<?php
-				foreach ($this->fields as $name => $config) {
-					if ($is_connected && !empty($credentials[$name])) {
-					if (in_array($name, $admin_only) && !current_user_can('manage_options')) {
-						continue;
-					}
-					?>
-					<span class="label"><?=$config['label']?>:</span>
-					<code>
-						<?php
-						if (str_contains($name, 'secret')) {
-							for ($i = 1; $i<=strlen($credentials[$name]) - 8; $i++) {
-								echo '*';
-							}
-							echo substr($credentials[$name], -8);
-						} else {
-							echo $credentials[$name];
-						}
-						?>
-					</code>
-					<?php
-					} else {
-						$config['value'] = $credentials[$name]??'';
-						$config['autocomplete'] = 'off';
-						$config['base'] = $this->service_name.'_';
-						echo Form::render($name, '', $config);
-					}
-				}
-				if ($this->hasWebhooks) {
-					echo $this->renderWebhookUrl();
-				}
-				?>
-			</details>
-			<?php
-
-			if ($this->isOAuthService) {
-				$this->renderConnectedOAuthStatus();
-			}
-
-			?>
-
-			<div class="integration-content">
-
-				<?php
-
-
-
-				if (!empty($this->advanced)) {
-					?>
-					<details>
-						<summary>Advanced Settings</summary>
-						<?php
-						foreach ($this->advanced as $name => $config) {
-							$config['value'] = $credentials[$name]??'';
-							$config['base'] = $this->service_name.'_';
-							$config['autocomplete'] = 'off';
-							Form::render($name,null, $config);
-						}
-						?>
-					</details>
-					<?php
-				}
-				if (!empty($this->defaults)) {
-					?>
-					<a href="<?php echo admin_url('admin.php?page=jvb-integration-' . $this->service_name); ?>"
-					   class="button">
-						More Settings
-					</a>
-					<?php
-				}
-				?>
-			</div>
-			<div class="actions row x-btw wrap">
-				<?php
-				foreach ($this->buttons as $action => $label) {
-					if (!$is_connected && $action !== 'save_credentials') {
-						continue;
-					}
-					$title = $confirm = '';
-					switch ($action) {
-						case 'save_credentials':
-							$title = $label;
-							$label = jvbIcon('floppy-disk');
-							break;
-						case 'clear_credentials':
-							$title = $label;
-							$label = jvbIcon('plugs');
-							$confirm = ' data-confirm="Are you sure you want to delete these credentials?"';
-							break;
-						case 'clear_cache':
-							$title = $label;
-							$label = jvbIcon('arrows-clockwise');
-							break;
-					}
-					$title = $title === '' ? '' : ' title ="'.$title.'"';
-					?>
-					<button type="button" data-action="<?=$action?>"<?=$title?><?=$confirm?>><?=$label?></button>
-					<?php
-				}
-				?>
-			</div>
-		</form>
-		<?php
-		$result = ob_get_clean();
-		if(!$return) {
-			echo $result;
-		}
-		return $result;
-	}
-
-	protected function renderConnectedOAuthStatus(): void
-	{
-		if (!$this->isSetup()) {
-			return;
-		}
-		$credentials = $this->getCredentials();
-		$hasCredentials = $this->hasOAuthCredentials();
-		$returnURL = is_admin() ? admin_url('admin.php?page=jvb-integrations') : (get_the_permalink() ?: home_url());
-		?>
-
-		<details <?= $hasCredentials?' open':''?>>
-			<summary>
-				<?php if ($hasCredentials) { ?>
-					Connected Account
-				<?php } else { ?>
-					<div class="oauth-connect">
-						<a href="<?php echo esc_url($this->getOAuthUrl($returnURL)); ?>"
-						   class="button button-primary jvb-oauth-connect"
-						   data-service="<?php echo esc_attr($this->service_name); ?>">
-							<?php echo jvbIcon($this->icon); ?>
-							Authorize Connection
-						</a>
-					</div>
-				<?php } ?>
-				<div class="connection-status <?= $hasCredentials ? 'connected' : 'disconnected' ?>">
-					<span class="status-indicator">●</span>
-					<span><?= $hasCredentials ? 'Connected' : 'Not Connected' ?></span>
-				</div>
-			</summary>
-			<label>OAuth Redirect URL:</label>
-			<code>
-				<?= $this->getRedirectUri(); ?>
-			</code>
-
-
-		<?php if (!empty($credentials['updated_at'])): ?>
-		<div class="oauth-meta">
-			<small>Token expires: <?php
-				echo isset($credentials['expires_at'])
-					? human_time_diff($credentials['expires_at'])
-					: 'Never';
-				?></small>
-		</div>
-		<?php endif;
-		// Allow child classes to add service-specific connected UI
-		$this->renderOAuthConnectedOptions();
-		?>
-		</details>
-		<?php
-	}
-
-	protected function renderOAuthConnectedOptions():void
-	{
-
-	}
-
-
 	/**
 	 * Update last tested time
 	 */
@@ -1985,180 +586,6 @@
 		$cred->updateTested($this->service_name, $this->userID);
 	}
 
-	public function handleAdminPost(): void
-	{
-		if (!current_user_can('manage_options')) {
-			wp_die('Insufficient permissions');
-		}
-
-		$service = $this->getServiceName();
-
-		// Verify nonce
-		$nonce_field = 'jvb_integration_nonce_' . $service;
-		$nonce_action = 'jvb_integration_save_' . $service;
-		if (!isset($_POST[$nonce_field]) || !wp_verify_nonce($_POST[$nonce_field], $nonce_action)) {
-			wp_die('Security check failed');
-		}
-
-		// Get the action type
-		$action_type = sanitize_text_field($_POST['action_type'] ?? 'save');
-
-		// Prepare the request in the format handleAjaxRequest expects
-		$_REQUEST['action'] = $action_type;
-		$_REQUEST['service'] = $service;
-
-		// Copy all POST data to REQUEST
-		foreach ($_POST as $key => $value) {
-			$_REQUEST[$key] = $value;
-		}
-
-		// Set up for JSON response capture
-		ob_start();
-
-		try {
-			// Call the existing AJAX handler
-			$this->handleAjaxRequest();
-			$response = ob_get_clean();
-
-			// Parse the JSON response
-			$result = json_decode($response, true);
-
-			if ($result && isset($result['success'])) {
-				if ($result['success']) {
-					$message = $result['message'] ?? ucfirst($service) . ' settings saved successfully!';
-					$this->setAdminNotice($message, 'success');
-				} else {
-					$message = $result['message'] ?? 'Failed to save ' . ucfirst($service) . ' settings.';
-					$this->setAdminNotice($message, 'error');
-				}
-			} else {
-				// If no proper JSON response, check if connection worked
-				if ($action_type === 'test') {
-					$connected = $this->testConnection();
-					$message = $connected ? 'Connection successful!' : 'Connection failed. Please check your credentials.';
-					$this->setAdminNotice($message, $connected ? 'success' : 'error');
-				}
-			}
-		} catch (Exception $e) {
-			ob_end_clean();
-			$this->setAdminNotice('Error: ' . $e->getMessage(), 'error');
-		}
-
-		// Redirect back to integrations page
-		wp_redirect(admin_url('admin.php?page=jvb-integrations'));
-		exit;
-	}
-
-	/**
-	 * Set admin notice using transients for redirect
-	 */
-	protected function setAdminNotice(string $message, string $type = 'info'): void
-	{
-		$notices = get_transient('jvb_admin_notices') ?: [];
-		$notices[] = [
-			'message' => $message,
-			'type' => $type === 'success' ? 'updated' : 'error'
-		];
-		set_transient('jvb_admin_notices', $notices, 30);
-	}
-
-	/**
-	 * Display admin notices from transient
-	 */
-	public static function displayAdminNotices(): void
-	{
-		$notices = get_transient('jvb_admin_notices');
-
-		if ($notices) {
-			foreach ($notices as $notice) {
-				?>
-				<div class="notice notice-<?php echo esc_attr($notice['type']); ?> is-dismissible">
-					<p><?php echo esc_html($notice['message']); ?></p>
-				</div>
-				<?php
-			}
-			delete_transient('jvb_admin_notices');
-		}
-	}
-
-	public function hasDefaults():bool
-	{
-		return !empty($this->defaults);
-	}
-
-	public function renderDefaults():void
-	{
-		$types = $this->enabledContentTypes();
-		if (empty($types)) {
-			return;
-		}
-		$meta = Meta::forOptions($this->userID.'_integrations');
-		?>
-		<form>
-			<h1><?= $this->title?> Defaults:</h1>
-			<p>Find yourself constantly repeating yourself?</p>
-			<p>Set defaults for different content types and <?=$this->title?>!</p>
-			<?php
-			foreach ($this->defaults as $name => $config) {
-				$config['required'] = false;
-
-				$config['base'] = $this->service_name.'_';
-				$config['autocomplete'] = 'off';
-				echo Form::render($name, null, $config);
-			}
-			foreach ($this->syncPostTypes as $type) {
-				$registrar = Registrar::getInstance($type);
-
-				$icon = $registrar->getIcon();
-				$icon = $icon === '' ? jvbDefaultIcon() : $icon;
-				?>
-				<details>
-					<summary><?= jvbIcon($icon) ?><?= $registrar->getSingular()?> Defaults</summary>
-					<?php
-					$fields = new AddIntegrationFields($this->service_name);
-					$fields = $fields->getIntegrationFields();
-					foreach($fields as $name=>$c) {
-						$c['required'] = false;
-						if ($c['type'] === 'number') {
-							$c['type'] = 'text';
-							$c['subtype'] = 'number';
-						}
-						if (array_key_exists('description', $c)) {
-							$c['hint'] = $c['description'];
-							unset($c['description']);
-						}
-						echo Form::render($name, null, $c);
-					}
-					?>
-				</details>
-				<?php
-			}
-			?>
-		</form>
-		<?php
-	}
-
-	public function hasContent():bool
-	{
-		return $this->has_content;
-	}
-	public function getDefaultContentType():string
-	{
-		return $this->defaultContent;
-	}
-
-	public function enabledContentTypes():array
-	{
-		if (!$this->has_content) {
-			return [];
-		}
-
-		return array_filter(array_map(function($registrar) {
-			$registrar = Registrar::getInstance($registrar);
-			return $registrar->getIntegration($this->service_name)->getContentType();
-		}, Registrar::withIntegration($this->service_name)));
-	}
-
 	protected function getSupportedImage(int $imgID):int
 	{
 		//If this integration supports webp, we can just send the original image id
@@ -2187,11 +614,6 @@
 	}
 
 
-	public function getAllowedContent():array
-	{
-		return $this->allowedContent;
-	}
-
 	/**
 	 * Used by JVBase\registrar\helpers\AddIntegrationFields.php
 	 * @return array
@@ -2206,18 +628,4 @@
 		return $this->icon;
 	}
 
-
-
-	public function canBatchUpdate():bool
-	{
-		return $this->hasBatchUpdate;
-	}
-	public function canBatchCreate():bool
-	{
-		return $this->hasBatchCreate;
-	}
-	public function canBatchDelete():bool
-	{
-		return $this->hasBatchDelete;
-	}
 }

--
Gitblit v1.10.0