From f94860aacd6200fb24c9e7431eb379a368cb392d Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Fri, 10 Jul 2026 00:35:44 +0000
Subject: [PATCH] =Refactored CredentialsManager.php to utilize a CustomTable instance instead
---
inc/integrations/Integrations.php | 161 +++++++++++---------------
inc/registrar/Fields.php | 16 ++
inc/integrations/CredentialsManager.php | 125 ++++++++------------
3 files changed, 135 insertions(+), 167 deletions(-)
diff --git a/inc/integrations/CredentialsManager.php b/inc/integrations/CredentialsManager.php
index 66a982e..a3967a2 100644
--- a/inc/integrations/CredentialsManager.php
+++ b/inc/integrations/CredentialsManager.php
@@ -1,6 +1,8 @@
<?php
namespace JVBase\integrations;
+use JVBase\managers\CustomTable;
+
if (!defined('ABSPATH')) {
exit;
}
@@ -9,12 +11,39 @@
{
private static ?CredentialsManager $instance = null;
private string $encryption_key;
- private string $option_prefix = 'jvb_integration_';
- private string $user_meta_prefix = 'jvb_integration_';
+ private CustomTable $table;
private function __construct()
{
- $this->encryption_key = $this->getEncryptionKey();
+ $this->getEncryptionKey();
+ $this->defineTable();
+ }
+
+ protected function defineTable():void
+ {
+ $table = CustomTable::for('integrations');
+ $table->setColumns([
+ 'id' => 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
+ 'user_id' => $table->getUserIDType().' NOT NULL',
+ 'integration' => "ENUM('bluesky','cloudflare','facebook','google-maps','gmb','helcim','instagram','postmark','square','umami') NOT NULL",
+ 'credentials' => 'varchar(255) DEFAULT NULL',
+ 'created_at' => 'datetime DEFAULT CURRENT_TIMESTAMP',
+ 'updated_at' => 'datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
+ ]);
+
+ $table->setKeys([
+ ['key' => 'PRIMARY', 'value' => '(`id`)'],
+ ['key' => 'UNIQUE', 'value' => '`user_integration` (`user_id`, `integration`)'],
+ '`user` (`user_id`)',
+ ]);
+
+ $base = BASE;
+ $table->setConstraints([
+ "CONSTRAINT `{$base}integrations_user` FOREIGN KEY (`user_id`)
+ REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE"
+ ]);
+ $table->defineTable();
+ $this->table = $table;
}
public static function getInstance(): CredentialsManager
@@ -30,18 +59,11 @@
*/
public function storeCredentials(string $service, array $credentials, ?int $userID = null): bool
{
- $credentials['updated_at'] = time();
- if ($userID) {
- return $this->storeUserCredentials($service, $credentials, $userID);
- }
- $encrypted_data = $this->encrypt(json_encode($credentials));
- return update_option($this->option_prefix . $service, $encrypted_data);
- }
-
- public function storeUserCredentials(string $service, array $credentials, int $userID): bool
- {
- $encrypted_data = $this->encrypt(json_encode($credentials));
- return update_user_meta($userID, $this->user_meta_prefix . $service, $encrypted_data);
+ $find = $this->findBy($service, $userID);
+ $update = [
+ 'credentials' => $this->encrypt(json_encode($credentials))
+ ];
+ return (bool)$this->table->findOrCreate($find, $update);
}
/**
@@ -49,30 +71,13 @@
*/
public function getCredentials(string $service, ?int $userID = null): array
{
- if ($userID) {
- return $this->getUserCredentials($service, $userID);
- }
-
- $encrypted_data = get_option($this->option_prefix . $service, '');
-
- if (empty($encrypted_data)) {
+ $find = $this->findBy($service, $userID);
+ $credentials = $this->table->pluck('credentials', $find);
+ if (empty($credentials)) {
return [];
}
-
- $decrypted_data = $this->decrypt($encrypted_data);
- return $decrypted_data ? json_decode($decrypted_data, true) : [];
- }
-
- public function getUserCredentials(string $service, int $userID): array
- {
- $encrypted_data = get_user_meta($userID, $this->user_meta_prefix . $service, true);
-
- if (empty($encrypted_data)) {
- return [];
- }
-
- $decrypted_data = $this->decrypt($encrypted_data);
- return $decrypted_data ? json_decode($decrypted_data, true) : [];
+ $decrypted_data = $this->decrypt($credentials[0]);
+ return empty($decrypted_data) ? [] : json_decode($decrypted_data, true);
}
/**
@@ -80,16 +85,16 @@
*/
public function deleteCredentials(string $service, ?int $userID = null): bool
{
-
- if ($userID) {
- return $this->deleteUserCredentials($service, $userID);
- }
- return delete_option($this->option_prefix . $service);
+ $find = $this->findBy($service, $userID);
+ return $this->table->delete($find);
}
- public function deleteUserCredentials(string $service, int $userID): bool
+ protected function findBy(string $service, ?int $userID = null):array
{
- return delete_user_meta($userID, $this->user_meta_prefix . $service);
+ return [
+ 'user_id' => is_null($userID) ? 0 : $userID,
+ 'integration'=> $service
+ ];
}
/**
@@ -97,31 +102,15 @@
*/
public function hasCredentials(string $service, ?int $userID = null): bool
{
- if ($userID) {
- return $this->hasUserCredentials($service, $userID);
- }
- return !empty(get_option($this->option_prefix . $service, ''));
- }
-
- public function hasUserCredentials(string $service, int $userID): bool
- {
- $encrypted_data = get_user_meta($userID, $this->user_meta_prefix . $service, true);
- return !empty($encrypted_data);
+ return !empty($this->getCredentials($service, $userID));
}
/**
* Get or create encryption key
*/
- private function getEncryptionKey(): string
+ private function getEncryptionKey():void
{
- $key = get_option('jvb_encryption_key');
-
- if (!$key) {
- $key = base64_encode(random_bytes(32));
- update_option('jvb_encryption_key', $key);
- }
-
- return base64_decode($key);
+ $this->encryption_key = JVB_KEY;
}
/**
@@ -151,14 +140,6 @@
*/
public function getUsersWithCredentials(string $service): array
{
- global $wpdb;
-
- $meta_key = $this->user_meta_prefix . $service;
- $results = $wpdb->get_results($wpdb->prepare(
- "SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = %s",
- $meta_key
- ));
-
- return wp_list_pluck($results, 'user_id');
+ return $this->table->pluck('user_id', ['integration' => $service]);
}
}
diff --git a/inc/integrations/Integrations.php b/inc/integrations/Integrations.php
index f7ce033..6f6232c 100644
--- a/inc/integrations/Integrations.php
+++ b/inc/integrations/Integrations.php
@@ -2312,93 +2312,6 @@
}
- protected function getTermFieldMapping(string $taxonomy): array
- {
- return apply_filters(
- "jvb_{$this->service_name}_term_field_mapping",
- [],
- $taxonomy,
- $this
- );
- }
-
- /**
- * Share post to external service (override in child classes)
- */
- protected function sharePost(WP_Post $post): ?string
- {
- throw new Exception('sharePost must be implemented by child class');
- }
-
- /**
- * Update shared post on external service (override in child classes)
- */
- protected function updateSharedPost(WP_Post $post, string $external_id): void
- {
- throw new Exception('updateSharedPost must be implemented by child class');
- }
-
- /**
- * Remove post from external service (override in child classes)
- */
- protected function unsharePost(WP_Post $post, string $external_id): void
- {
- throw new Exception('unsharePost must be implemented by child class');
- }
-
- /**
- * Get sync capabilities
- */
- public function getSyncCapabilities(): array
- {
- return $this->canSync;
- }
-
- /**
- * Check if a specific sync capability is supported
- */
- public function supportsSyncCapability(string $capability): bool
- {
- return $this->canSync[$capability] ?? false;
- }
-
- /**
- * Get posts shared to this service
- */
- public function getSharedPosts(array $args = []): array
- {
- $defaults = [
- 'post_type' => $this->syncPostTypes,
- 'meta_query' => [
- [
- 'key' => BASE."_{$this->service_name}_item_id",
- 'compare' => 'EXISTS'
- ]
- ],
- 'posts_per_page' => -1
- ];
-
- $args = wp_parse_args($args, $defaults);
- return get_posts($args);
- }
-
- /**
- * Check if post is shared to this service
- */
- public function isPostShared(int $postID): bool
- {
- $external_id = get_post_meta($postID, BASE."_{$this->service_name}_item_id", true);
- return $external_id !== '';
- }
-
- /**
- * Get external ID for a post
- */
- public function getPostExternalId(int $postID): ?string
- {
- $external_id = get_post_meta($postID, BASE."_{$this->service_name}_item_id", true);
- return $external_id ?: null;
- }
/*******************************************************************
* UTILITIES
@@ -2793,14 +2706,6 @@
return strtolower($this->service_name);
}
- /**
- * Refresh credentials from storage and re-initialize
- */
- public function refreshCredentials(): void
- {
- $this->credentials = [];
- $this->ensureInitialized();
- }
/**
* Enhanced webhook handling with common patterns
@@ -4001,8 +3906,74 @@
/*********************************************************************************
* Syncing WP items from Service
*********************************************************************************/
+ /**
+ * @param array $itemData
+ * @return bool
+ * @throws Exception
+ */
protected function createItemFromService(array $itemData):bool
{
+ throw new Exception('createItemFromService must be implemented by child class '.$this->service_name);
+ }
+ /**
+ * @param array $itemData
+ * @return bool
+ * @throws Exception
+ */
+ protected function updateItemFromService(array $itemData):bool
+ {
+ throw new Exception('updateItemFromService must be implemented by child class '.$this->service_name);
+ }
+
+ /**
+ * @param array $itemData
+ * @return bool
+ * @throws Exception
+ */
+ protected function deleteItemFromService(array $itemData):bool
+ {
+ throw new Exception('deleteItemFromService must be implemented by child class '.$this->service_name);
+ }
+
+ /****************************************************************
+ * CONNECT to a user's integration
+ ****************************************************************/
+ protected array $connections = [];
+ public function connectAs(?int $userID = null):bool
+ {
+ if (!$this->userCanConnect($userID)) {
+ return false;
+ }
+ $this->cleanup();
+ $this->userID = $userID;
+ $this->ensureInitialized();
+ return true;
+ }
+
+ public function userCanConnect(?int $userID):bool
+ {
+ $user = get_userdata($userID);
+ if (!$user || is_wp_error($user)) {
+ return false;
+ }
+ if (current_user_can('manage_options')) {
+ return true;
+ }
+ $role = jvbUserRole($userID);
+ $registrar = Registrar::getInstance($role);
+ if (!$registrar || !$registrar->hasIntegration($this->service_name)) {
+ return false;
+ }
+
+ return (bool) Meta::forUser($userID)->get('integration_'.$this->service_name.'_connected');
+ }
+
+ protected function cleanup():void
+ {
+ $this->userID = false;
+ $this->credentials = [];
+ $this->request_history = [];
+ $this->resetTokenRefreshFlag();
}
}
diff --git a/inc/registrar/Fields.php b/inc/registrar/Fields.php
index c8a2d95..f9668f2 100644
--- a/inc/registrar/Fields.php
+++ b/inc/registrar/Fields.php
@@ -36,6 +36,9 @@
break;
case 'user':
$this->addUserFields();
+ if ($registrar->hasAnyIntegrations()) {
+ $this->addIntegrationSetupField();
+ }
break;
}
}
@@ -458,4 +461,17 @@
{
return (new self())->defaultUserFields();
}
+
+ protected function addIntegrationSetupField():void
+ {
+ $integrations = $this->registrar->getIntegrations();
+ foreach (array_keys($integrations) as $integration) {
+ $this->addField('integration_'.$integration.'_connected', [
+ 'type' => 'true_false',
+ 'hidden' => true,
+ 'label' => 'Connected to '.$integration,
+ 'default' => false,
+ ]);
+ }
+ }
}
--
Gitblit v1.10.0