diff --git a/backend/Actions/ACPT/ACPTController.php b/backend/Actions/ACPT/ACPTController.php index af0fc8825..8ef63013a 100644 --- a/backend/Actions/ACPT/ACPTController.php +++ b/backend/Actions/ACPT/ACPTController.php @@ -6,7 +6,7 @@ namespace BitApps\Integrations\Actions\ACPT; -use BitApps\Integrations\Core\Util\HttpHelper; +use BitApps\Integrations\Authorization\AuthorizationType; use WP_Error; /** @@ -14,27 +14,14 @@ */ class ACPTController { - protected $_defaultHeader; - - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->setHeaders($fieldsRequestParams->api_key); - - $apiEndpoint = $fieldsRequestParams->base_url . '/wp-json/acpt/v1/taxonomy'; - $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); - - if (is_wp_error($response) || HttpHelper::$responseCode != 200) { - wp_send_json_error( - !empty($response->message) - ? $response->message - : __('Please enter valid Api key-secret', 'bit-integrations'), - HttpHelper::$responseCode - ); - } - - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'acpt', + 'fields' => [ + 'api_key' => 'value', + 'base_url' => 'base_url', + ], + ]; public function execute($integrationData, $fieldValues) { @@ -59,20 +46,4 @@ public function execute($integrationData, $fieldValues) return $acptApiResponse; } - - private function checkValidation($fieldsRequestParams, $customParam = '**') - { - if (empty($fieldsRequestParams->base_url) || empty($fieldsRequestParams->api_key) || empty($customParam)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - } - - private function setHeaders($apiKey) - { - $this->_defaultHeader = [ - 'acpt-api-key' => $apiKey, - 'Content-Type' => 'application/json', - 'accept' => 'application/json', - ]; - } } diff --git a/backend/Actions/ACPT/Routes.php b/backend/Actions/ACPT/Routes.php index 981f2b9fb..51dc11fcf 100644 --- a/backend/Actions/ACPT/Routes.php +++ b/backend/Actions/ACPT/Routes.php @@ -6,5 +6,3 @@ use BitApps\Integrations\Actions\ACPT\ACPTController; use BitApps\Integrations\Core\Util\Route; - -Route::post('acpt_authentication', [ACPTController::class, 'authentication']); diff --git a/backend/Actions/AcademyLms/AcademyLmsController.php b/backend/Actions/AcademyLms/AcademyLmsController.php index ed7a0f31a..eabb978bc 100644 --- a/backend/Actions/AcademyLms/AcademyLmsController.php +++ b/backend/Actions/AcademyLms/AcademyLmsController.php @@ -20,22 +20,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - /** - * Process ajax request for generate_token - * - * @return JSON zoho crm api response and status - */ - public static function Authorization() - { - include_once ABSPATH . 'wp-admin/includes/plugin.php'; - if (is_plugin_active('academy/academy.php')) { - wp_send_json_success(true, 200); - } - - // translators: %s: Plugin name - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'Academy Lms')); - } - public static function getAllLesson() { if (!class_exists('Academy')) { diff --git a/backend/Actions/AcademyLms/Routes.php b/backend/Actions/AcademyLms/Routes.php index a655d3623..ff94a62a9 100644 --- a/backend/Actions/AcademyLms/Routes.php +++ b/backend/Actions/AcademyLms/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\AcademyLms\AcademyLmsController; use BitApps\Integrations\Core\Util\Route; -Route::post('academy_lms_authorize', [AcademyLmsController::class, 'Authorization']); Route::get('academy_lms_all_course', [AcademyLmsController::class, 'getAllCourse']); Route::get('academy_lms_all_lesson', [AcademyLmsController::class, 'getAllLesson']); diff --git a/backend/Actions/ActiveCampaign/ActiveCampaignController.php b/backend/Actions/ActiveCampaign/ActiveCampaignController.php index d3be0cbd6..ceb0fa833 100644 --- a/backend/Actions/ActiveCampaign/ActiveCampaignController.php +++ b/backend/Actions/ActiveCampaign/ActiveCampaignController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ActiveCampaign; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,15 @@ */ class ActiveCampaignController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'activecampaign', + 'fields' => [ + 'api_key' => 'value', + 'api_url' => 'api_url', + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -26,42 +36,6 @@ public static function _apiEndpoint($api_url, $method) return "{$api_url}/api/3/{$method}/"; } - /** - * Process ajax request - * - * @param $requestsParams Params to authorize - * - * @return JSON Active Campaign api response and status - */ - public static function activeCampaignAuthorize($requestsParams) - { - if ( - empty($requestsParams->api_key) - || empty($requestsParams->api_url) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = self::_apiEndpoint($requestsParams->api_url, 'accounts'); - $authorizationHeader['Api-Token'] = $requestsParams->api_key; - $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); - - if (is_wp_error($apiResponse) || empty($apiResponse)) { - wp_send_json_error( - empty($apiResponse) ? 'Unknown' : $apiResponse, - 400 - ); - } - - wp_send_json_success(true); - } - /** * Process ajax request for refresh lists * diff --git a/backend/Actions/ActiveCampaign/Routes.php b/backend/Actions/ActiveCampaign/Routes.php index b5e496266..ae12ead36 100644 --- a/backend/Actions/ActiveCampaign/Routes.php +++ b/backend/Actions/ActiveCampaign/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\ActiveCampaign\ActiveCampaignController; use BitApps\Integrations\Core\Util\Route; -Route::post('aCampaign_authorize', [ActiveCampaignController::class, 'activeCampaignAuthorize']); Route::post('aCampaign_headers', [ActiveCampaignController::class, 'activeCampaignHeaders']); Route::post('aCampaign_lists', [ActiveCampaignController::class, 'activeCampaignLists']); Route::post('aCampaign_accounts', [ActiveCampaignController::class, 'activeCampaignAccounts']); diff --git a/backend/Actions/Acumbamail/AcumbamailController.php b/backend/Actions/Acumbamail/AcumbamailController.php index 3f83d53f5..572d3c053 100644 --- a/backend/Actions/Acumbamail/AcumbamailController.php +++ b/backend/Actions/Acumbamail/AcumbamailController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Acumbamail; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,14 @@ */ class AcumbamailController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'acumbamail', + 'fields' => [ + 'auth_token' => 'value', + ], + ]; + private $baseUrl = 'https://acumbamail.com/api/1/'; public function fetchAllLists($requestParams) @@ -46,32 +55,6 @@ public function fetchAllLists($requestParams) } } - public function acumbamailAuthAndFetchSubscriberList($requestParams) - { - if (empty($requestParams->auth_token)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $apiEndpoints = $this->baseUrl . 'getSubscribers/'; - - $requestParams = [ - 'auth_token' => $requestParams->auth_token, - ]; - - $response = HttpHelper::post($apiEndpoints, $requestParams); - - if ($response == 'Unauthorized' || $response == 'This endpoint is not available for non-paying customers' || $response == 'Your auth token has expired check /apidoc/ for the new one') { - wp_send_json_error($response, 400); - } else { - wp_send_json_success($response, 200); - } - } - public function acumbamailRefreshFields($refreshFieldsRequestParams) { if (empty($refreshFieldsRequestParams->auth_token) || empty($refreshFieldsRequestParams->list_id)) { diff --git a/backend/Actions/Acumbamail/Routes.php b/backend/Actions/Acumbamail/Routes.php index 3abc1e6fe..9dd043ab0 100644 --- a/backend/Actions/Acumbamail/Routes.php +++ b/backend/Actions/Acumbamail/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Acumbamail\AcumbamailController; use BitApps\Integrations\Core\Util\Route; -Route::post('acumbamail_authorization_and_fetch_subscriber_list', [AcumbamailController::class, 'acumbamailAuthAndFetchSubscriberList']); Route::post('acumbamail_fetch_all_list', [AcumbamailController::class, 'fetchAllLists']); Route::post('acumbamail_refresh_fields', [AcumbamailController::class, 'acumbamailRefreshFields']); diff --git a/backend/Actions/Affiliate/AffiliateController.php b/backend/Actions/Affiliate/AffiliateController.php index 320e10ded..40c17c99e 100644 --- a/backend/Actions/Affiliate/AffiliateController.php +++ b/backend/Actions/Affiliate/AffiliateController.php @@ -30,16 +30,6 @@ public static function pluginActive($option = null) return false; } - public static function authorizeAffiliate() - { - include_once ABSPATH . 'wp-admin/includes/plugin.php'; - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'Affiliate')); - } - public static function getAllAffiliate() { $cache_key = Config::withPrefix('affiliate_wp_all_affiliates'); diff --git a/backend/Actions/Affiliate/Routes.php b/backend/Actions/Affiliate/Routes.php index 4f72f66f6..b2fdb9317 100644 --- a/backend/Actions/Affiliate/Routes.php +++ b/backend/Actions/Affiliate/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\Affiliate\AffiliateController; use BitApps\Integrations\Core\Util\Route; -Route::post('affiliate_authorize', [AffiliateController::class, 'authorizeAffiliate']); Route::post('affiliate_fetch_all_affiliate', [AffiliateController::class, 'getAllAffiliate']); diff --git a/backend/Actions/AgiledCRM/AgiledCRMController.php b/backend/Actions/AgiledCRM/AgiledCRMController.php index d9965882f..86346b741 100644 --- a/backend/Actions/AgiledCRM/AgiledCRMController.php +++ b/backend/Actions/AgiledCRM/AgiledCRMController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\AgiledCRM; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,29 +15,16 @@ */ class AgiledCRMController { - protected $_defaultHeader; - - public function authentication($fieldsRequestParams) - { - if (empty($fieldsRequestParams->auth_token) || empty($fieldsRequestParams->brand)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'agiledcrm', + 'fields' => [ + 'auth_token' => 'value', + 'brand' => 'brand', + ], + ]; - $brand = $fieldsRequestParams->brand; - $apiKey = $fieldsRequestParams->auth_token; - $apiEndpoint = "https://my.agiled.app/api/v1/users?api_token={$apiKey}"; - $header = [ - 'Brand' => $brand - ]; - - $response = HttpHelper::get($apiEndpoint, null, $header); - - if (isset($response->data[0]->id)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid Brand name & API key', 'bit-integrations'), 400); - } - } + protected $_defaultHeader; public function getAllOwners($fieldsRequestParams) { diff --git a/backend/Actions/AgiledCRM/Routes.php b/backend/Actions/AgiledCRM/Routes.php index 7d60da9fa..6972919eb 100644 --- a/backend/Actions/AgiledCRM/Routes.php +++ b/backend/Actions/AgiledCRM/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\AgiledCRM\AgiledCRMController; use BitApps\Integrations\Core\Util\Route; -Route::post('agiled_authentication', [AgiledCRMController::class, 'authentication']); Route::post('agiled_fetch_all_owners', [AgiledCRMController::class, 'getAllOwners']); Route::post('agiled_fetch_all_accounts', [AgiledCRMController::class, 'getAllAccounts']); Route::post('agiled_fetch_all_sources', [AgiledCRMController::class, 'getAllSources']); diff --git a/backend/Actions/Airtable/AirtableController.php b/backend/Actions/Airtable/AirtableController.php index 5961cb54e..848866f32 100644 --- a/backend/Actions/Airtable/AirtableController.php +++ b/backend/Actions/Airtable/AirtableController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Airtable; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,9 +15,17 @@ */ class AirtableController { + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'airtable', + 'fields' => [ + 'auth_token' => 'token', + ], + ]; + protected $_defaultHeader; - public function authentication($fieldsRequestParams) + public function fetchAllBases($fieldsRequestParams) { if (empty($fieldsRequestParams->auth_token)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); @@ -31,6 +40,7 @@ public function authentication($fieldsRequestParams) $response = HttpHelper::get($apiEndpoint, null, $header); if (isset($response->bases)) { + $bases = []; foreach ($response->bases as $base) { if ($base->permissionLevel === 'create') { $bases[] = [ @@ -61,6 +71,7 @@ public function getAllTables($fieldsRequestParams) $response = HttpHelper::get($apiEndpoint, null, $header); if (isset($response->tables)) { + $tables = []; foreach ($response->tables as $table) { $tables[] = [ 'id' => $table->id, @@ -91,6 +102,7 @@ public function getAllFields($fieldsRequestParams) $acceptedTypes = ['singleLineText', 'multilineText', 'singleSelect', 'multipleSelects', 'multipleAttachments', 'date', 'phoneNumber', 'email', 'url', 'number', 'currency', 'percent', 'duration', 'rating', 'barcode']; if (isset($response->tables)) { + $fields = []; foreach ($response->tables as $table) { if ($table->id === $tableId) { foreach ($table->fields as $field) { diff --git a/backend/Actions/Airtable/Routes.php b/backend/Actions/Airtable/Routes.php index 5993a390b..38e18d854 100644 --- a/backend/Actions/Airtable/Routes.php +++ b/backend/Actions/Airtable/Routes.php @@ -7,6 +7,6 @@ use BitApps\Integrations\Actions\Airtable\AirtableController; use BitApps\Integrations\Core\Util\Route; -Route::post('airtable_authentication', [AirtableController::class, 'authentication']); +Route::post('airtable_fetch_all_bases', [AirtableController::class, 'fetchAllBases']); Route::post('airtable_fetch_all_tables', [AirtableController::class, 'getAllTables']); Route::post('airtable_fetch_all_fields', [AirtableController::class, 'getAllFields']); diff --git a/backend/Actions/Asana/AsanaController.php b/backend/Actions/Asana/AsanaController.php index 3c268fbda..1ac477b70 100644 --- a/backend/Actions/Asana/AsanaController.php +++ b/backend/Actions/Asana/AsanaController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Asana; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,7 +15,13 @@ */ class AsanaController { - protected $_defaultHeader; + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'asana', + 'fields' => [ + 'api_key' => 'token', + ], + ]; protected $apiEndpoint; @@ -23,27 +30,6 @@ public function __construct() $this->apiEndpoint = 'https://app.asana.com/api/1.0/'; } - public function authentication($fieldsRequestParams) - { - if (empty($fieldsRequestParams->api_key)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiKey = $fieldsRequestParams->api_key; - $apiEndpoint = $this->apiEndpoint . 'users/me'; - $headers = [ - 'Authorization' => 'Bearer ' . $apiKey, - ]; - - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (isset($response->data)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API key', 'bit-integrations'), 400); - } - } - public function getCustomFields($fieldsRequestParams) { if (empty($fieldsRequestParams->api_key)) { diff --git a/backend/Actions/Asana/Routes.php b/backend/Actions/Asana/Routes.php index 34ac3470b..d780696b5 100644 --- a/backend/Actions/Asana/Routes.php +++ b/backend/Actions/Asana/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Asana\AsanaController; use BitApps\Integrations\Core\Util\Route; -Route::post('asana_authentication', [AsanaController::class, 'authentication']); Route::post('asana_fetch_custom_fields', [AsanaController::class, 'getCustomFields']); Route::post('asana_fetch_all_tasks', [AsanaController::class, 'getAllTasks']); Route::post('asana_fetch_all_Projects', [AsanaController::class, 'getAllProjects']); diff --git a/backend/Actions/AsgarosForum/AsgarosForumController.php b/backend/Actions/AsgarosForum/AsgarosForumController.php index 051948f25..8f6ee88a6 100644 --- a/backend/Actions/AsgarosForum/AsgarosForumController.php +++ b/backend/Actions/AsgarosForum/AsgarosForumController.php @@ -7,12 +7,6 @@ */ class AsgarosForumController { - public static function asgarosForumAuthorize() - { - self::checkPluginExists(); - wp_send_json_success(true); - } - public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; @@ -22,11 +16,4 @@ public function execute($integrationData, $fieldValues) return $recordApiHelper->execute($fieldValues, $fieldMap); } - - private static function checkPluginExists() - { - if (!class_exists('AsgarosForum')) { - wp_send_json_error(__('Asgaros Forum is not activated or not installed', 'bit-integrations'), 400); - } - } } diff --git a/backend/Actions/AsgarosForum/Routes.php b/backend/Actions/AsgarosForum/Routes.php deleted file mode 100644 index 6eeca47c5..000000000 --- a/backend/Actions/AsgarosForum/Routes.php +++ /dev/null @@ -1,10 +0,0 @@ - AuthorizationType::API_KEY, + 'slug' => 'benchmark', + 'fields' => [ + 'api_secret' => 'value', + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -26,41 +35,6 @@ public static function _apiEndpoint($method) return "https://clientapi.benchmarkemail.com/{$method}"; } - /** - * Process ajax request - * - * @param $requestsParams Params to authorize - * - * @return JSON Benchmark api response and status - */ - public static function benchMarkAuthorize($requestsParams) - { - if (empty($requestsParams->api_secret) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = self::_apiEndpoint('Client/'); - - $authorizationHeader['AuthToken'] = $requestsParams->api_secret; - $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); - - if (is_wp_error($apiResponse) || empty($apiResponse)) { - wp_send_json_error( - empty($apiResponse) ? 'Unknown' : $apiResponse, - 400 - ); - } - - wp_send_json_success(true); - } - /** * Process ajax request for refresh Lists * diff --git a/backend/Actions/BenchMark/Routes.php b/backend/Actions/BenchMark/Routes.php index aa1b75edf..996408d8f 100644 --- a/backend/Actions/BenchMark/Routes.php +++ b/backend/Actions/BenchMark/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\BenchMark\BenchMarkController; use BitApps\Integrations\Core\Util\Route; -Route::post('benchMark_authorize', [BenchMarkController::class, 'benchMarkAuthorize']); Route::post('benchMark_headers', [BenchMarkController::class, 'benchMarkHeaders']); Route::post('benchMark_lists', [BenchMarkController::class, 'benchMarkLists']); diff --git a/backend/Actions/Bento/BentoController.php b/backend/Actions/Bento/BentoController.php index 5fd99b32a..6a72923b4 100644 --- a/backend/Actions/Bento/BentoController.php +++ b/backend/Actions/Bento/BentoController.php @@ -6,9 +6,9 @@ namespace BitApps\Integrations\Actions\Bento; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Config; use BitApps\Integrations\Core\Util\Hooks; -use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; /** @@ -16,24 +16,15 @@ */ class BentoController { - protected $_defaultHeader; - - public function authentication($fieldsRequestParams) - { - BentoHelper::checkValidation($fieldsRequestParams); - - $headers = BentoHelper::setHeaders($fieldsRequestParams->publishable_key, $fieldsRequestParams->secret_key); - $apiEndpoint = BentoHelper::setEndpoint('tags', $fieldsRequestParams->site_uuid); - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (BentoHelper::checkResponseCode()) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - - return; - } - - wp_send_json_error(!empty($response) ? $response : __('Please enter valid Publishable Key, Secret Key & Site UUID', 'bit-integrations'), 400); - } + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'bento', + 'fields' => [ + 'publishable_key' => 'username', + 'secret_key' => 'password', + 'site_uuid' => 'site_uuid', + ], + ]; public function getAllFields($fieldsRequestParams) { diff --git a/backend/Actions/Bento/Routes.php b/backend/Actions/Bento/Routes.php index 764aa9011..1dd87a4f4 100644 --- a/backend/Actions/Bento/Routes.php +++ b/backend/Actions/Bento/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Bento\BentoController; use BitApps\Integrations\Core\Util\Route; -Route::post('bento_authentication', [BentoController::class, 'authentication']); Route::post('bento_get_fields', [BentoController::class, 'getAllFields']); Route::post('bento_get_all_tags', [BentoController::class, 'getAlTags']); diff --git a/backend/Actions/BitForm/BitFormController.php b/backend/Actions/BitForm/BitFormController.php index 3a2034e0a..331b551a8 100644 --- a/backend/Actions/BitForm/BitFormController.php +++ b/backend/Actions/BitForm/BitFormController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\BitForm; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,36 +15,14 @@ */ class BitFormController { - public function bitFormAuthorization($requestParams) - { - if ( - empty($requestParams->app_domain) - || empty($requestParams->api_key) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $authorizationHeader = [ - 'Bitform-Api-Key' => $requestParams->api_key - ]; - - $apiEndpoint = $requestParams->app_domain . '/wp-json/bitform/v1/forms'; - $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader, ['sslverify' => false]); - - if ($apiResponse->success) { - wp_send_json_success($apiResponse, 200); - } else { - wp_send_json_error( - 'There is an error .', - 400 - ); - } - } + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'bitform', + 'fields' => [ + 'api_key' => 'value', + 'domainName' => 'domainName', + ], + ]; public function bitFormAllFormList($requestParams) { diff --git a/backend/Actions/BitForm/Routes.php b/backend/Actions/BitForm/Routes.php index 027e8ac7d..50d68f110 100644 --- a/backend/Actions/BitForm/Routes.php +++ b/backend/Actions/BitForm/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\BitForm\BitFormController; use BitApps\Integrations\Core\Util\Route; -Route::post('bitForm_authorization_and_fetch_form_list', [BitFormController::class, 'bitFormAuthorization']); Route::post('bitForm_all_form_list', [BitFormController::class, 'bitFormAllFormList']); Route::post('bitForm_fetch_single_form_fields', [BitFormController::class, 'bitFormFetchSingleFormFields']); diff --git a/backend/Actions/BuddyBoss/BuddyBossController.php b/backend/Actions/BuddyBoss/BuddyBossController.php index 2e3362a2b..6177c14bb 100644 --- a/backend/Actions/BuddyBoss/BuddyBossController.php +++ b/backend/Actions/BuddyBoss/BuddyBossController.php @@ -19,16 +19,6 @@ public static function pluginActive($option = null) return (bool) (class_exists('BuddyPress')); } - public static function authorizeBuddyBoss() - { - include_once ABSPATH . 'wp-admin/includes/plugin.php'; - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'BuddyBoss')); - } - public static function getAllGroups() { include_once ABSPATH . 'wp-admin/includes/plugin.php'; diff --git a/backend/Actions/BuddyBoss/Routes.php b/backend/Actions/BuddyBoss/Routes.php index 25f1b9535..0ac3fd4dd 100644 --- a/backend/Actions/BuddyBoss/Routes.php +++ b/backend/Actions/BuddyBoss/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\BuddyBoss\BuddyBossController; use BitApps\Integrations\Core\Util\Route; -Route::post('buddyBoss_authorize', [BuddyBossController::class, 'authorizeBuddyBoss']); Route::post('fetch_all_group', [BuddyBossController::class, 'getAllGroups']); Route::post('fetch_all_user', [BuddyBossController::class, 'getAllUser']); Route::post('fetch_all_forum', [BuddyBossController::class, 'getAllForums']); diff --git a/backend/Actions/CampaignMonitor/CampaignMonitorController.php b/backend/Actions/CampaignMonitor/CampaignMonitorController.php index 0043c3af5..36c1c961f 100644 --- a/backend/Actions/CampaignMonitor/CampaignMonitorController.php +++ b/backend/Actions/CampaignMonitor/CampaignMonitorController.php @@ -2,11 +2,21 @@ namespace BitApps\Integrations\Actions\CampaignMonitor; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; class CampaignMonitorController { + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'campaignmonitor', + 'fields' => [ + 'api_key' => 'username', + 'client_id' => 'client_id', + ], + ]; + private $baseUrl; public function __construct() @@ -14,22 +24,6 @@ public function __construct() $this->baseUrl = 'https://api.createsend.com/api/v3.3'; } - public function authorization($requestParams) - { - $this->checkValidation($requestParams->api_key, $requestParams->client_id); - $apiEndpoint = $this->baseUrl . "/clients/{$requestParams->client_id}.json"; - $headers = $this->setHeader($requestParams->api_key); - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (!isset($response->ApiKey)) { - wp_send_json_error( - empty($response) ? 'Unknown' : $response, - 400 - ); - } - wp_send_json_success(true); - } - public function getAllLists($requestParams) { $this->checkValidation($requestParams->api_key, $requestParams->client_id); diff --git a/backend/Actions/CampaignMonitor/Routes.php b/backend/Actions/CampaignMonitor/Routes.php index 4e1d3c6ae..620b11b50 100644 --- a/backend/Actions/CampaignMonitor/Routes.php +++ b/backend/Actions/CampaignMonitor/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\CampaignMonitor\CampaignMonitorController; use BitApps\Integrations\Core\Util\Route; -Route::post('campaign_monitor_authorize', [CampaignMonitorController::class, 'authorization']); Route::post('campaign_monitor_lists', [CampaignMonitorController::class, 'getAllLists']); Route::post('campaign_monitor_custom_fields', [CampaignMonitorController::class, 'getCustomFields']); diff --git a/backend/Actions/CapsuleCRM/CapsuleCRMController.php b/backend/Actions/CapsuleCRM/CapsuleCRMController.php index ef644eccb..c6e1b2f7d 100644 --- a/backend/Actions/CapsuleCRM/CapsuleCRMController.php +++ b/backend/Actions/CapsuleCRM/CapsuleCRMController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\CapsuleCRM; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,15 @@ */ class CapsuleCRMController { + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'capsulecrm', + 'fields' => [ + 'api_key' => 'token', + 'api_url' => 'api_url', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; @@ -23,27 +33,6 @@ public function __construct() $this->apiEndpoint = 'https://api.capsulecrm.com/api/v2'; } - public function authentication($fieldsRequestParams) - { - if (empty($fieldsRequestParams->api_key)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiKey = $fieldsRequestParams->api_key; - $apiEndpoint = $this->apiEndpoint . '/users'; - $headers = [ - 'Authorization' => 'Bearer ' . $apiKey, - ]; - - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (isset($response->users)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API key', 'bit-integrations'), 400); - } - } - public function getCustomFields($fieldsRequestParams) { if (empty($fieldsRequestParams->api_key)) { diff --git a/backend/Actions/CapsuleCRM/Routes.php b/backend/Actions/CapsuleCRM/Routes.php index e6ebd62d1..21103a3fb 100644 --- a/backend/Actions/CapsuleCRM/Routes.php +++ b/backend/Actions/CapsuleCRM/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\CapsuleCRM\CapsuleCRMController; use BitApps\Integrations\Core\Util\Route; -Route::post('capsulecrm_authentication', [CapsuleCRMController::class, 'authentication']); Route::post('capsulecrm_fetch_custom_fields', [CapsuleCRMController::class, 'getCustomFields']); Route::post('capsulecrm_fetch_all_opportunities', [CapsuleCRMController::class, 'getAllOpportunities']); Route::post('capsulecrm_fetch_all_owners', [CapsuleCRMController::class, 'getAllOwners']); diff --git a/backend/Actions/Clickup/ClickupController.php b/backend/Actions/Clickup/ClickupController.php index 1878bf39b..3b77644fa 100644 --- a/backend/Actions/Clickup/ClickupController.php +++ b/backend/Actions/Clickup/ClickupController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Clickup; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,14 @@ */ class ClickupController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'clickup', + 'fields' => [ + 'api_key' => 'value', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; @@ -23,27 +32,6 @@ public function __construct() $this->apiEndpoint = 'https://api.clickup.com/api/v2/'; } - public function authentication($fieldsRequestParams) - { - if (empty($fieldsRequestParams->api_key)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiKey = $fieldsRequestParams->api_key; - $apiEndpoint = $this->apiEndpoint . 'user'; - $headers = [ - 'Authorization' => $apiKey, - ]; - - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (isset($response->user)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API key', 'bit-integrations'), 400); - } - } - public function getCustomFields($fieldsRequestParams) { if (empty($fieldsRequestParams->api_key)) { diff --git a/backend/Actions/Clickup/Routes.php b/backend/Actions/Clickup/Routes.php index 3f486e49b..361318e93 100644 --- a/backend/Actions/Clickup/Routes.php +++ b/backend/Actions/Clickup/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Clickup\ClickupController; use BitApps\Integrations\Core\Util\Route; -Route::post('clickup_authentication', [ClickupController::class, 'authentication']); Route::post('clickup_fetch_custom_fields', [ClickupController::class, 'getCustomFields']); Route::post('clickup_fetch_all_tasks', [ClickupController::class, 'getAllTasks']); Route::post('clickup_fetch_all_Teams', [ClickupController::class, 'getAllTeams']); diff --git a/backend/Actions/ClinchPad/ClinchPadController.php b/backend/Actions/ClinchPad/ClinchPadController.php index f52b4748c..650c0c242 100644 --- a/backend/Actions/ClinchPad/ClinchPadController.php +++ b/backend/Actions/ClinchPad/ClinchPadController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ClinchPad; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,14 @@ */ class ClinchPadController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'clinchpad', + 'fields' => [ + 'api_key' => 'value', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; @@ -23,27 +32,6 @@ public function __construct() $this->apiEndpoint = 'https://www.clinchpad.com/api/v1'; } - public function authentication($fieldsRequestParams) - { - if (empty($fieldsRequestParams->api_key)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiKey = $fieldsRequestParams->api_key; - $apiEndpoint = $this->apiEndpoint . '/users'; - $headers = [ - 'Authorization' => 'Basic ' . base64_encode("api-key:{$apiKey}") - ]; - - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (isset($response)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API key', 'bit-integrations'), 400); - } - } - public function getAllParentOrganizations($fieldsRequestParams) { if (empty($fieldsRequestParams->api_key)) { diff --git a/backend/Actions/ClinchPad/Routes.php b/backend/Actions/ClinchPad/Routes.php index 4c2735431..012a54786 100644 --- a/backend/Actions/ClinchPad/Routes.php +++ b/backend/Actions/ClinchPad/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\ClinchPad\ClinchPadController; use BitApps\Integrations\Core\Util\Route; -Route::post('clinchPad_authentication', [ClinchPadController::class, 'authentication']); Route::post('clinchPad_fetch_custom_fields', [ClinchPadController::class, 'getCustomFields']); Route::post('clinchPad_fetch_all_leads', [ClinchPadController::class, 'getAllLeads']); Route::post('clinchPad_fetch_all_parentOrganizations', [ClinchPadController::class, 'getAllParentOrganizations']); diff --git a/backend/Actions/CompanyHub/CompanyHubController.php b/backend/Actions/CompanyHub/CompanyHubController.php index 223980a4f..ce0e06c1f 100644 --- a/backend/Actions/CompanyHub/CompanyHubController.php +++ b/backend/Actions/CompanyHub/CompanyHubController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\CompanyHub; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,15 @@ */ class CompanyHubController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'companyhub', + 'fields' => [ + 'api_key' => 'value', + 'sub_domain' => 'sub_domain', + ], + ]; + protected $_defaultHeader; protected $_apiEndpoint; @@ -23,20 +33,6 @@ public function __construct() $this->_apiEndpoint = 'https://api.companyhub.com/v1'; } - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->setHeaders($fieldsRequestParams->sub_domain, $fieldsRequestParams->api_key); - $apiEndpoint = $this->_apiEndpoint . '/me'; - $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); - - if (!isset($response->Success) && !$response->Success) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid Sub Domain & API Key', 'bit-integrations'), 400); - } - } - public function getAllCompanies($fieldsRequestParams) { $this->checkValidation($fieldsRequestParams); diff --git a/backend/Actions/CompanyHub/Routes.php b/backend/Actions/CompanyHub/Routes.php index a5adcbb7e..01dc1e966 100644 --- a/backend/Actions/CompanyHub/Routes.php +++ b/backend/Actions/CompanyHub/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\CompanyHub\CompanyHubController; use BitApps\Integrations\Core\Util\Route; -Route::post('company_hub_authentication', [CompanyHubController::class, 'authentication']); Route::post('company_hub_fetch_all_contacts', [CompanyHubController::class, 'getAllContacts']); Route::post('company_hub_fetch_all_companies', [CompanyHubController::class, 'getAllCompanies']); diff --git a/backend/Actions/ConstantContact/ConstantContactController.php b/backend/Actions/ConstantContact/ConstantContactController.php index 2f6856751..e8d280c1d 100644 --- a/backend/Actions/ConstantContact/ConstantContactController.php +++ b/backend/Actions/ConstantContact/ConstantContactController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ConstantContact; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use BitApps\Integrations\Log\LogHandler; @@ -16,81 +17,44 @@ */ class ConstantContactController { - protected $_defaultHeader; - - public static function generateTokens($requestsParams) - { - if (empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $auth = $requestsParams->clientId . ':' . $requestsParams->clientSecret; - // Base64 encode it - $credentials = base64_encode($auth); - - $authorizationHeader['Accept'] = 'application/json'; - $authorizationHeader['Content-Type'] = 'application/x-www-form-urlencoded'; - $authorizationHeader['Authorization'] = 'Basic ' . $credentials; - - $requestParams = [ - 'code' => $requestsParams->code, - 'redirect_uri' => "{$requestsParams->redirectURI}", - 'grant_type' => 'authorization_code' - ]; - - $apiResponse = HttpHelper::post('https://authz.constantcontact.com/oauth2/default/v1/token', $requestParams, $authorizationHeader); + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'constantcontact', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } + protected $_defaultHeader; public static function refreshList($queryParams) { - if (empty($queryParams->tokenDetails) - || empty($queryParams->clientId) - || empty($queryParams->clientSecret) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); + $tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $isConnectionAuth = !empty($queryParams->connection_id); + $oldToken = $tokenDetails->access_token ?? ''; + + if (empty($oldToken)) { + wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $response = []; - if ((\intval($queryParams->tokenDetails->generates_on) + (1435 * 60)) < time()) { - $refreshedToken = ConstantContactController::_refreshAccessToken($queryParams); + if (!$isConnectionAuth && self::isTokenExpired($tokenDetails)) { + $refreshedToken = self::_refreshAccessToken((object) [ + 'clientId' => $queryParams->clientId ?? '', + 'clientSecret' => $queryParams->clientSecret ?? '', + 'tokenDetails' => $tokenDetails, + ]); - if ($refreshedToken) { - $response['tokenDetails'] = $refreshedToken; - } else { - wp_send_json_error( - __('Failed to refresh access token', 'bit-integrations'), - 400 - ); + if (!$refreshedToken) { + wp_send_json_error(__('Failed to refresh access token', 'bit-integrations'), 400); } + + $tokenDetails = $refreshedToken; } - $apiEndpoint = 'https://api.cc.email/v3/contact_lists'; - $authorizationHeader['Authorization'] = "Bearer {$queryParams->tokenDetails->access_token}"; + $apiEndpoint = 'https://api.cc.email/v3/contact_lists'; + $authorizationHeader['Authorization'] = "Bearer {$tokenDetails->access_token}"; $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); $allList = []; @@ -103,51 +67,51 @@ public static function refreshList($queryParams) ]; } uksort($allList, 'strnatcasecmp'); - - $response['contactList'] = $allList; } else { - wp_send_json_error( - $apiResponse->response->error->message, - 400 - ); + wp_send_json_error($apiResponse->response->error->message ?? __('List fetch failed', 'bit-integrations'), 400); } - if (!empty($response['tokenDetails']) && $response['tokenDetails'] && !empty($queryParams->integId)) { - static::_saveRefreshedToken($queryParams->integId, $response['tokenDetails'], $response); + + if (!$isConnectionAuth && $oldToken !== ($tokenDetails->access_token ?? '') && !empty($queryParams->integId)) { + self::_saveRefreshedToken($queryParams->integId, $tokenDetails); } - wp_send_json_success($response, 200); + + wp_send_json_success( + [ + 'contactList' => $allList, + 'tokenDetails' => $tokenDetails + ], + 200 + ); } public static function refreshTags($queryParams) { - if (empty($queryParams->tokenDetails) - || empty($queryParams->clientId) - || empty($queryParams->clientSecret) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); + $tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $isConnectionAuth = !empty($queryParams->connection_id); + $oldToken = $tokenDetails->access_token ?? ''; + + if (empty($oldToken)) { + wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $response = []; - if ((\intval($queryParams->tokenDetails->generates_on) + (1435 * 60)) < time()) { - $refreshedToken = ConstantContactController::_refreshAccessToken($queryParams); - if ($refreshedToken) { - $response['tokenDetails'] = $refreshedToken; - } else { - wp_send_json_error( - __('Failed to refresh access token', 'bit-integrations'), - 400 - ); + if (!$isConnectionAuth && self::isTokenExpired($tokenDetails)) { + $refreshedToken = self::_refreshAccessToken((object) [ + 'clientId' => $queryParams->clientId ?? '', + 'clientSecret' => $queryParams->clientSecret ?? '', + 'tokenDetails' => $tokenDetails, + ]); + + if (!$refreshedToken) { + wp_send_json_error(__('Failed to refresh access token', 'bit-integrations'), 400); } + + $tokenDetails = $refreshedToken; } - $apiEndpoint = 'https://api.cc.email/v3/contact_tags'; - $authorizationHeader['Authorization'] = "Bearer {$queryParams->tokenDetails->access_token}"; + $apiEndpoint = 'https://api.cc.email/v3/contact_tags'; + $authorizationHeader['Authorization'] = "Bearer {$tokenDetails->access_token}"; $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); + $allTag = []; if (!is_wp_error($apiResponse) && empty($apiResponse->response->error)) { $contactTags = $apiResponse->tags; @@ -158,51 +122,49 @@ public static function refreshTags($queryParams) ]; } uksort($allTag, 'strnatcasecmp'); - - $response['contactTag'] = $allTag; } else { - wp_send_json_error( - $apiResponse->response->error->message, - 400 - ); + wp_send_json_error($apiResponse->response->error->message ?? __('Tag fetch failed', 'bit-integrations'), 400); } - if (!empty($response['tokenDetails']) && $response['tokenDetails'] && !empty($queryParams->integId)) { - static::_saveRefreshedToken($queryParams->integId, $response['tokenDetails'], $response); + if (!$isConnectionAuth && $oldToken !== ($tokenDetails->access_token ?? '') && !empty($queryParams->integId)) { + self::_saveRefreshedToken($queryParams->integId, $tokenDetails); } - wp_send_json_success($response, 200); + + wp_send_json_success( + [ + 'contactTag' => $allTag, + 'tokenDetails' => $tokenDetails + ], + 200 + ); } public static function getCustomFields($queryParams) { - if (empty($queryParams->tokenDetails) - || empty($queryParams->clientId) - || empty($queryParams->clientSecret) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); + $tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $isConnectionAuth = !empty($queryParams->connection_id); + $oldToken = $tokenDetails->access_token ?? ''; + + if (empty($oldToken)) { + wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $response = []; - if ((\intval($queryParams->tokenDetails->generates_on) + (1435 * 60)) < time()) { - $refreshedToken = ConstantContactController::_refreshAccessToken($queryParams); - if ($refreshedToken) { - $response['tokenDetails'] = $refreshedToken; - } else { - wp_send_json_error( - __('Failed to refresh access token', 'bit-integrations'), - 400 - ); + if (!$isConnectionAuth && self::isTokenExpired($tokenDetails)) { + $refreshedToken = self::_refreshAccessToken((object) [ + 'clientId' => $queryParams->clientId ?? '', + 'clientSecret' => $queryParams->clientSecret ?? '', + 'tokenDetails' => $tokenDetails, + ]); + + if (!$refreshedToken) { + wp_send_json_error(__('Failed to refresh access token', 'bit-integrations'), 400); } + + $tokenDetails = $refreshedToken; } - $apiEndpoint = 'https://api.cc.email/v3/contact_custom_fields'; - $authorizationHeader['Authorization'] = "Bearer {$queryParams->tokenDetails->access_token}"; + $apiEndpoint = 'https://api.cc.email/v3/contact_custom_fields'; + $authorizationHeader['Authorization'] = "Bearer {$tokenDetails->access_token}"; $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); $allCFields = []; if (!is_wp_error($apiResponse) && empty($apiResponse->response->error)) { @@ -215,26 +177,30 @@ public static function getCustomFields($queryParams) ]; } uksort($allCFields, 'strnatcasecmp'); - - $response['customFields'] = $allCFields; } else { - wp_send_json_error( - $apiResponse->response->error->message, - 400 - ); + wp_send_json_error($apiResponse->response->error->message ?? __('Custom fields fetch failed', 'bit-integrations'), 400); } - if (!empty($response['tokenDetails']) && $response['tokenDetails'] && !empty($queryParams->integId)) { - static::_saveRefreshedToken($queryParams->integId, $response['tokenDetails'], $response); + if (!$isConnectionAuth && $oldToken !== ($tokenDetails->access_token ?? '') && !empty($queryParams->integId)) { + self::_saveRefreshedToken($queryParams->integId, $tokenDetails); } - wp_send_json_success($response, 200); + + wp_send_json_success( + [ + 'customFields' => $allCFields, + 'tokenDetails' => $tokenDetails + ], + 200 + ); } public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; - $auth_token = $integrationDetails->tokenDetails->access_token; + $tokenDetails = self::normalizeConnectionToken($integrationDetails->tokenDetails ?? null); + $isConnectionAuth = !empty($integrationDetails->connection_id); + $authToken = $tokenDetails->access_token ?? ''; $listIds = $integrationDetails->list_ids; $tagIds = $integrationDetails->tag_ids; $fieldMap = $integrationDetails->field_map; @@ -245,24 +211,22 @@ public function execute($integrationData, $fieldValues) $phoneType = $integrationDetails->phone_type; $update = $integrationDetails->actions->update ?? false; - if ( - empty($fieldMap) - || empty($auth_token) - ) { + if (empty($fieldMap) || empty($authToken)) { // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Constant Contact')); } - if ((\intval($integrationDetails->tokenDetails->generates_on) + (1435 * 60)) < time()) { - $requiredParams['clientId'] = $integrationDetails->clientId; - $requiredParams['clientSecret'] = $integrationDetails->clientSecret; - $requiredParams['tokenDetails'] = $integrationDetails->tokenDetails; - - $newTokenDetails = ConstantContactController::_refreshAccessToken((object) $requiredParams); + if (!$isConnectionAuth && self::isTokenExpired($tokenDetails)) { + $newTokenDetails = self::_refreshAccessToken((object) [ + 'clientId' => $integrationDetails->clientId ?? '', + 'clientSecret' => $integrationDetails->clientSecret ?? '', + 'tokenDetails' => $tokenDetails + ]); if ($newTokenDetails) { - ConstantContactController::_saveRefreshedToken($integId, $newTokenDetails); - $integrationDetails->tokenDetails->access_token = $newTokenDetails->access_token; + self::_saveRefreshedToken($integId, $newTokenDetails); + $tokenDetails = $newTokenDetails; + $integrationDetails->tokenDetails = $newTokenDetails; } else { LogHandler::save($integId, 'token', 'error', __('Failed to refresh access token', 'bit-integrations')); @@ -294,21 +258,24 @@ public function execute($integrationData, $fieldValues) protected static function _refreshAccessToken($apiData) { - if ( - empty($apiData->tokenDetails) - ) { + if (empty($apiData->tokenDetails) || empty($apiData->tokenDetails->refresh_token)) { + return false; + } + + $clientId = $apiData->clientId ?? ($apiData->tokenDetails->client_id ?? ''); + $clientSecret = $apiData->clientSecret ?? ($apiData->tokenDetails->client_secret ?? ''); + if (empty($clientId) || empty($clientSecret)) { return false; } - $tokenDetails = $apiData->tokenDetails; + $tokenDetails = $apiData->tokenDetails; $apiEndpoint = 'https://authz.constantcontact.com/oauth2/default/v1/token'; $requestParams = [ 'grant_type' => 'refresh_token', 'refresh_token' => $tokenDetails->refresh_token, ]; - $auth = $apiData->clientId . ':' . $apiData->clientSecret; - // Base64 encode it + $auth = $clientId . ':' . $clientSecret; $credentials = base64_encode($auth); $authorizationHeader['Authorization'] = 'Basic ' . $credentials; @@ -318,11 +285,52 @@ protected static function _refreshAccessToken($apiData) return false; } $tokenDetails->generates_on = time(); - $tokenDetails->access_token = $apiResponse->access_token; + $tokenDetails->generated_at = $tokenDetails->generates_on; + $tokenDetails->access_token = $apiResponse->access_token ?? $tokenDetails->access_token; + $tokenDetails->refresh_token = $apiResponse->refresh_token ?? $tokenDetails->refresh_token; + $tokenDetails->token_type = $apiResponse->token_type ?? ($tokenDetails->token_type ?? 'Bearer'); + $tokenDetails->expires_in = $apiResponse->expires_in ?? ($tokenDetails->expires_in ?? 0); return $tokenDetails; } + private static function isTokenExpired($tokenDetails) + { + if (empty($tokenDetails)) { + return true; + } + + $generatedOn = (int) ($tokenDetails->generates_on ?? $tokenDetails->generated_at ?? 0); + $expiresIn = (int) ($tokenDetails->expires_in ?? 0); + + if ($generatedOn <= 0) { + return true; + } + + if ($expiresIn > 0) { + return ($generatedOn + \max($expiresIn - 300, 0)) < time(); + } + + return ($generatedOn + (1435 * 60)) < time(); + } + + private static function normalizeConnectionToken($token) + { + if (!\is_object($token)) { + $token = (object) []; + } + + if (empty($token->generates_on) && !empty($token->generated_at)) { + $token->generates_on = (int) $token->generated_at; + } + + if (empty($token->generated_at) && !empty($token->generates_on)) { + $token->generated_at = (int) $token->generates_on; + } + + return $token; + } + private static function _saveRefreshedToken($integrationID, $tokenDetails) { if (empty($integrationID)) { diff --git a/backend/Actions/ConstantContact/Routes.php b/backend/Actions/ConstantContact/Routes.php index 0b4bd4ddf..f992e793c 100644 --- a/backend/Actions/ConstantContact/Routes.php +++ b/backend/Actions/ConstantContact/Routes.php @@ -7,8 +7,6 @@ use BitApps\Integrations\Actions\ConstantContact\ConstantContactController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('cContact_generate_token', [ConstantContactController::class, 'generateTokens']); Route::post('cContact_refresh_list', [ConstantContactController::class, 'refreshList']); -Route::post('cContact_refresh_fields', [ConstantContactController::class, 'refreshListFields']); Route::post('cContact_refresh_tags', [ConstantContactController::class, 'refreshTags']); Route::post('cContact_custom_fields', [ConstantContactController::class, 'getCustomFields']); diff --git a/backend/Actions/ConvertKit/ConvertKitController.php b/backend/Actions/ConvertKit/ConvertKitController.php index 545a6319c..78b8e93c1 100644 --- a/backend/Actions/ConvertKit/ConvertKitController.php +++ b/backend/Actions/ConvertKit/ConvertKitController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ConvertKit; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,14 @@ */ class ConvertKitController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'convertkit', + 'fields' => [ + 'api_secret' => 'value', + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -26,38 +35,6 @@ public static function _apiEndpoint($method, $apiSecret) return "https://api.convertkit.com/v3/{$method}?api_secret={$apiSecret}"; } - /** - * Process ajax request - * - * @param $requestsParams Params to authorize - * - * @return JSON Convert Kit api response and status - */ - public static function convertKitAuthorize($requestsParams) - { - if (empty($requestsParams->api_secret)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = self::_apiEndpoint('account', $requestsParams->api_secret); - $apiResponse = HttpHelper::get($apiEndpoint, null); - - if (is_wp_error($apiResponse) || empty($apiResponse) || !empty($apiResponse->error) || empty($apiResponse->primary_email_address)) { - wp_send_json_error( - !empty($apiResponse->error) ? $apiResponse->message : 'Unknown', - 400 - ); - } - - wp_send_json_success(true); - } - /** * Process ajax request for refresh Forms * diff --git a/backend/Actions/ConvertKit/Routes.php b/backend/Actions/ConvertKit/Routes.php index b49f71dbf..fdaf8cd39 100644 --- a/backend/Actions/ConvertKit/Routes.php +++ b/backend/Actions/ConvertKit/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\ConvertKit\ConvertKitController; use BitApps\Integrations\Core\Util\Route; -Route::post('convertKit_authorize', [ConvertKitController::class, 'convertKitAuthorize']); Route::post('convertKit_headers', [ConvertKitController::class, 'convertKitHeaders']); Route::post('convertKit_forms', [ConvertKitController::class, 'convertKitForms']); Route::post('convertKit_tags', [ConvertKitController::class, 'convertKitTags']); diff --git a/backend/Actions/CopperCRM/CopperCRMController.php b/backend/Actions/CopperCRM/CopperCRMController.php index 0a0747fb7..29b8c7b7c 100644 --- a/backend/Actions/CopperCRM/CopperCRMController.php +++ b/backend/Actions/CopperCRM/CopperCRMController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\CopperCRM; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,15 @@ */ class CopperCRMController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'coppercrm', + 'fields' => [ + 'api_key' => 'value', + 'api_email' => 'api_email', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; @@ -23,31 +33,6 @@ public function __construct() $this->apiEndpoint = 'https://api.copper.com/developer_api/v1'; } - public function authentication($fieldsRequestParams) - { - if (empty($fieldsRequestParams->api_key)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiKey = $fieldsRequestParams->api_key; - $apiEmail = $fieldsRequestParams->api_email; - $apiEndpoint = $this->apiEndpoint . '/account'; - $headers = [ - 'X-PW-AccessToken' => $apiKey, - 'X-PW-Application' => 'developer_api', - 'X-PW-UserEmail' => $apiEmail, - 'Content-Type' => 'application/json' - ]; - - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (!isset($response->error)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API key', 'bit-integrations'), 400); - } - } - public function getCustomFields($fieldsRequestParams) { if (empty($fieldsRequestParams->api_key)) { diff --git a/backend/Actions/CopperCRM/Routes.php b/backend/Actions/CopperCRM/Routes.php index cea1b67d7..eaff590b0 100644 --- a/backend/Actions/CopperCRM/Routes.php +++ b/backend/Actions/CopperCRM/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\CopperCRM\CopperCRMController; use BitApps\Integrations\Core\Util\Route; -Route::post('coppercrm_authentication', [CopperCRMController::class, 'authentication']); Route::post('coppercrm_fetch_custom_fields', [CopperCRMController::class, 'getCustomFields']); Route::post('coppercrm_fetch_all_opportunities', [CopperCRMController::class, 'getAllOpportunities']); Route::post('coppercrm_fetch_all_owners', [CopperCRMController::class, 'getAllOwners']); diff --git a/backend/Actions/CreatorLms/CreatorLmsController.php b/backend/Actions/CreatorLms/CreatorLmsController.php index 06d7b0864..eb68a87d6 100644 --- a/backend/Actions/CreatorLms/CreatorLmsController.php +++ b/backend/Actions/CreatorLms/CreatorLmsController.php @@ -26,12 +26,6 @@ public static function isExists() } } - public static function creatorLmsAuthorize() - { - self::isExists(); - wp_send_json_success(true); - } - public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; diff --git a/backend/Actions/CreatorLms/Routes.php b/backend/Actions/CreatorLms/Routes.php deleted file mode 100644 index 044f1b6da..000000000 --- a/backend/Actions/CreatorLms/Routes.php +++ /dev/null @@ -1,10 +0,0 @@ - AuthorizationType::API_KEY, + 'slug' => 'demio', + 'fields' => [ + 'api_key' => 'value', + 'api_secret' => 'api_secret', + ], + ]; + protected $_defaultHeader; protected $_apiEndpoint; @@ -23,20 +33,6 @@ public function __construct() $this->_apiEndpoint = 'https://my.demio.com/api/v1'; } - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->setHeaders($fieldsRequestParams->api_key, $fieldsRequestParams->api_secret); - $apiEndpoint = $this->_apiEndpoint . '/ping'; - $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); - - if ($response->pong) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API Key & API Secret', 'bit-integrations'), 400); - } - } - public function getAllEvents($fieldsRequestParams) { $this->checkValidation($fieldsRequestParams); diff --git a/backend/Actions/Demio/Routes.php b/backend/Actions/Demio/Routes.php index 8717e7db9..e6741f3b8 100644 --- a/backend/Actions/Demio/Routes.php +++ b/backend/Actions/Demio/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Demio\DemioController; use BitApps\Integrations\Core\Util\Route; -Route::post('demio_authentication', [DemioController::class, 'authentication']); Route::post('demio_fetch_all_events', [DemioController::class, 'getAllEvents']); Route::post('demio_fetch_all_sessions', [DemioController::class, 'getAllSessions']); diff --git a/backend/Actions/DirectIq/DirectIqController.php b/backend/Actions/DirectIq/DirectIqController.php index 484425023..23ff93969 100644 --- a/backend/Actions/DirectIq/DirectIqController.php +++ b/backend/Actions/DirectIq/DirectIqController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\DirectIq; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,15 @@ */ class DirectIqController { + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'directiq', + 'fields' => [ + 'client_id' => 'username', + 'client_secret' => 'password', + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -26,41 +36,6 @@ public static function _apiEndpoint($method) return "https://clientapi.benchmarkemail.com/{$method}"; } - /** - * Process ajax request - * - * @param $requestsParams Params to authorize - * - * @return JSON DirectIQ api response and status - */ - public static function directIqAuthorize($requestsParams) - { - if ( - empty($requestsParams->client_id) || empty($requestsParams->client_secret) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $endpoint = 'https://rest.directiq.com/subscription/authorize'; - $header = ['Authorization' => 'Basic ' . base64_encode("{$requestsParams->client_id}:{$requestsParams->client_secret}")]; - HttpHelper::get($endpoint, null, $header); - - if (HttpHelper::$responseCode !== 200) { - wp_send_json_error( - empty($apiResponse) ? 'Unknown' : $apiResponse, - 400 - ); - } - - wp_send_json_success(true); - } - /** * Process ajax request for refresh Lists * diff --git a/backend/Actions/DirectIq/Routes.php b/backend/Actions/DirectIq/Routes.php index 030b4ea04..213c1a5bc 100644 --- a/backend/Actions/DirectIq/Routes.php +++ b/backend/Actions/DirectIq/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\DirectIq\DirectIqController; use BitApps\Integrations\Core\Util\Route; -Route::post('directIq_authorize', [DirectIqController::class, 'directIqAuthorize']); Route::post('directIq_headers', [DirectIqController::class, 'directIqHeaders']); Route::post('directIq_lists', [DirectIqController::class, 'directIqLists']); diff --git a/backend/Actions/Discord/DiscordController.php b/backend/Actions/Discord/DiscordController.php index 0062cbdef..9256d7474 100644 --- a/backend/Actions/Discord/DiscordController.php +++ b/backend/Actions/Discord/DiscordController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Discord; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -16,42 +17,13 @@ class DiscordController { public const APIENDPOINT = 'https://discord.com/api/v10'; - /** - * Process ajax request for generate_token - * - * @param object $requestsParams Params to authorize - * @param mixed $tokenRequestParams - * - * @return JSON discord api response and status - */ - public static function handleAuthorize($tokenRequestParams) - { - if ( - empty($tokenRequestParams->accessToken) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $header = [ - 'Authorization' => 'Bot ' . $tokenRequestParams->accessToken, - ]; - $apiEndpoint = self::APIENDPOINT . '/users/@me'; - - $apiResponse = HttpHelper::get($apiEndpoint, null, $header); - - if (!isset($apiResponse->id)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - wp_send_json_success($apiResponse, 200); - } + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'discord', + 'fields' => [ + 'accessToken' => 'value', + ], + ]; public static function fetchServers($tokenRequestParams) { diff --git a/backend/Actions/Discord/Routes.php b/backend/Actions/Discord/Routes.php index 90262fe94..c5be6e01e 100644 --- a/backend/Actions/Discord/Routes.php +++ b/backend/Actions/Discord/Routes.php @@ -8,6 +8,5 @@ use BitApps\Integrations\Core\Util\Route; // Discord -Route::post('handle_authorize', [DiscordController::class, 'handleAuthorize']); Route::post('discord_fetch_servers', [DiscordController::class, 'fetchServers']); Route::post('discord_fetch_channels', [DiscordController::class, 'fetchChannels']); diff --git a/backend/Actions/Dokan/DokanController.php b/backend/Actions/Dokan/DokanController.php index b02fbfed4..751e440c8 100644 --- a/backend/Actions/Dokan/DokanController.php +++ b/backend/Actions/Dokan/DokanController.php @@ -14,21 +14,6 @@ */ class DokanController { - public function authentication() - { - if (self::checkedDokanExists()) { - wp_send_json_success(true); - } else { - wp_send_json_error( - __( - 'Please! Install Dokan', - 'bit-integrations' - ), - 400 - ); - } - } - public static function checkedDokanExists() { if (!is_plugin_active('dokan-lite/dokan.php')) { diff --git a/backend/Actions/Dokan/Routes.php b/backend/Actions/Dokan/Routes.php index c9d963de7..ff2383f2f 100644 --- a/backend/Actions/Dokan/Routes.php +++ b/backend/Actions/Dokan/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Dokan\DokanController; use BitApps\Integrations\Core\Util\Route; -Route::post('dokan_authentication', [DokanController::class, 'authentication']); Route::post('dokan_fetch_eu_fields', [DokanController::class, 'getEUFields']); Route::post('dokan_fetch_vendors', [DokanController::class, 'getAllVendors']); diff --git a/backend/Actions/Drip/DripController.php b/backend/Actions/Drip/DripController.php index 5497c2df0..d5a336f34 100644 --- a/backend/Actions/Drip/DripController.php +++ b/backend/Actions/Drip/DripController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Drip; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,14 @@ */ class DripController { + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'drip', + 'fields' => [ + 'api_token' => 'username', + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -21,7 +30,7 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - public static function dripAuthorize($requestsParams) + public static function getAllAccounts($requestsParams) { if (empty($requestsParams->api_token) ) { @@ -56,16 +65,16 @@ public static function dripAuthorize($requestsParams) ]; } - wp_send_json_success($accounts); + wp_send_json_success($accounts, 200); } public static function getCustomFields($fieldsRequestParams) { - if (empty($fieldsRequestParams->apiToken) || empty($fieldsRequestParams->selectedAccountId)) { + if (empty($fieldsRequestParams->api_token) || empty($fieldsRequestParams->selectedAccountId)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $apiToken = $fieldsRequestParams->apiToken; + $apiToken = $fieldsRequestParams->api_token; $accountId = $fieldsRequestParams->selectedAccountId; $apiEndpoints = 'https://api.getdrip.com/v2/' . $accountId . '/custom_field_identifiers'; $header = [ @@ -96,11 +105,11 @@ public static function getCustomFields($fieldsRequestParams) public static function getAllTags($fieldsRequestParams) { - if (empty($fieldsRequestParams->apiToken) || empty($fieldsRequestParams->selectedAccountId)) { + if (empty($fieldsRequestParams->api_token) || empty($fieldsRequestParams->selectedAccountId)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $apiToken = $fieldsRequestParams->apiToken; + $apiToken = $fieldsRequestParams->api_token; $accountId = $fieldsRequestParams->selectedAccountId; $apiEndpoints = 'https://api.getdrip.com/v2/' . $accountId . '/tags'; $header = [ diff --git a/backend/Actions/Drip/Routes.php b/backend/Actions/Drip/Routes.php index bf0873c29..956516399 100644 --- a/backend/Actions/Drip/Routes.php +++ b/backend/Actions/Drip/Routes.php @@ -7,6 +7,6 @@ use BitApps\Integrations\Actions\Drip\DripController; use BitApps\Integrations\Core\Util\Route; -Route::post('drip_authorize', [DripController::class, 'dripAuthorize']); +Route::post('drip_fetch_all_accounts', [DripController::class, 'getAllAccounts']); Route::post('drip_fetch_all_custom_fields', [DripController::class, 'getCustomFields']); Route::post('drip_fetch_all_tags', [DripController::class, 'getAllTags']); diff --git a/backend/Actions/Dropbox/DropboxController.php b/backend/Actions/Dropbox/DropboxController.php index 197b33f56..c953d7d27 100644 --- a/backend/Actions/Dropbox/DropboxController.php +++ b/backend/Actions/Dropbox/DropboxController.php @@ -3,6 +3,7 @@ namespace BitApps\Integrations\Actions\Dropbox; use BitApps\Integrations\Actions\Dropbox\RecordApiHelper as DropboxRecordApiHelper; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use BitApps\Integrations\Log\LogHandler; @@ -10,6 +11,16 @@ class DropboxController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'dropbox', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; + protected static $apiBaseUri = 'https://api.dropboxapi.com'; private $integrationID; @@ -19,41 +30,31 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function checkAuthorization($tokenRequestParams) + public static function getAllFolders($queryParams) { - if (empty($tokenRequestParams->accessCode) || empty($tokenRequestParams->clientId) || empty($tokenRequestParams->clientSecret)) { + $tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $isConnectionAuth = !empty($queryParams->connection_id); + $clientId = $queryParams->clientId ?? ''; + $clientSecret = $queryParams->clientSecret ?? ''; + + if (empty($tokenDetails->access_token)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $body = [ - 'code' => $tokenRequestParams->accessCode, - 'grant_type' => 'authorization_code', - 'client_id' => $tokenRequestParams->clientId, - 'client_secret' => $tokenRequestParams->clientSecret, - ]; - - $apiEndpoint = self::$apiBaseUri . '/oauth2/token'; - $apiResponse = HttpHelper::post($apiEndpoint, $body); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error(empty($apiResponse->error_description) ? 'Unknown' : $apiResponse->error_description, 400); + $oldToken = $tokenDetails->access_token; + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $clientId, $clientSecret); } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public static function getAllFolders($queryParams) - { - if (empty($queryParams->tokenDetails) || empty($queryParams->clientId) || empty($queryParams->clientSecret)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + wp_send_json_error(__('Authorization failed', 'bit-integrations'), 400); } - $token = self::tokenExpiryCheck($queryParams->tokenDetails, $queryParams->clientId, $queryParams->clientSecret); - if ($token->access_token !== $queryParams->tokenDetails->access_token) { - self::saveRefreshedToken($queryParams->flowID, $token); + if (!$isConnectionAuth && !empty($queryParams->flowID) && $tokenDetails->access_token !== $oldToken) { + self::saveRefreshedToken($queryParams->flowID, $tokenDetails); } - $folders = self::getDropboxFoldersList($token->access_token); + $folders = self::getDropboxFoldersList($tokenDetails->access_token); $data = []; if ($folders->entries) { foreach ($folders->entries as $folder) { @@ -68,7 +69,7 @@ public static function getAllFolders($queryParams) } $response['dropboxFoldersList'] = $data; - $response['tokenDetails'] = $token; + $response['tokenDetails'] = $tokenDetails; wp_send_json_success($response, 200); } @@ -108,8 +109,22 @@ public function execute($integrationData, $fieldValues) $integrationDetails = $integrationData->flow_details; $actions = $integrationDetails->actions; $fieldMap = $integrationDetails->field_map; - $tokenDetails = self::tokenExpiryCheck($integrationDetails->tokenDetails, $integrationDetails->clientId, $integrationDetails->clientSecret); - if ($tokenDetails->access_token !== $integrationDetails->tokenDetails->access_token) { + $isConnectionAuth = !empty($integrationDetails->connection_id); + $tokenDetails = self::normalizeConnectionToken($integrationDetails->tokenDetails ?? null); + $oldToken = $tokenDetails->access_token ?? ''; + + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $integrationDetails->clientId ?? '', $integrationDetails->clientSecret ?? ''); + } + + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + // translators: %s: Service name + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'dropbox', 'type_name' => 'file_upload']), 'error', wp_sprintf(__('Not Authorization By %s', 'bit-integrations'), 'Dropbox')); + + return false; + } + + if (!$isConnectionAuth && $tokenDetails->access_token !== $oldToken) { self::saveRefreshedToken($this->integrationID, $tokenDetails); } @@ -129,7 +144,9 @@ private static function tokenExpiryCheck($token, $clientId, $clientSecret) return false; } - if (($token->generates_on + $token->expires_in - 30) < time()) { + $generatedOn = !empty($token->generates_on) ? (int) $token->generates_on : (int) ($token->generated_at ?? 0); + + if (($generatedOn + $token->expires_in - 30) < time()) { $refreshToken = self::refreshToken($token->refresh_token, $clientId, $clientSecret); if (is_wp_error($refreshToken) || !empty($refreshToken->error)) { return false; @@ -138,6 +155,8 @@ private static function tokenExpiryCheck($token, $clientId, $clientSecret) $token->access_token = $refreshToken->access_token; $token->expires_in = $refreshToken->expires_in; $token->generates_on = $refreshToken->generates_on; + $token->generated_at = $refreshToken->generated_at; + $token->refresh_token = $refreshToken->refresh_token; } return $token; @@ -159,6 +178,20 @@ private static function refreshToken($refresh_token, $clientId, $clientSecret) } $token = $apiResponse; $token->generates_on = time(); + $token->generated_at = $token->generates_on; + + return $token; + } + + private static function normalizeConnectionToken($token) + { + if (!\is_object($token)) { + $token = (object) []; + } + + if (empty($token->generates_on) && !empty($token->generated_at)) { + $token->generates_on = (int) $token->generated_at; + } return $token; } diff --git a/backend/Actions/Dropbox/Routes.php b/backend/Actions/Dropbox/Routes.php index ce087f493..cacfd10ec 100644 --- a/backend/Actions/Dropbox/Routes.php +++ b/backend/Actions/Dropbox/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\Dropbox\DropboxController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('dropbox_authorization', [DropboxController::class, 'checkAuthorization']); Route::post('dropbox_get_all_folders', [DropboxController::class, 'getAllFolders']); diff --git a/backend/Actions/ElasticEmail/ElasticEmailController.php b/backend/Actions/ElasticEmail/ElasticEmailController.php index 3c4f04400..b0298bdf5 100644 --- a/backend/Actions/ElasticEmail/ElasticEmailController.php +++ b/backend/Actions/ElasticEmail/ElasticEmailController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ElasticEmail; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,37 +15,18 @@ */ class ElasticEmailController { - public static function elasticEmailAuthorize($requestsParams) - { - if (empty($requestsParams->api_key)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = 'https://api.elasticemail.com/v4/lists'; - $apiKey = $requestsParams->api_key; - $header = [ - 'X-ElasticEmail-ApiKey' => $apiKey, - 'Accept' => '*/*', - ]; - $apiResponse = HttpHelper::get($apiEndpoint, null, $header); - if (is_wp_error($apiResponse) || !\is_null($apiResponse->Error)) { - wp_send_json_error( - empty($apiResponse->code) ? 'Unknown' : $apiResponse->Error, - 400 - ); - } - wp_send_json_success(true); - } + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'elasticemail', + 'fields' => [ + 'api_key' => 'value', + ], + ]; public static function getAllLists($requestsParams) { - if (empty($requestsParams->apiKey)) { + $apiKey = $requestsParams->apiKey ?? $requestsParams->api_key ?? null; + if (empty($apiKey)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -55,7 +37,6 @@ public static function getAllLists($requestsParams) } $apiEndpoint = 'https://api.elasticemail.com/v4/lists'; - $apiKey = $requestsParams->apiKey; $header = [ 'X-ElasticEmail-ApiKey' => $apiKey, 'Accept' => '*/*', diff --git a/backend/Actions/ElasticEmail/Routes.php b/backend/Actions/ElasticEmail/Routes.php index bda0b5ffa..95cd71ec1 100644 --- a/backend/Actions/ElasticEmail/Routes.php +++ b/backend/Actions/ElasticEmail/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\ElasticEmail\ElasticEmailController; use BitApps\Integrations\Core\Util\Route; -Route::post('elasticemail_authorize', [ElasticEmailController::class, 'elasticEmailAuthorize']); Route::get('get_all_lists', [ElasticEmailController::class, 'getAllLists']); diff --git a/backend/Actions/EmailOctopus/EmailOctopusController.php b/backend/Actions/EmailOctopus/EmailOctopusController.php index 71e946069..3005f5804 100644 --- a/backend/Actions/EmailOctopus/EmailOctopusController.php +++ b/backend/Actions/EmailOctopus/EmailOctopusController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\EmailOctopus; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,9 +15,17 @@ */ class EmailOctopusController { + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'emailoctopus', + 'fields' => [ + 'auth_token' => 'token', + ], + ]; + protected $_defaultHeader; - public function authentication($fieldsRequestParams) + public function fetchAllLists($fieldsRequestParams) { if (empty($fieldsRequestParams->auth_token)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); @@ -27,6 +36,7 @@ public function authentication($fieldsRequestParams) $response = HttpHelper::get($apiEndpoint, null, null); if (!isset($response->error)) { + $lists = []; foreach ($response->data as $list) { $lists[] = [ 'id' => $list->id, @@ -51,6 +61,7 @@ public function getAllFields($fieldsRequestParams) $response = HttpHelper::get($apiEndpoint, null, null); if (!isset($response->error)) { + $fields = []; foreach ($response->fields as $field) { $fields[] = [ 'key' => $field->tag, @@ -75,6 +86,7 @@ public function getAllTags($fieldsRequestParams) $apiEndpoint = 'https://emailoctopus.com/api/1.6/lists/' . $listId . '/tags?api_key=' . $apiKey; $response = HttpHelper::get($apiEndpoint, null, null); + $tags = []; foreach ($response->data as $tag) { $tags[] = [ 'name' => $tag->tag diff --git a/backend/Actions/EmailOctopus/Routes.php b/backend/Actions/EmailOctopus/Routes.php index 47f9f169e..3a8a3d305 100644 --- a/backend/Actions/EmailOctopus/Routes.php +++ b/backend/Actions/EmailOctopus/Routes.php @@ -7,6 +7,6 @@ use BitApps\Integrations\Actions\EmailOctopus\EmailOctopusController; use BitApps\Integrations\Core\Util\Route; -Route::post('emailOctopus_authentication', [EmailOctopusController::class, 'authentication']); +Route::post('emailOctopus_fetch_all_lists', [EmailOctopusController::class, 'fetchAllLists']); Route::post('emailOctopus_fetch_all_tags', [EmailOctopusController::class, 'getAllTags']); Route::post('emailOctopus_fetch_all_fields', [EmailOctopusController::class, 'getAllFields']); diff --git a/backend/Actions/Encharge/EnchargeController.php b/backend/Actions/Encharge/EnchargeController.php index eab9191e8..266626eaf 100644 --- a/backend/Actions/Encharge/EnchargeController.php +++ b/backend/Actions/Encharge/EnchargeController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Encharge; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,14 @@ */ class EnchargeController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'encharge', + 'fields' => [ + 'api_key' => 'value', + ], + ]; + public const APIENDPOINT = 'https://api.encharge.io/v1/'; private $_integrationID; @@ -23,40 +32,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - /** - * Process ajax request for generate_token - * - * @param $requestsParams Params for Auth - * - * @return JSON enchagre user Authorization - */ - public static function enChargeAuthorize($requestsParams) - { - if (empty($requestsParams->api_key)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = self::APIENDPOINT . 'accounts/info'; - $authorizationHeader['Accept'] = 'application/json'; - $authorizationHeader['X-Encharge-Token'] = $requestsParams->api_key; - $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); - - if (is_wp_error($apiResponse) || isset($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->code) ? 'Unknown' : $apiResponse->error->message, - 400 - ); - } - - wp_send_json_success(true); - } - /** * Process ajax request for refresh crm modules * diff --git a/backend/Actions/Encharge/Routes.php b/backend/Actions/Encharge/Routes.php index de991c80c..71de8a901 100644 --- a/backend/Actions/Encharge/Routes.php +++ b/backend/Actions/Encharge/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\Encharge\EnchargeController; use BitApps\Integrations\Core\Util\Route; -Route::post('encharge_authorize', [EnchargeController::class, 'enChargeAuthorize']); Route::post('encharge_headers', [EnchargeController::class, 'enchargeHeaders']); diff --git a/backend/Actions/Fabman/FabmanController.php b/backend/Actions/Fabman/FabmanController.php index a55c9130e..bc593ed2b 100644 --- a/backend/Actions/Fabman/FabmanController.php +++ b/backend/Actions/Fabman/FabmanController.php @@ -6,11 +6,20 @@ namespace BitApps\Integrations\Actions\Fabman; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; class FabmanController { - public static function authorization($requestParams) + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'fabman', + 'fields' => [ + 'apiKey' => 'token', + ], + ]; + + public static function fetchAccountId($requestParams) { if (empty($requestParams->apiKey)) { wp_send_json_error(__('API Key is required', 'bit-integrations'), 400); @@ -68,7 +77,8 @@ public static function fetchWorkspaces($requestParams) public static function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; - $apiKey = $integrationDetails->apiKey; + $apiKey = $integrationDetails->apiKey ?? ''; + $selectedWorkspace = $integrationDetails->selectedWorkspace ?? null; $accountId = $integrationDetails->accountId ?? null; $actionName = $integrationDetails->actionName; diff --git a/backend/Actions/Fabman/RecordApiHelper.php b/backend/Actions/Fabman/RecordApiHelper.php index 6ea93e7a1..01adc5199 100644 --- a/backend/Actions/Fabman/RecordApiHelper.php +++ b/backend/Actions/Fabman/RecordApiHelper.php @@ -4,8 +4,8 @@ use BitApps\Integrations\Config; use BitApps\Integrations\Core\Util\Common; -use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Core\Util\Hooks; +use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Log\LogHandler; use DateTime; use WP_Error; @@ -170,9 +170,7 @@ private function createMember($data) return $apiResponse; } - $errorMessage = isset($apiResponse->error) ? $apiResponse->error : \__('Failed to create member', 'bit-integrations'); - - return new WP_Error('API_ERROR', $errorMessage); + return new WP_Error('API_ERROR', __('Failed to create member', 'bit-integrations'), $apiResponse); } private function updateMember($data) diff --git a/backend/Actions/Fabman/Routes.php b/backend/Actions/Fabman/Routes.php index 64d4cbbb9..44b1f4cc0 100644 --- a/backend/Actions/Fabman/Routes.php +++ b/backend/Actions/Fabman/Routes.php @@ -7,5 +7,5 @@ use BitApps\Integrations\Actions\Fabman\FabmanController; use BitApps\Integrations\Core\Util\Route; -Route::post('fabman_authorization', [FabmanController::class, 'authorization']); +Route::post('fabman_fetch_account_id', [FabmanController::class, 'fetchAccountId']); Route::post('fabman_fetch_workspaces', [FabmanController::class, 'fetchWorkspaces']); diff --git a/backend/Actions/Flowlu/FlowluController.php b/backend/Actions/Flowlu/FlowluController.php index a545eaa75..12e5a33e7 100644 --- a/backend/Actions/Flowlu/FlowluController.php +++ b/backend/Actions/Flowlu/FlowluController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Flowlu; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,15 @@ */ class FlowluController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'flowlu', + 'fields' => [ + 'api_key' => 'value', + 'company_name' => 'company_name', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; @@ -25,21 +35,6 @@ public function __construct() $this->_defaultHeader = ['Content-type' => 'application/json']; } - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->comapnyName = $fieldsRequestParams->company_name; - $apiKey = $fieldsRequestParams->api_key; - $apiEndpoint = $this->setApiEndpoint() . "/module/crm/account?api_key={$apiKey}"; - $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); - - if (!isset($response->error)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid Session Token or Link Name', 'bit-integrations'), 400); - } - } - public function getAllFields($fieldsRequestParams) { $this->checkValidation($fieldsRequestParams, $fieldsRequestParams->action_name); diff --git a/backend/Actions/Flowlu/Routes.php b/backend/Actions/Flowlu/Routes.php index 67a493e64..9ae250fb1 100644 --- a/backend/Actions/Flowlu/Routes.php +++ b/backend/Actions/Flowlu/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Flowlu\FlowluController; use BitApps\Integrations\Core\Util\Route; -Route::post('flowlu_authentication', [FlowluController::class, 'authentication']); Route::post('Flowlu_all_fields', [FlowluController::class, 'getAllFields']); Route::post('flowlu_fetch_all_account_categories', [FlowluController::class, 'getAllAccountCategories']); Route::post('flowlu_fetch_all_industries', [FlowluController::class, 'getAllIndustries']); diff --git a/backend/Actions/FluentCart/FluentCartController.php b/backend/Actions/FluentCart/FluentCartController.php index 4988b2c66..6d03b4862 100644 --- a/backend/Actions/FluentCart/FluentCartController.php +++ b/backend/Actions/FluentCart/FluentCartController.php @@ -26,12 +26,6 @@ public static function isExists() } } - public static function fluentCartAuthorize() - { - self::isExists(); - wp_send_json_success(true); - } - public function refreshProducts() { self::isExists(); diff --git a/backend/Actions/FluentCart/Routes.php b/backend/Actions/FluentCart/Routes.php index 3dbcf1a05..74089b256 100644 --- a/backend/Actions/FluentCart/Routes.php +++ b/backend/Actions/FluentCart/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\FluentCart\FluentCartController; use BitApps\Integrations\Core\Util\Route; -Route::post('fluent_cart_authorize', [FluentCartController::class, 'fluentCartAuthorize']); Route::post('refresh_fluent_cart_products', [FluentCartController::class, 'refreshProducts']); Route::post('refresh_fluent_cart_products_categories', [FluentCartController::class, 'refreshProductCategories']); Route::post('refresh_fluent_cart_customers', [FluentCartController::class, 'refreshCustomers']); diff --git a/backend/Actions/FluentCrm/FluentCrmController.php b/backend/Actions/FluentCrm/FluentCrmController.php index f7c450a9c..984c74000 100644 --- a/backend/Actions/FluentCrm/FluentCrmController.php +++ b/backend/Actions/FluentCrm/FluentCrmController.php @@ -147,24 +147,6 @@ public static function fluentCrmFields() wp_send_json_success($response, 200); } - /** - * @return true Fluent crm are exists - */ - public static function fluentCrmAuthorize() - { - if (self::checkedExistsFluentCRM()) { - wp_send_json_success(true); - } else { - wp_send_json_error( - __( - 'Please! Install Fluent CRM', - 'bit-integrations' - ), - 400 - ); - } - } - public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; diff --git a/backend/Actions/FluentCrm/Routes.php b/backend/Actions/FluentCrm/Routes.php index a125716dc..391959259 100644 --- a/backend/Actions/FluentCrm/Routes.php +++ b/backend/Actions/FluentCrm/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\FluentCrm\FluentCrmController; use BitApps\Integrations\Core\Util\Route; -Route::post('fluent_crm_authorize', [FluentCrmController::class, 'fluentCrmAuthorize']); Route::post('refresh_fluent_crm_lists', [FluentCrmController::class, 'fluentCrmLists']); Route::post('refresh_fluent_crm_tags', [FluentCrmController::class, 'fluentCrmTags']); Route::post('fluent_crm_headers', [FluentCrmController::class, 'fluentCrmFields']); diff --git a/backend/Actions/FluentSupport/FluentSupportController.php b/backend/Actions/FluentSupport/FluentSupportController.php index d47451403..a2cd3c963 100644 --- a/backend/Actions/FluentSupport/FluentSupportController.php +++ b/backend/Actions/FluentSupport/FluentSupportController.php @@ -16,21 +16,6 @@ */ class FluentSupportController { - public function checkAuthorization() - { - if (!is_plugin_active('fluent-support/fluent-support.php')) { - wp_send_json_error( - __( - 'Fluent Support Plugin is not active or not installed', - 'bit-integrations' - ), - 400 - ); - } else { - return true; - } - } - public function getCustomFields() { if (!class_exists(\FluentSupportPro\App\Services\CustomFieldsService::class)) { diff --git a/backend/Actions/FluentSupport/Routes.php b/backend/Actions/FluentSupport/Routes.php index 4b459e543..6a912f39d 100644 --- a/backend/Actions/FluentSupport/Routes.php +++ b/backend/Actions/FluentSupport/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\FluentSupport\FluentSupportController; use BitApps\Integrations\Core\Util\Route; -Route::post('fluentSupport_authorization', [FluentSupportController::class, 'checkAuthorization']); Route::post('fluent_support_get_custom_fields', [FluentSupportController::class, 'getCustomFields']); Route::post('fluent_support_get_all_support_staff', [FluentSupportController::class, 'getAllSupportStaff']); Route::post('fluent_support_get_all_business_inboxes', [FluentSupportController::class, 'getAllBusinessInboxes']); diff --git a/backend/Actions/FreshSales/FreshSalesController.php b/backend/Actions/FreshSales/FreshSalesController.php index bcc9ddc80..a71e51957 100644 --- a/backend/Actions/FreshSales/FreshSalesController.php +++ b/backend/Actions/FreshSales/FreshSalesController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\FreshSales; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,37 +15,14 @@ */ class FreshSalesController { - public function authorization($requestParams) - { - if (empty($requestParams->api_key) || empty($requestParams->bundle_alias)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoints = 'https://' . $requestParams->bundle_alias . '/api/settings/sales_accounts/fields'; - $headers = ['Authorization' => 'Token token=' . $requestParams->api_key]; - $response = HttpHelper::get($apiEndpoints, null, $headers); - - if (isset($response->fields)) { - wp_send_json_success(__( - 'Authorization Success', - 'bit-integrations' - ), 200); - } else { - wp_send_json_error( - __( - 'The token is invalid', - 'bit-integrations' - ), - 400 - ); - } - } + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'freshsales', + 'fields' => [ + 'api_key' => 'value', + 'bundle_alias' => 'bundle_alias', + ], + ]; public function getMetaData($requestParams) { @@ -172,7 +150,7 @@ public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; - $api_key = $integrationDetails->api_key; + $api_key = $integrationDetails->api_key ?: ($integrationDetails->value ?? ''); $bundle_alias = $integrationDetails->bundle_alias; $fieldMap = $integrationDetails->field_map; $module = strtolower($integrationDetails->moduleData->module); diff --git a/backend/Actions/FreshSales/Routes.php b/backend/Actions/FreshSales/Routes.php index c4032c3c0..4bf2397ba 100644 --- a/backend/Actions/FreshSales/Routes.php +++ b/backend/Actions/FreshSales/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\FreshSales\FreshSalesController; use BitApps\Integrations\Core\Util\Route; -Route::post('FreshSales_authorization', [FreshSalesController::class, 'authorization']); Route::post('FreshSales_refresh_fields', [FreshSalesController::class, 'getFields']); Route::post('FreshSales_fetch_meta_data', [FreshSalesController::class, 'getMetaData']); diff --git a/backend/Actions/Freshdesk/FreshdeskController.php b/backend/Actions/Freshdesk/FreshdeskController.php index a720dc03c..ee6a5b8db 100644 --- a/backend/Actions/Freshdesk/FreshdeskController.php +++ b/backend/Actions/Freshdesk/FreshdeskController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Freshdesk; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Config; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -15,45 +16,14 @@ */ class FreshdeskController { - /** - * Process ajax request for generate_token - * - * @param object $requestsParams Params to authorize - * @param mixed $tokenRequestParams - * - * @return JSON Freshdesk api response and status - */ - public function checkAuthorizationAndFetchTickets($tokenRequestParams) - { - if ( - empty($tokenRequestParams->app_domain) - || empty($tokenRequestParams->api_key) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $header = [ - 'Authorization' => base64_encode("{$tokenRequestParams->api_key}"), - 'Content-Type' => 'application/json' - ]; - - $apiEndpoint = $tokenRequestParams->app_domain . '/api/v2/tickets'; - $apiResponse = HttpHelper::get($apiEndpoint, null, $header); - - if (is_wp_error($apiResponse) || HttpHelper::$responseCode !== 200) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - - wp_send_json_success($apiResponse, 200); - } + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'freshdesk', + 'fields' => [ + 'api_key' => 'value', + 'app_domain' => 'app_domain', + ], + ]; /** * Process ajax request for Fetch Ticket fields @@ -186,15 +156,17 @@ public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integrationId = $integrationData->id; - $api_key = $integrationDetails->api_key; + $api_key = $integrationDetails->api_key ?: ($integrationDetails->value ?? ''); $status = (int) $integrationDetails->status; $priority = (int) $integrationDetails->priority; $fieldMap = $integrationDetails->field_map; $fieldMapContact = $integrationDetails->field_map_contact; + $app_base_domamin = $integrationDetails->app_domain; if ( empty($api_key) || empty($integrationDetails) + || empty($app_base_domamin) || empty($status) || empty($priority) || empty($fieldMap) @@ -203,7 +175,6 @@ public function execute($integrationData, $fieldValues) // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Freshdesk')); } - $app_base_domamin = $integrationDetails->app_domain; $apiEndpoint = $integrationDetails->app_domain . '/api/v2/tickets'; $recordApiHelper = new RecordApiHelper($api_key, $integrationId); $freshdeskApiResponse = $recordApiHelper->execute( diff --git a/backend/Actions/Freshdesk/RecordApiHelper.php b/backend/Actions/Freshdesk/RecordApiHelper.php index f8d7dde7b..183803c72 100644 --- a/backend/Actions/Freshdesk/RecordApiHelper.php +++ b/backend/Actions/Freshdesk/RecordApiHelper.php @@ -202,6 +202,7 @@ public function execute( $integrationDetails, $app_base_domamin ) { + $apiKey = $integrationDetails->api_key ?: ($integrationDetails->value ?? ''); $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); $finalData = $finalData + ['status' => json_decode($integrationDetails->status)] + ['priority' => json_decode($integrationDetails->priority)]; @@ -225,15 +226,15 @@ public function execute( $finalDataContact = $this->generateReqDataFromFieldMapContact($fieldValues, $fieldMapContact); $avatarFieldName = $integrationDetails->actions->attachments; $avatar = $fieldValues[$avatarFieldName]; - $apiResponseFetchContact = $this->fetchContact($app_base_domamin, $finalDataContact['email'], $integrationDetails->api_key); + $apiResponseFetchContact = $this->fetchContact($app_base_domamin, $finalDataContact['email'], $apiKey); if (empty($apiResponseFetchContact)) { $typeName = 'create-contact'; - $apiResponseContact = $this->insertContact($app_base_domamin, $finalDataContact, $integrationDetails->api_key, $avatar); + $apiResponseContact = $this->insertContact($app_base_domamin, $finalDataContact, $apiKey, $avatar); } elseif ($integrationDetails->updateContact) { $typeName = 'update-contact'; $contactId = $apiResponseFetchContact[0]->id; - $apiResponseContact = $this->updateContact($app_base_domamin, $finalDataContact, $integrationDetails->api_key, $contactId); + $apiResponseContact = $this->updateContact($app_base_domamin, $finalDataContact, $apiKey, $contactId); } else { $finalData['requester_id'] = $apiResponseFetchContact[0]->id; $typeName = 'fetch-contact'; @@ -253,7 +254,7 @@ public function execute( $attachmentsFieldName = $integrationDetails->actions->file; $fileTicket = $fieldValues[$attachmentsFieldName]; - $apiResponse = $this->insertTicket($apiEndpoint, $finalData, $integrationDetails->api_key, $fileTicket); + $apiResponse = $this->insertTicket($apiEndpoint, $finalData, $apiKey, $fileTicket); if (property_exists($apiResponse, 'errors')) { LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'ticket', 'type_name' => 'create-ticket']), 'error', wp_json_encode($apiResponse)); diff --git a/backend/Actions/Freshdesk/Routes.php b/backend/Actions/Freshdesk/Routes.php index f28fdca91..24693f3ee 100644 --- a/backend/Actions/Freshdesk/Routes.php +++ b/backend/Actions/Freshdesk/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Freshdesk\FreshdeskController; use BitApps\Integrations\Core\Util\Route; -Route::post('freshdesk_authorization_and_fetch_tickets', [FreshdeskController::class, 'checkAuthorizationAndFetchTickets']); Route::post('freshdesk_fetch_ticket_fields', [FreshdeskController::class, 'getAllTicketFields']); Route::post('freshdesk_fetch_Contact_fields', [FreshdeskController::class, 'getAllContactFields']); diff --git a/backend/Actions/GamiPress/GamiPressController.php b/backend/Actions/GamiPress/GamiPressController.php index 2cecde79e..a0d0febc0 100644 --- a/backend/Actions/GamiPress/GamiPressController.php +++ b/backend/Actions/GamiPress/GamiPressController.php @@ -31,16 +31,6 @@ public static function pluginActive($option = null) return false; } - public static function authorizeGamiPress() - { - include_once ABSPATH . 'wp-admin/includes/plugin.php'; - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'GamiPress')); - } - public static function getCourses() { $courses = []; diff --git a/backend/Actions/GamiPress/Routes.php b/backend/Actions/GamiPress/Routes.php index d5bec37ec..1bc67ad84 100644 --- a/backend/Actions/GamiPress/Routes.php +++ b/backend/Actions/GamiPress/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\GamiPress\GamiPressController; use BitApps\Integrations\Core\Util\Route; -Route::post('gamiPress_authorize', [GamiPressController::class, 'authorizeGamiPress']); Route::post('gamiPress_fetch_all_rank_type', [GamiPressController::class, 'fetchAllRankType']); Route::post('gamiPress_fetch_all_rank_by_type', [GamiPressController::class, 'fetchAllRankBYType']); Route::post('gamiPress_fetch_all_achievement_type', [GamiPressController::class, 'fetchAllAchievementType']); diff --git a/backend/Actions/GetResponse/GetResponseController.php b/backend/Actions/GetResponse/GetResponseController.php index 9770d13bc..6dd474f56 100644 --- a/backend/Actions/GetResponse/GetResponseController.php +++ b/backend/Actions/GetResponse/GetResponseController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\GetResponse; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,14 @@ */ class GetResponseController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'getresponse', + 'fields' => [ + 'auth_token' => 'value', + ], + ]; + protected $_defaultHeader; private $baseUrl = 'https://api.getresponse.com/v3/'; @@ -97,9 +106,9 @@ public function fetchAllTags($requestParams) } } - public function authentication($refreshFieldsRequestParams) + public function fetchAllList($requestParams) { - if (empty($refreshFieldsRequestParams->auth_token)) { + if (empty($requestParams->auth_token)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -108,16 +117,14 @@ public function authentication($refreshFieldsRequestParams) 400 ); } - $apiEndpoints = $this->baseUrl . 'campaigns'; - - $apiKey = $refreshFieldsRequestParams->auth_token; + $apiEndpoints = $this->baseUrl . 'campaigns'; + $apiKey = $requestParams->auth_token; $header = [ 'X-Auth-Token' => 'api-key ' . $apiKey, ]; $response = HttpHelper::get($apiEndpoints, null, $header); - $campaigns = []; foreach ($response as $campaign) { @@ -127,7 +134,7 @@ public function authentication($refreshFieldsRequestParams) ]; } - if (property_exists($response[0], 'campaignId')) { + if (!empty($response) && isset($response[0]) && property_exists($response[0], 'campaignId')) { wp_send_json_success($campaigns, 200); } else { wp_send_json_error(__('Please enter valid API key', 'bit-integrations'), 400); diff --git a/backend/Actions/GetResponse/Routes.php b/backend/Actions/GetResponse/Routes.php index 258f2169e..3eb8e8499 100644 --- a/backend/Actions/GetResponse/Routes.php +++ b/backend/Actions/GetResponse/Routes.php @@ -8,6 +8,5 @@ use BitApps\Integrations\Core\Util\Route; Route::post('getresponse_fetch_all_tags', [GetResponseController::class, 'fetchAllTags']); -Route::post('getresponse_authentication', [GetResponseController::class, 'authentication']); Route::post('getresponse_fetch_all_list', [GetResponseController::class, 'fetchAllList']); Route::post('getresponse_fetch_custom_fields', [GetResponseController::class, 'fetchCustomFields']); diff --git a/backend/Actions/Getgist/GetgistController.php b/backend/Actions/Getgist/GetgistController.php index c60984ded..abb5ab6df 100644 --- a/backend/Actions/Getgist/GetgistController.php +++ b/backend/Actions/Getgist/GetgistController.php @@ -2,45 +2,27 @@ namespace BitApps\Integrations\Actions\Getgist; -use BitApps\Integrations\Core\Util\HttpHelper; +use BitApps\Integrations\Authorization\AuthorizationType; use WP_Error; class GetgistController { public const APIENDPOINT = 'https://api.getgist.com'; - public static function getgistAuthorize($requestsParams) - { - if (empty($requestsParams->api_key)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = self::APIENDPOINT . '/contacts'; - $authorizationHeader['Authorization'] = "Bearer {$requestsParams->api_key}"; - $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); - - if (is_wp_error($apiResponse) || $apiResponse->code === 'authentication_failed') { - wp_send_json_error( - empty($apiResponse->code) ? 'Unknown' : $apiResponse->message, - 400 - ); - } - - wp_send_json_success(true); - } + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'getgist', + 'fields' => [ + 'api_key' => 'value', + ], + ]; public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; - $api_key = $integrationDetails->api_key; + $api_key = $integrationDetails->api_key ?: ($integrationDetails->value ?? ''); $fieldMap = $integrationDetails->field_map; $actions = $integrationDetails->actions; if (empty($api_key) diff --git a/backend/Actions/Getgist/Routes.php b/backend/Actions/Getgist/Routes.php index 6b7eeed6b..d59d7bee0 100644 --- a/backend/Actions/Getgist/Routes.php +++ b/backend/Actions/Getgist/Routes.php @@ -6,5 +6,3 @@ use BitApps\Integrations\Actions\Getgist\GetgistController; use BitApps\Integrations\Core\Util\Route; - -Route::post('getgist_authorize', [GetgistController::class, 'getgistAuthorize']); diff --git a/backend/Actions/GiveWp/GiveWpController.php b/backend/Actions/GiveWp/GiveWpController.php index eb456e4dd..2b3388ccb 100644 --- a/backend/Actions/GiveWp/GiveWpController.php +++ b/backend/Actions/GiveWp/GiveWpController.php @@ -15,16 +15,6 @@ public static function pluginActive($option = null) return false; } - public static function authorizeGiveWp() - { - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - // translators: %s: Placeholder value - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'GiveWp')); - } - public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; diff --git a/backend/Actions/GiveWp/Routes.php b/backend/Actions/GiveWp/Routes.php deleted file mode 100644 index e03dd9e81..000000000 --- a/backend/Actions/GiveWp/Routes.php +++ /dev/null @@ -1,10 +0,0 @@ - AuthorizationType::OAUTH2, + 'slug' => 'googlecalendar', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; + private $integrationID; public function __construct($integrationID) @@ -17,43 +28,41 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function authorization($requestParams) + public static function getAllCalendarLists($queryParams) { - if (empty($requestParams->clientId) || empty($requestParams->clientSecret) || empty($requestParams->code) || empty($requestParams->redirectURI)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); + $tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $clientId = $queryParams->clientId ?? ''; + $clientSecret = $queryParams->clientSecret ?? ''; + $flowID = $queryParams->flowID ?? null; + $isConnectionAuth = !empty($queryParams->connection_id); + + if (empty($tokenDetails->access_token) && !empty($queryParams->accessToken)) { + $tokenDetails->access_token = $queryParams->accessToken; + $tokenDetails->refresh_token = $queryParams->refreshToken ?? ''; } - $body = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestParams->clientId, - 'client_secret' => $requestParams->clientSecret, - 'redirect_uri' => urldecode($requestParams->redirectURI), - 'code' => urldecode($requestParams->code) - ]; + if (empty($tokenDetails->access_token)) { + wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); + } - $apiEndpoint = 'https://oauth2.googleapis.com/token'; - $header['Content-Type'] = 'application/x-www-form-urlencoded'; + $oldToken = $tokenDetails->access_token; - $apiResponse = HttpHelper::post($apiEndpoint, $body, $header); + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $clientId, $clientSecret); - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error(empty($apiResponse->error_description) ? 'Unknown' : $apiResponse->error_description, 400); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + wp_send_json_error(__('Authorization failed', 'bit-integrations'), 400); + } - public static function getAllCalendarLists($queryParams) - { - if (empty($queryParams->tokenDetails) || empty($queryParams->clientId) || empty($queryParams->clientSecret)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); + if (!empty($flowID) && $tokenDetails->access_token !== $oldToken) { + self::saveRefreshedToken($flowID, $tokenDetails); + } } - $token = self::tokenExpiryCheck($queryParams->tokenDetails, $queryParams->clientId, $queryParams->clientSecret); - $lists = self::getGoogleCalendarList($token->access_token); + $lists = self::getGoogleCalendarList($tokenDetails->access_token); $data = []; - if (\is_array($lists->items)) { + if (!empty($lists) && !empty($lists->items) && \is_array($lists->items)) { foreach ($lists->items as $list) { $data[] = (object) [ 'id' => $list->id, @@ -64,28 +73,47 @@ public static function getAllCalendarLists($queryParams) } $response['googleCalendarLists'] = $data; - $response['tokenDetails'] = $token; + $response['tokenDetails'] = $tokenDetails; wp_send_json_success($response, 200); } public function execute($integrationData, $fieldValues) { - if (empty($integrationData->flow_details->tokenDetails->access_token)) { - // translators: %s: Service name - LogHandler::save($this->integrationID, wp_json_encode(['type' => 'record', 'type_name' => 'insert']), 'error', wp_sprintf(__('Not Authorization By %s', 'bit-integrations'), 'GoogleCalendar')); - - return false; - } - $integrationDetails = $integrationData->flow_details; $actions = $integrationDetails->actions; $timeZone = $integrationDetails->timeZone; $fieldMap = $integrationDetails->field_map; $calendarId = $integrationDetails->calendarId; $reminderFieldMap = $integrationDetails->reminder_field_map; - $tokenDetails = self::tokenExpiryCheck($integrationDetails->tokenDetails, $integrationDetails->clientId, $integrationDetails->clientSecret); - if ($tokenDetails->access_token !== $integrationDetails->tokenDetails->access_token) { - $this->saveRefreshedToken($this->integrationID, $tokenDetails); + $isConnectionAuth = !empty($integrationDetails->connection_id); + $tokenDetails = self::normalizeConnectionToken($integrationDetails->tokenDetails ?? null); + + if (empty($tokenDetails->access_token)) { + // translators: %s: Service name + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'record', 'type_name' => 'insert']), 'error', wp_sprintf(__('Not Authorization By %s', 'bit-integrations'), 'GoogleCalendar')); + + return false; + } + + $oldToken = $tokenDetails->access_token; + + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck( + $tokenDetails, + $integrationDetails->clientId ?? '', + $integrationDetails->clientSecret ?? '' + ); + + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + // translators: %s: Service name + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'record', 'type_name' => 'insert']), 'error', wp_sprintf(__('Not Authorization By %s', 'bit-integrations'), 'GoogleCalendar')); + + return false; + } + + if ($tokenDetails->access_token !== $oldToken) { + $this->saveRefreshedToken($this->integrationID, $tokenDetails); + } } if (empty($fieldMap)) { @@ -125,15 +153,21 @@ protected static function tokenExpiryCheck($token, $clientId, $clientSecret) return false; } - if ((\intval($token->generates_on) + (55 * 60)) < time()) { + $generatedOn = !empty($token->generates_on) ? (int) $token->generates_on : (int) ($token->generated_at ?? 0); + + if ($generatedOn > 0 && ($generatedOn + (55 * 60)) < time()) { $refreshToken = self::refreshToken($token->refresh_token, $clientId, $clientSecret); if (is_wp_error($refreshToken) || !empty($refreshToken->error)) { return false; } - $token->access_token = $refreshToken->access_token; - $token->expires_in = $refreshToken->expires_in; - $token->generates_on = $refreshToken->generates_on; + if (isset($refreshToken->access_token)) { + $token->access_token = $refreshToken->access_token; + $token->expires_in = $refreshToken->expires_in; + $token->generates_on = $refreshToken->generates_on; + $token->generated_at = $refreshToken->generated_at; + $token->refresh_token = $refreshToken->refresh_token; + } } return $token; @@ -155,6 +189,20 @@ protected static function refreshToken($refresh_token, $clientId, $clientSecret) } $token = $apiResponse; $token->generates_on = time(); + $token->generated_at = $token->generates_on; + + return $token; + } + + protected static function normalizeConnectionToken($token) + { + if (!\is_object($token)) { + $token = (object) []; + } + + if (empty($token->generates_on) && !empty($token->generated_at)) { + $token->generates_on = (int) $token->generated_at; + } return $token; } diff --git a/backend/Actions/GoogleCalendar/Routes.php b/backend/Actions/GoogleCalendar/Routes.php index 93a635f05..670b36d4f 100644 --- a/backend/Actions/GoogleCalendar/Routes.php +++ b/backend/Actions/GoogleCalendar/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\GoogleCalendar\GoogleCalendarController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('googleCalendar_authorization', [GoogleCalendarController::class, 'authorization']); Route::post('googleCalendar_get_all_lists', [GoogleCalendarController::class, 'getAllCalendarLists']); diff --git a/backend/Actions/GoogleContacts/GoogleContactsController.php b/backend/Actions/GoogleContacts/GoogleContactsController.php index 081ad4eb0..077485cf8 100644 --- a/backend/Actions/GoogleContacts/GoogleContactsController.php +++ b/backend/Actions/GoogleContacts/GoogleContactsController.php @@ -3,6 +3,7 @@ namespace BitApps\Integrations\Actions\GoogleContacts; use BitApps\Integrations\Actions\GoogleContacts\RecordApiHelper as GoogleContactsRecordApiHelper; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use BitApps\Integrations\Log\LogHandler; @@ -10,6 +11,16 @@ class GoogleContactsController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'googlecontacts', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; + private $integrationID; public function __construct($integrationID) @@ -44,11 +55,31 @@ public static function authorization($requestParams) public static function getAllCalendarLists($queryParams) { - if (empty($queryParams->tokenDetails) || empty($queryParams->clientId) || empty($queryParams->clientSecret)) { + $tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $clientId = $queryParams->clientId ?? ''; + $clientSecret = $queryParams->clientSecret ?? ''; + $flowID = $queryParams->flowID ?? null; + $isConnectionAuth = !empty($queryParams->connection_id); + + if (empty($tokenDetails->access_token)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $token = self::tokenExpiryCheck($queryParams->tokenDetails, $queryParams->clientId, $queryParams->clientSecret); + $oldToken = $tokenDetails->access_token; + + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $clientId, $clientSecret); + + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + wp_send_json_error(__('Authorization failed', 'bit-integrations'), 400); + } + + if (!empty($flowID) && $tokenDetails->access_token !== $oldToken) { + self::saveRefreshedToken($flowID, $tokenDetails); + } + } + + $token = $tokenDetails; $lists = self::getGoogleCalendarList($token->access_token); $data = []; @@ -80,9 +111,31 @@ public function execute($integrationData, $fieldValues) $actions = $integrationDetails->actions; $fieldMap = $integrationDetails->field_map; $mainAction = $integrationDetails->mainAction; - $tokenDetails = self::tokenExpiryCheck($integrationDetails->tokenDetails, $integrationDetails->clientId, $integrationDetails->clientSecret); - if ($tokenDetails->access_token !== $integrationDetails->tokenDetails->access_token) { - $this->saveRefreshedToken($this->integrationID, $tokenDetails); + $isConnectionAuth = !empty($integrationDetails->connection_id); + $tokenDetails = self::normalizeConnectionToken($integrationDetails->tokenDetails ?? null); + + if (empty($tokenDetails->access_token)) { + // translators: %s: Service name + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'record', 'type_name' => 'insert']), 'error', wp_sprintf(__('Not Authorization By %s', 'bit-integrations'), 'GoogleContact')); + + return false; + } + + $oldToken = $tokenDetails->access_token; + + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $integrationDetails->clientId, $integrationDetails->clientSecret); + + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + // translators: %s: Service name + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'record', 'type_name' => 'insert']), 'error', wp_sprintf(__('Not Authorization By %s', 'bit-integrations'), 'GoogleContact')); + + return false; + } + + if ($tokenDetails->access_token !== $oldToken) { + self::saveRefreshedToken($this->integrationID, $tokenDetails); + } } if (empty($fieldMap)) { @@ -122,7 +175,9 @@ protected static function tokenExpiryCheck($token, $clientId, $clientSecret) return false; } - if ((\intval($token->generates_on) + (55 * 60)) < time()) { + $generatedOn = !empty($token->generates_on) ? (int) $token->generates_on : (int) ($token->generated_at ?? 0); + + if ($generatedOn > 0 && ($generatedOn + (55 * 60)) < time()) { $refreshToken = self::refreshToken($token->refresh_token, $clientId, $clientSecret); if (is_wp_error($refreshToken) || !empty($refreshToken->error)) { return false; @@ -131,6 +186,8 @@ protected static function tokenExpiryCheck($token, $clientId, $clientSecret) $token->access_token = $refreshToken->access_token; $token->expires_in = $refreshToken->expires_in; $token->generates_on = $refreshToken->generates_on; + $token->generated_at = $refreshToken->generated_at; + $token->refresh_token = $refreshToken->refresh_token; } return $token; @@ -152,11 +209,25 @@ protected static function refreshToken($refresh_token, $clientId, $clientSecret) } $token = $apiResponse; $token->generates_on = time(); + $token->generated_at = $token->generates_on; + + return $token; + } + + protected static function normalizeConnectionToken($token) + { + if (!\is_object($token)) { + $token = (object) []; + } + + if (empty($token->generates_on) && !empty($token->generated_at)) { + $token->generates_on = (int) $token->generated_at; + } return $token; } - protected function saveRefreshedToken($integrationID, $tokenDetails) + protected static function saveRefreshedToken($integrationID, $tokenDetails) { if (empty($integrationID)) { return; diff --git a/backend/Actions/GoogleContacts/Routes.php b/backend/Actions/GoogleContacts/Routes.php index f18e6089b..5c539fc24 100644 --- a/backend/Actions/GoogleContacts/Routes.php +++ b/backend/Actions/GoogleContacts/Routes.php @@ -3,8 +3,3 @@ if (!defined('ABSPATH')) { exit; } - -use BitApps\Integrations\Actions\GoogleContacts\GoogleContactsController; -use BitApps\Integrations\Core\Util\Route; - -Route::no_sanitize()->post('googleContacts_authorization', [GoogleContactsController::class, 'authorization']); diff --git a/backend/Actions/GoogleDrive/GoogleDriveController.php b/backend/Actions/GoogleDrive/GoogleDriveController.php index 044a7e051..69ac7fc0d 100644 --- a/backend/Actions/GoogleDrive/GoogleDriveController.php +++ b/backend/Actions/GoogleDrive/GoogleDriveController.php @@ -3,6 +3,7 @@ namespace BitApps\Integrations\Actions\GoogleDrive; use BitApps\Integrations\Actions\GoogleDrive\RecordApiHelper as GoogleDriveRecordApiHelper; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use BitApps\Integrations\Log\LogHandler; @@ -10,6 +11,16 @@ class GoogleDriveController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'googledrive', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; + private $integrationID; public function __construct($integrationID) @@ -17,57 +28,54 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function authorization($requestParams) + public static function getAllFolders($queryParams) { - if (empty($requestParams->clientId) || empty($requestParams->clientSecret) || empty($requestParams->code) || empty($requestParams->redirectURI)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } + $tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $clientId = $queryParams->clientId ?? ''; + $clientSecret = $queryParams->clientSecret ?? ''; + $flowID = $queryParams->flowID ?? null; + $isConnectionAuth = !empty($queryParams->connection_id); - $body = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestParams->clientId, - 'client_secret' => $requestParams->clientSecret, - 'redirect_uri' => urldecode($requestParams->redirectURI), - 'code' => urldecode($requestParams->code) - ]; - - $apiEndpoint = 'https://oauth2.googleapis.com/token'; - $header['Content-Type'] = 'application/x-www-form-urlencoded'; - $apiResponse = HttpHelper::post($apiEndpoint, $body, $header); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error(empty($apiResponse->error_description) ? 'Unknown' : $apiResponse->error_description, 400); + if (empty($tokenDetails->access_token) && !empty($queryParams->accessToken)) { + $tokenDetails->access_token = $queryParams->accessToken; + $tokenDetails->refresh_token = $queryParams->refreshToken ?? ''; } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public static function getAllFolders($queryParams) - { - if (empty($queryParams->tokenDetails) || empty($queryParams->clientId) || empty($queryParams->clientSecret)) { + if (empty($tokenDetails->access_token)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $token = self::tokenExpiryCheck($queryParams->tokenDetails, $queryParams->clientId, $queryParams->clientSecret); - if ($token->access_token !== $queryParams->tokenDetails->access_token) { - self::saveRefreshedToken($queryParams->flowID, $token); + $oldToken = $tokenDetails->access_token; + + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $clientId, $clientSecret); + + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + wp_send_json_error(__('Authorization failed', 'bit-integrations'), 400); + } + + if (!empty($flowID) && $tokenDetails->access_token !== $oldToken) { + self::saveRefreshedToken($flowID, $tokenDetails); + } } - $folders = self::getGoogleDriveFoldersList($token->access_token); - $folders = self::getPathFromParentId($folders->files); + $folders = self::getGoogleDriveFoldersList($tokenDetails->access_token); $data = []; - if (\is_array($folders)) { - foreach ($folders as $folder) { - $data[] = (object) [ - 'id' => $folder->id, - 'name' => $folder->name, - ]; + if (!empty($folders) && !empty($folders->files)) { + $folders = self::getPathFromParentId($folders->files); + if (\is_array($folders)) { + foreach ($folders as $folder) { + $data[] = (object) [ + 'id' => $folder->id, + 'name' => $folder->name, + ]; + } } } $response['googleDriveFoldersList'] = $data; - $response['tokenDetails'] = $token; + $response['tokenDetails'] = $tokenDetails; wp_send_json_success($response, 200); } @@ -90,19 +98,38 @@ public static function getGoogleDriveFoldersList($token) public function execute($integrationData, $fieldValues) { - if (empty($integrationData->flow_details->tokenDetails->access_token)) { + $integrationDetails = $integrationData->flow_details; + $actions = $integrationDetails->actions; + $fieldMap = $integrationDetails->field_map; + $isConnectionAuth = !empty($integrationDetails->connection_id); + $tokenDetails = self::normalizeConnectionToken($integrationDetails->tokenDetails ?? null); + + if (empty($tokenDetails->access_token)) { // translators: %s: Service name LogHandler::save($this->integrationID, wp_json_encode(['type' => 'googleDrive', 'type_name' => 'file_upload']), 'error', wp_sprintf(__('Not Authorization By %s', 'bit-integrations'), 'GoogleDrive')); return false; } - $integrationDetails = $integrationData->flow_details; - $actions = $integrationDetails->actions; - $fieldMap = $integrationDetails->field_map; - $tokenDetails = self::tokenExpiryCheck($integrationDetails->tokenDetails, $integrationDetails->clientId, $integrationDetails->clientSecret); - if ($tokenDetails->access_token !== $integrationDetails->tokenDetails->access_token) { - self::saveRefreshedToken($this->integrationID, $tokenDetails); + $oldToken = $tokenDetails->access_token; + + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck( + $tokenDetails, + $integrationDetails->clientId ?? '', + $integrationDetails->clientSecret ?? '' + ); + + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + // translators: %s: Service name + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'googleDrive', 'type_name' => 'file_upload']), 'error', wp_sprintf(__('Not Authorization By %s', 'bit-integrations'), 'GoogleDrive')); + + return false; + } + + if ($tokenDetails->access_token !== $oldToken) { + self::saveRefreshedToken($this->integrationID, $tokenDetails); + } } if (empty($fieldMap)) { @@ -155,15 +182,21 @@ private static function tokenExpiryCheck($token, $clientId, $clientSecret) return false; } - if ((\intval($token->generates_on) + (55 * 60)) < time()) { + $generatedOn = !empty($token->generates_on) ? (int) $token->generates_on : (int) ($token->generated_at ?? 0); + + if ($generatedOn > 0 && ($generatedOn + (55 * 60)) < time()) { $refreshToken = self::refreshToken($token->refresh_token, $clientId, $clientSecret); if (is_wp_error($refreshToken) || !empty($refreshToken->error)) { return false; } - $token->access_token = $refreshToken->access_token; - $token->expires_in = $refreshToken->expires_in; - $token->generates_on = $refreshToken->generates_on; + if (isset($refreshToken->access_token)) { + $token->access_token = $refreshToken->access_token; + $token->expires_in = $refreshToken->expires_in; + $token->generates_on = $refreshToken->generates_on; + $token->generated_at = $refreshToken->generated_at; + $token->refresh_token = $refreshToken->refresh_token; + } } return $token; @@ -185,6 +218,20 @@ private static function refreshToken($refresh_token, $clientId, $clientSecret) } $token = $apiResponse; $token->generates_on = time(); + $token->generated_at = $token->generates_on; + + return $token; + } + + private static function normalizeConnectionToken($token) + { + if (!\is_object($token)) { + $token = (object) []; + } + + if (empty($token->generates_on) && !empty($token->generated_at)) { + $token->generates_on = (int) $token->generated_at; + } return $token; } diff --git a/backend/Actions/GoogleDrive/Routes.php b/backend/Actions/GoogleDrive/Routes.php index 751a1f328..d0e29db27 100644 --- a/backend/Actions/GoogleDrive/Routes.php +++ b/backend/Actions/GoogleDrive/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\GoogleDrive\GoogleDriveController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('googleDrive_authorization', [GoogleDriveController::class, 'authorization']); Route::post('googleDrive_get_all_folders', [GoogleDriveController::class, 'getAllFolders']); diff --git a/backend/Actions/GoogleSheet/GoogleSheetController.php b/backend/Actions/GoogleSheet/GoogleSheetController.php index cd20c51f5..0a73c90ed 100644 --- a/backend/Actions/GoogleSheet/GoogleSheetController.php +++ b/backend/Actions/GoogleSheet/GoogleSheetController.php @@ -9,6 +9,7 @@ if (! defined('ABSPATH')) { exit; } +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use WP_Error; @@ -18,6 +19,16 @@ */ class GoogleSheetController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'googlesheet', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -32,7 +43,6 @@ public function __construct($integrationID) */ public static function registerAjax() { - add_action('wp_ajax_gsheet_generate_token', [__CLASS__, 'generateTokens']); add_action('wp_ajax_gsheet_refresh_spreadsheets', [__CLASS__, 'refreshSpreadsheetsAjaxHelper']); add_action('wp_ajax_gsheet_refresh_worksheets', [__CLASS__, 'refreshWorksheetsAjaxHelper']); add_action('wp_ajax_gsheet_refresh_worksheet_headers', [__CLASS__, 'refreshWorksheetHeadersAjaxHelper']); @@ -93,8 +103,10 @@ public static function generateTokens($requestsParams) */ public static function refreshSpreadsheetsAjaxHelper($queryParams) { - if (empty($queryParams->tokenDetails) - ) { + $queryParams->tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $isConnectionAuth = !empty($queryParams->connection_id); + + if (empty($queryParams->tokenDetails->access_token)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -105,10 +117,13 @@ public static function refreshSpreadsheetsAjaxHelper($queryParams) } $spreadSheets = "https://www.googleapis.com/drive/v3/files?q=mimeType%20%3D%20'application%2Fvnd.google-apps.spreadsheet'"; $response = []; - if ((\intval($queryParams->tokenDetails->generates_on) + (55 * 60)) < time()) { + if (!$isConnectionAuth && (\intval($queryParams->tokenDetails->generates_on) + (55 * 60)) < time()) { $response['tokenDetails'] = GoogleSheetController::refreshAccessToken($queryParams); - $authorizationHeader['Authorization'] = 'Bearer ' . $response['tokenDetails']->access_token; - } else { + if ($response['tokenDetails'] && !empty($response['tokenDetails']->access_token)) { + $authorizationHeader['Authorization'] = 'Bearer ' . $response['tokenDetails']->access_token; + } + } + if (empty($authorizationHeader['Authorization'])) { $authorizationHeader['Authorization'] = "Bearer {$queryParams->tokenDetails->access_token}"; } @@ -130,8 +145,8 @@ public static function refreshSpreadsheetsAjaxHelper($queryParams) 400 ); } - if (!empty($response['tokenDetails']) && !empty($queryParams->id)) { - GoogleSheetController::saveRefreshedToken($queryParams->id, $response['tokenDetails'], $response['$spreadsheets']); + if (!$isConnectionAuth && !empty($response['tokenDetails']) && !empty($queryParams->id)) { + GoogleSheetController::saveRefreshedToken($queryParams->id, $response['tokenDetails'], $response); } wp_send_json_success($response, 200); } @@ -145,7 +160,10 @@ public static function refreshSpreadsheetsAjaxHelper($queryParams) */ public static function refreshWorksheetsAjaxHelper($queryParams) { - if (empty($queryParams->tokenDetails) + $queryParams->tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $isConnectionAuth = !empty($queryParams->connection_id); + + if (empty($queryParams->tokenDetails->access_token) || empty($queryParams->spreadsheetId) ) { wp_send_json_error( @@ -157,8 +175,11 @@ public static function refreshWorksheetsAjaxHelper($queryParams) ); } $response = []; - if ((\intval($queryParams->tokenDetails->generates_on) + (55 * 60)) < time()) { + if (!$isConnectionAuth && (\intval($queryParams->tokenDetails->generates_on) + (55 * 60)) < time()) { $response['tokenDetails'] = GoogleSheetController::refreshAccessToken($queryParams); + if ($response['tokenDetails'] && !empty($response['tokenDetails']->access_token)) { + $queryParams->tokenDetails = $response['tokenDetails']; + } } $worksheetsMetaApiEndpoint = "https://sheets.googleapis.com/v4/spreadsheets/{$queryParams->spreadsheetId}?&fields=sheets.properties"; @@ -176,7 +197,7 @@ public static function refreshWorksheetsAjaxHelper($queryParams) 400 ); } - if (!empty($response['tokenDetails']) && $response['tokenDetails'] && !empty($queryParams->id)) { + if (!$isConnectionAuth && !empty($response['tokenDetails']) && !empty($queryParams->id)) { $response['queryWorkbook'] = $queryParams->workbook; GoogleSheetController::saveRefreshedToken($queryParams->id, $response['tokenDetails'], $response); } @@ -192,8 +213,11 @@ public static function refreshWorksheetsAjaxHelper($queryParams) */ public static function refreshWorksheetHeadersAjaxHelper($queryParams) { + $queryParams->tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $isConnectionAuth = !empty($queryParams->connection_id); + if (empty($queryParams->worksheetName) - || empty($queryParams->tokenDetails) + || empty($queryParams->tokenDetails->access_token) || empty($queryParams->header) || empty($queryParams->headerRow) ) { @@ -206,8 +230,11 @@ public static function refreshWorksheetHeadersAjaxHelper($queryParams) ); } $response = []; - if ((\intval($queryParams->tokenDetails->generates_on) + (55 * 60)) < time()) { + if (!$isConnectionAuth && (\intval($queryParams->tokenDetails->generates_on) + (55 * 60)) < time()) { $response['tokenDetails'] = GoogleSheetController::refreshAccessToken($queryParams); + if ($response['tokenDetails'] && !empty($response['tokenDetails']->access_token)) { + $queryParams->tokenDetails = $response['tokenDetails']; + } } $headerRow = $queryParams->headerRow; if ($queryParams->header === 'ROWS') { @@ -237,7 +264,7 @@ public static function refreshWorksheetHeadersAjaxHelper($queryParams) $response['worksheet_headers'][] = "{$header}_{$key}"; } - if (!empty($response['tokenDetails']) && $response['tokenDetails'] && !empty($queryParams->id)) { + if (!$isConnectionAuth && !empty($response['tokenDetails']) && !empty($queryParams->id)) { $response['queryModule'] = $queryParams->module; GoogleSheetController::saveRefreshedToken($queryParams->id, $response['tokenDetails'], $response); } @@ -251,7 +278,8 @@ public function execute($integrationData, $fieldValues) // wp_send_json_success($integrationDetails); - $tokenDetails = $integrationDetails->tokenDetails; + $tokenDetails = self::normalizeConnectionToken($integrationDetails->tokenDetails ?? null); + $isConnectionAuth = !empty($integrationDetails->connection_id); $spreadsheetId = $integrationDetails->spreadsheetId; $worksheetName = $integrationDetails->worksheetName; $headerRow = $integrationDetails->headerRow; @@ -269,7 +297,7 @@ public function execute($integrationData, $fieldValues) return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Google sheet')); } - if ((\intval($tokenDetails->generates_on) + (55 * 60)) < time()) { + if (!$isConnectionAuth && (\intval($tokenDetails->generates_on) + (55 * 60)) < time()) { $requiredParams['clientId'] = $integrationDetails->clientId; $requiredParams['clientSecret'] = $integrationDetails->clientSecret; $requiredParams['tokenDetails'] = $tokenDetails; @@ -331,6 +359,7 @@ protected static function refreshAccessToken($apiData) return false; } $tokenDetails->generates_on = time(); + $tokenDetails->generated_at = $tokenDetails->generates_on; $tokenDetails->access_token = $apiResponse->access_token; return $tokenDetails; @@ -372,4 +401,17 @@ protected static function saveRefreshedToken($integrationID, $tokenDetails, $oth $flow->update($integrationID, ['flow_details' => wp_json_encode($newDetails)]); } + + private static function normalizeConnectionToken($token) + { + if (!\is_object($token)) { + $token = (object) []; + } + + if (empty($token->generates_on) && !empty($token->generated_at)) { + $token->generates_on = (int) $token->generated_at; + } + + return $token; + } } diff --git a/backend/Actions/GoogleSheet/Routes.php b/backend/Actions/GoogleSheet/Routes.php index df662a004..ce9ddb943 100644 --- a/backend/Actions/GoogleSheet/Routes.php +++ b/backend/Actions/GoogleSheet/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\GoogleSheet\GoogleSheetController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('gsheet_generate_token', [GoogleSheetController::class, 'generateTokens']); Route::post('gsheet_refresh_spreadsheets', [GoogleSheetController::class, 'refreshSpreadsheetsAjaxHelper']); Route::post('gsheet_refresh_worksheets', [GoogleSheetController::class, 'refreshWorksheetsAjaxHelper']); Route::post('gsheet_refresh_worksheet_headers', [GoogleSheetController::class, 'refreshWorksheetHeadersAjaxHelper']); diff --git a/backend/Actions/Gravitec/GravitecController.php b/backend/Actions/Gravitec/GravitecController.php index 3ca1a96c7..b97fe511e 100644 --- a/backend/Actions/Gravitec/GravitecController.php +++ b/backend/Actions/Gravitec/GravitecController.php @@ -6,7 +6,7 @@ namespace BitApps\Integrations\Actions\Gravitec; -use BitApps\Integrations\Core\Util\HttpHelper; +use BitApps\Integrations\Authorization\AuthorizationType; use WP_Error; /** @@ -14,35 +14,15 @@ */ class GravitecController { - public function authentication($fieldsRequestParams) - { - if (empty($fieldsRequestParams->site_url) || empty($fieldsRequestParams->app_key) || empty($fieldsRequestParams->app_secret)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $headers = [ - 'Content-Type' => 'application/json', - 'Authorization' => 'Basic ' . base64_encode("{$fieldsRequestParams->app_key}:{$fieldsRequestParams->app_secret}") - ]; - - $data = [ - 'payload' => [ - 'title' => 'Authorization', - 'message' => __('Authorized Successfully', 'bit-integrations'), - 'icon' => BTCBI_ASSET_URI . '/gravitec.jpg', - 'redirect_url' => $fieldsRequestParams->site_url - ] - ]; - - $apiEndpoint = 'https://uapi.gravitec.net/api/v3/push'; - $response = HttpHelper::post($apiEndpoint, wp_json_encode($data), $headers); - - if (isset($response->id)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid Site Url, App Key & App Secret', 'bit-integrations'), 400); - } - } + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'gravitec', + 'fields' => [ + 'app_key' => 'username', + 'app_secret' => 'password', + 'site_url' => 'site_url', + ], + ]; public function execute($integrationData, $fieldValues) { diff --git a/backend/Actions/Gravitec/Routes.php b/backend/Actions/Gravitec/Routes.php index b755c128a..3f11357c5 100644 --- a/backend/Actions/Gravitec/Routes.php +++ b/backend/Actions/Gravitec/Routes.php @@ -6,5 +6,3 @@ use BitApps\Integrations\Actions\Gravitec\GravitecController; use BitApps\Integrations\Core\Util\Route; - -Route::post('gravitec_authentication', [GravitecController::class, 'authentication']); diff --git a/backend/Actions/Groundhogg/GroundhoggController.php b/backend/Actions/Groundhogg/GroundhoggController.php index 0488e49e7..33a19f801 100644 --- a/backend/Actions/Groundhogg/GroundhoggController.php +++ b/backend/Actions/Groundhogg/GroundhoggController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Groundhogg; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,16 @@ */ class GroundhoggController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'groundhogg', + 'fields' => [ + 'token' => 'value', + 'public_key' => 'public_key', + 'domainName' => 'domainName', + ], + ]; + public static function groundhoggFetchAllTags($requestParams) { if ( @@ -47,39 +58,6 @@ public static function groundhoggFetchAllTags($requestParams) } } - public static function fetchAllContacts($requestParams) - { - if ( - empty($requestParams->public_key) || empty($requestParams->token) - || empty($requestParams->domainName) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $authorizationHeader = [ - 'Gh-Token' => $requestParams->token, - 'Gh-Public-Key' => $requestParams->public_key - ]; - $apiEndpoint = $requestParams->domainName . '/index.php?rest_route=/gh/v4/contacts'; - - $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); - - if ($apiResponse->status === 'success') { - wp_send_json_success($apiResponse, 200); - } else { - wp_send_json_error( - 'There is an error .', - 400 - ); - } - } - public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; diff --git a/backend/Actions/Groundhogg/Routes.php b/backend/Actions/Groundhogg/Routes.php index dedef3fd1..f25e27f5a 100644 --- a/backend/Actions/Groundhogg/Routes.php +++ b/backend/Actions/Groundhogg/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\Groundhogg\GroundhoggController; use BitApps\Integrations\Core\Util\Route; -Route::post('groundhogg_authorization_and_fetch_contacts', [GroundhoggController::class, 'fetchAllContacts']); Route::post('groundhogg_fetch_all_tags', [GroundhoggController::class, 'groundhoggFetchAllTags']); diff --git a/backend/Actions/HighLevel/HighLevelController.php b/backend/Actions/HighLevel/HighLevelController.php index 76a6aceaf..ff978edd0 100644 --- a/backend/Actions/HighLevel/HighLevelController.php +++ b/backend/Actions/HighLevel/HighLevelController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\HighLevel; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -15,6 +16,15 @@ class HighLevelController { private const V2_HEADER_VERSION = '2021-07-28'; + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'gohighlevel', + 'fields' => [ + 'api_key' => 'token', + 'version' => 'version', + 'location_id' => 'location_id', + ], + ]; private $_integrationID; @@ -23,34 +33,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - public static function highLevelAuthorization($requestsParams) - { - $apiKey = self::getApiKey($requestsParams); - $version = self::getVersion($requestsParams); - $headers = self::buildHeaders($apiKey, $version); - - if ($version === 'v2') { - $locationId = self::getLocationIdIfV2($requestsParams, $version); - $endpoint = "https://services.leadconnectorhq.com/locations/{$locationId}"; - $response = self::getOrError($endpoint, $headers); - - if (!isset($response->location) && !isset($response->id)) { - wp_send_json_error($response, 400); - } - - wp_send_json_success($response); - } - - $endpoint = 'https://rest.gohighlevel.com/v1/contacts/?limit=1'; - $response = self::getOrError($endpoint, $headers); - - if (!isset($response->contacts) || !\is_array($response->contacts)) { - wp_send_json_error($response, 400); - } - - wp_send_json_success($response); - } - public static function getCustomFields($requestsParams) { $apiKey = self::getApiKey($requestsParams); diff --git a/backend/Actions/HighLevel/Routes.php b/backend/Actions/HighLevel/Routes.php index f0a95b402..fc68b6516 100644 --- a/backend/Actions/HighLevel/Routes.php +++ b/backend/Actions/HighLevel/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\HighLevel\HighLevelController; use BitApps\Integrations\Core\Util\Route; -Route::post('highLevel_authorization', [HighLevelController::class, 'highLevelAuthorization']); Route::post('get_highLevel_contact_custom_fields', [HighLevelController::class, 'getCustomFields']); Route::post('high_level_contact_tags', [HighLevelController::class, 'getAllTags']); Route::post('get_highLevel_contacts', [HighLevelController::class, 'getContacts']); diff --git a/backend/Actions/Hubspot/HubspotController.php b/backend/Actions/Hubspot/HubspotController.php index 322d7d810..bd361c4f5 100644 --- a/backend/Actions/Hubspot/HubspotController.php +++ b/backend/Actions/Hubspot/HubspotController.php @@ -2,12 +2,21 @@ namespace BitApps\Integrations\Actions\Hubspot; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Log\LogHandler; use WP_Error; final class HubspotController { + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'hubspot', + 'fields' => [ + 'api_key' => 'token', + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -15,26 +24,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - public static function authorization($requestParams) - { - if (empty($requestParams->api_key)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiEndpoint = 'https://api.hubapi.com/crm/v3/objects/contacts'; - $header = [ - 'authorization' => 'Bearer ' . $requestParams->api_key - ]; - - $apiResponse = HttpHelper::get($apiEndpoint, null, $header); - - if (isset($apiResponse->results)) { - wp_send_json_success(__('Authorization successfull', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Authorization failed', 'bit-integrations'), 400); - } - } - public static function getFields($requestParams) { if (empty($requestParams->api_key) || empty($requestParams->type)) { diff --git a/backend/Actions/Hubspot/Routes.php b/backend/Actions/Hubspot/Routes.php index d3be0f01c..ae979ffd9 100644 --- a/backend/Actions/Hubspot/Routes.php +++ b/backend/Actions/Hubspot/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Hubspot\HubspotController; use BitApps\Integrations\Core\Util\Route; -Route::post('hubSpot_authorization', [HubspotController::class, 'authorization']); Route::post('getFields', [HubspotController::class, 'getFields']); Route::post('hubspot_pipeline', [HubspotController::class, 'getAllPipelines']); Route::post('hubspot_owners', [HubspotController::class, 'getAllOwners']); diff --git a/backend/Actions/Insightly/InsightlyController.php b/backend/Actions/Insightly/InsightlyController.php index 8b8aca2d9..c977cea7c 100644 --- a/backend/Actions/Insightly/InsightlyController.php +++ b/backend/Actions/Insightly/InsightlyController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Insightly; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,29 +15,16 @@ */ class InsightlyController { - protected $_defaultHeader; - - public function authentication($fieldsRequestParams) - { - if (empty($fieldsRequestParams->api_key) || empty($fieldsRequestParams->api_url)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiUrl = $fieldsRequestParams->api_url; - $apiKey = $fieldsRequestParams->api_key; - $apiEndpoint = 'https://api.' . $apiUrl . '/v3.1/Users'; - $headers = [ - 'Authorization' => 'Basic ' . base64_encode("{$apiKey}:"), - ]; - - $response = HttpHelper::get($apiEndpoint, null, $headers); + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'insightly', + 'fields' => [ + 'api_key' => 'username', + 'api_url' => 'api_url', + ], + ]; - if (\is_array($response) && isset($response[0]->USER_ID)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API URL & API key', 'bit-integrations'), 400); - } - } + protected $_defaultHeader; public function getAllOrganisations($fieldsRequestParams) { diff --git a/backend/Actions/Insightly/Routes.php b/backend/Actions/Insightly/Routes.php index eebb96fcd..28d433d48 100644 --- a/backend/Actions/Insightly/Routes.php +++ b/backend/Actions/Insightly/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Insightly\InsightlyController; use BitApps\Integrations\Core\Util\Route; -Route::post('insightly_authentication', [InsightlyController::class, 'authentication']); Route::post('insightly_fetch_all_organisations', [InsightlyController::class, 'getAllOrganisations']); Route::post('insightly_fetch_all_categories', [InsightlyController::class, 'getAllCategories']); Route::post('insightly_fetch_all_statuses', [InsightlyController::class, 'getAllStatuses']); diff --git a/backend/Actions/JetEngine/JetEngineController.php b/backend/Actions/JetEngine/JetEngineController.php index 8b2c6458a..62fe2841a 100644 --- a/backend/Actions/JetEngine/JetEngineController.php +++ b/backend/Actions/JetEngine/JetEngineController.php @@ -16,21 +16,6 @@ */ class JetEngineController { - public function authentication() - { - if (self::checkedJetEngineExists()) { - wp_send_json_success(true); - } else { - wp_send_json_error( - __( - 'Please! Install JetEngine', - 'bit-integrations' - ), - 400 - ); - } - } - public static function checkedJetEngineExists() { if (!is_plugin_active('jet-engine/jet-engine.php')) { diff --git a/backend/Actions/JetEngine/Routes.php b/backend/Actions/JetEngine/Routes.php index 4025eecaa..27c9cc005 100644 --- a/backend/Actions/JetEngine/Routes.php +++ b/backend/Actions/JetEngine/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\JetEngine\JetEngineController; use BitApps\Integrations\Core\Util\Route; -Route::post('jetEngine_authentication', [JetEngineController::class, 'authentication']); Route::post('jetEngine_menu_positions', [JetEngineController::class, 'getMenuPosition']); Route::post('jetEngine_menu_icons', [JetEngineController::class, 'getMenuIcons']); Route::post('jetEngine_supports', [JetEngineController::class, 'getSupports']); diff --git a/backend/Actions/Keap/KeapController.php b/backend/Actions/Keap/KeapController.php index 4ad8876a4..d446c8777 100644 --- a/backend/Actions/Keap/KeapController.php +++ b/backend/Actions/Keap/KeapController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Keap; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use WP_Error; @@ -15,6 +16,16 @@ */ class KeapController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'keap', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -24,8 +35,6 @@ public function __construct($integrationID) public static function refreshTagListAjaxHelper($queryParams) { - // var_dump($queryParams->tokenDetails); - // die; if ( empty($queryParams->clientId) || empty($queryParams->clientSecret) @@ -63,12 +72,11 @@ public static function refreshTagListAjaxHelper($queryParams) 'name' => $tag->name ]; } + if (!empty($response['tokenDetails']) && !empty($queryParams->id)) { + static::saveRefreshedToken($queryParams->id, $response['tokenDetails']); + } wp_send_json_success($tags, 200); } - if (!empty($response['tokenDetails']) && $response['tokenDetails'] && !empty($queryParams->id)) { - static::saveRefreshedToken($queryParams->id, $response['tokenDetails'], $response); - } - wp_send_json_success($response, 200); } public static function refreshCustomFieldAjaxHelper($queryParams) @@ -114,51 +122,6 @@ public static function refreshCustomFieldAjaxHelper($queryParams) wp_send_json_success($customFields, 200); } - /** - * Process ajax request for generate_token - * - * @param object $requestsParams Params for generate token - * - * @return JSON Keap api response and status - */ - public static function generateTokens($requestsParams) - { - if ( - empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = 'https://api.infusionsoft.com/token'; - $authorizationHeader['Content-Type'] = 'application/x-www-form-urlencoded'; - $requestParams = [ - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'code' => $requestsParams->code, - 'grant_type' => 'authorization_code', - 'redirect_uri' => $requestsParams->redirectURI - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams, $authorizationHeader); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public static function refreshAccessToken($requestsParams) { if ( diff --git a/backend/Actions/Keap/Routes.php b/backend/Actions/Keap/Routes.php index 4a8d39072..5cebd820a 100644 --- a/backend/Actions/Keap/Routes.php +++ b/backend/Actions/Keap/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Keap\KeapController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('keap_generate_token', [KeapController::class, 'generateTokens']); Route::post('keap_fetch_all_tags', [KeapController::class, 'refreshTagListAjaxHelper']); Route::post('keap_fetch_all_custom_fields', [KeapController::class, 'refreshCustomFieldAjaxHelper']); diff --git a/backend/Actions/KirimEmail/KirimEmailAuthorization.php b/backend/Actions/KirimEmail/KirimEmailAuthorization.php new file mode 100644 index 000000000..a3461fb05 --- /dev/null +++ b/backend/Actions/KirimEmail/KirimEmailAuthorization.php @@ -0,0 +1,53 @@ +getAuthDetails(); + $apiKey = $authDetails['api_key'] ?? ''; + + if ($apiKey === '') { + return [ + 'error' => true, + 'message' => __('Kirim Email api key is missing', 'bit-integrations'), + ]; + } + + return $apiKey; + } + + public function getAuthHeadersOrParams() + { + $authDetails = $this->getAuthDetails(); + $username = $authDetails['userName'] ?? ''; + $apiKey = $authDetails['api_key'] ?? ''; + + if ($username === '' || $apiKey === '') { + return [ + 'error' => true, + 'message' => __('Kirim Email username or api key is missing', 'bit-integrations'), + ]; + } + + $time = time(); + $generatedToken = hash_hmac('sha256', "{$username}" . '::' . "{$apiKey}" . '::' . $time, "{$apiKey}"); + + return [ + 'authLocation' => 'header', + 'data' => [ + 'Auth-Id' => $username, + 'Auth-Token' => $generatedToken, + 'Timestamp' => $time, + ], + ]; + } +} diff --git a/backend/Actions/KirimEmail/KirimEmailController.php b/backend/Actions/KirimEmail/KirimEmailController.php index 9a8463bfa..a5d3dd94c 100644 --- a/backend/Actions/KirimEmail/KirimEmailController.php +++ b/backend/Actions/KirimEmail/KirimEmailController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\KirimEmail; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,52 +15,23 @@ */ class KirimEmailController { - // $time = time(); - // $generated_token = hash_hmac("sha256","YOUR USERNAME"."::"."YOUR API TOKEN"."::".$time,"YOUR API TOKEN") - - public function checkAuthorization($tokenRequestParams) - { - if ( - empty($tokenRequestParams->username) - || empty($tokenRequestParams->api_key) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $userName = $tokenRequestParams->username; - $apiKey = $tokenRequestParams->api_key; - $time = time(); - $generated_token = hash_hmac('sha256', "{$userName}" . '::' . "{$apiKey}" . '::' . $time, "{$apiKey}"); - $header = [ - 'Auth-Id' => $userName, - 'Auth-Token' => $generated_token, - 'Timestamp' => $time, - ]; - - $apiEndpoint = 'https://api.kirim.email/v3/list'; - - $apiResponse = HttpHelper::get($apiEndpoint, null, $header); - - if (is_wp_error($apiResponse) || $apiResponse->code !== 200) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - - wp_send_json_success($apiResponse->data, 200); - } + public static array $authConfig = [ + 'authType' => AuthorizationType::CUSTOM, + 'slug' => 'KirimEmail', + 'fields' => [ + 'api_key' => 'api_key', + 'userName' => 'userName', + ], + ]; public function getAllList($tokenRequestParams) { + $userName = $tokenRequestParams->userName ?? $tokenRequestParams->username ?? ''; + $apiKey = $tokenRequestParams->api_key ?? ''; + if ( - empty($tokenRequestParams->username) - || empty($tokenRequestParams->api_key) + empty($userName) + || empty($apiKey) ) { wp_send_json_error( __( @@ -69,17 +41,9 @@ public function getAllList($tokenRequestParams) 400 ); } - $userName = $tokenRequestParams->username; - $apiKey = $tokenRequestParams->api_key; - $time = time(); - $generated_token = hash_hmac('sha256', "{$userName}" . '::' . "{$apiKey}" . '::' . $time, "{$apiKey}"); - $header = [ - 'Auth-Id' => $userName, - 'Auth-Token' => $generated_token, - 'Timestamp' => $time, - ]; $apiEndpoint = 'https://api.kirim.email/v3/list'; + $header = self::buildAuthHeaders($userName, $apiKey); $apiResponse = HttpHelper::get($apiEndpoint, null, $header); @@ -98,7 +62,7 @@ public function execute($integrationData, $fieldValues) $integrationDetails = $integrationData->flow_details; $integrationId = $integrationData->id; $api_key = $integrationDetails->api_key; - $userName = $integrationDetails->userName; + $userName = $integrationDetails->userName ?? $integrationDetails->username ?? ''; $fieldMap = $integrationDetails->field_map; $mainAction = $integrationDetails->mainAction; @@ -110,7 +74,7 @@ public function execute($integrationData, $fieldValues) ) { // translators: %s: Placeholder value - return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Freshdesk')); + return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'Kirim Email')); } $recordApiHelper = new RecordApiHelper($integrationId); $kirinEmailApiResponse = $recordApiHelper->execute( @@ -128,4 +92,16 @@ public function execute($integrationData, $fieldValues) return $kirinEmailApiResponse; } + + private static function buildAuthHeaders(string $userName, string $apiKey): array + { + $time = time(); + $generatedToken = hash_hmac('sha256', "{$userName}" . '::' . "{$apiKey}" . '::' . $time, "{$apiKey}"); + + return [ + 'Auth-Id' => $userName, + 'Auth-Token' => $generatedToken, + 'Timestamp' => $time, + ]; + } } diff --git a/backend/Actions/KirimEmail/Routes.php b/backend/Actions/KirimEmail/Routes.php index 541d3b3db..0b3b01ee3 100644 --- a/backend/Actions/KirimEmail/Routes.php +++ b/backend/Actions/KirimEmail/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\KirimEmail\KirimEmailController; use BitApps\Integrations\Core\Util\Route; -Route::post('kirimEmail_authorization', [KirimEmailController::class, 'checkAuthorization']); Route::post('kirimEmail_fetch_all_list', [KirimEmailController::class, 'getAllList']); diff --git a/backend/Actions/Klaviyo/KlaviyoController.php b/backend/Actions/Klaviyo/KlaviyoController.php index ec459743e..8c9a242c9 100644 --- a/backend/Actions/Klaviyo/KlaviyoController.php +++ b/backend/Actions/Klaviyo/KlaviyoController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Klaviyo; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,9 +15,17 @@ */ class KlaviyoController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'klaviyo', + 'fields' => [ + 'authKey' => 'value', + ], + ]; + private $baseUrl = 'https://a.klaviyo.com/api/'; - public function handleAuthorize($requestParams) + public function getAllLists($requestParams) { if (empty($requestParams->authKey)) { wp_send_json_error( diff --git a/backend/Actions/Klaviyo/Routes.php b/backend/Actions/Klaviyo/Routes.php index 33c812ece..cfe92d108 100644 --- a/backend/Actions/Klaviyo/Routes.php +++ b/backend/Actions/Klaviyo/Routes.php @@ -7,4 +7,4 @@ use BitApps\Integrations\Actions\Klaviyo\KlaviyoController; use BitApps\Integrations\Core\Util\Route; -Route::post('klaviyo_handle_authorize', [klaviyoController::class, 'handleAuthorize']); +Route::post('klaviyo_lists', [KlaviyoController::class, 'getAllLists']); diff --git a/backend/Actions/LMFWC/LMFWCController.php b/backend/Actions/LMFWC/LMFWCController.php index 34eb266a1..8fb9d7ab2 100644 --- a/backend/Actions/LMFWC/LMFWCController.php +++ b/backend/Actions/LMFWC/LMFWCController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\LMFWC; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,25 +15,17 @@ */ class LMFWCController { - protected $_defaultHeader; - - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->setHeaders($fieldsRequestParams->api_key, $fieldsRequestParams->api_secret); - - $apiEndpoint = $fieldsRequestParams->base_url . '/wp-json/lmfwc/v2/licenses'; - $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader, ['sslverify' => false]); + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'lmfwc', + 'fields' => [ + 'api_key' => 'value', + 'api_secret' => 'api_secret', + 'base_url' => 'base_url', + ], + ]; - if (is_wp_error($response)) { - wp_send_json_error($response->get_error_message(), HttpHelper::$responseCode); - } - if ((isset($response->code) && $response->code === 'lmfwc_rest_data_error') || (isset($response->success) && $response->success)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } - - wp_send_json_error(!empty($response->message) ? $response->message : __('Please enter valid Consumer key & Consumer secret', 'bit-integrations'), 400); - } + protected $_defaultHeader; public function getAllCustomer($fieldsRequestParams) { diff --git a/backend/Actions/LMFWC/Routes.php b/backend/Actions/LMFWC/Routes.php index d109e350c..640defc4e 100644 --- a/backend/Actions/LMFWC/Routes.php +++ b/backend/Actions/LMFWC/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\LMFWC\LMFWCController; use BitApps\Integrations\Core\Util\Route; -Route::post('lmfwc_authentication', [LMFWCController::class, 'authentication']); Route::post('lmfwc_fetch_all_customer', [LMFWCController::class, 'getAllCustomer']); Route::post('lmfwc_fetch_all_product', [LMFWCController::class, 'getAllProduct']); Route::post('lmfwc_fetch_all_order', [LMFWCController::class, 'getAllOrder']); diff --git a/backend/Actions/LearnDash/LearnDashController.php b/backend/Actions/LearnDash/LearnDashController.php index 205ab5a25..59eef90bd 100644 --- a/backend/Actions/LearnDash/LearnDashController.php +++ b/backend/Actions/LearnDash/LearnDashController.php @@ -34,17 +34,6 @@ public static function pluginActive($option = null) return false; } - public static function authorizeRestrictContent() - { - include_once ABSPATH . 'wp-admin/includes/plugin.php'; - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - // translators: %s: Placeholder value - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'LearnDash')); - } - public static function getCourses() { $courses = []; diff --git a/backend/Actions/LearnDash/Routes.php b/backend/Actions/LearnDash/Routes.php index 6bdc7fc26..6e1cfad55 100644 --- a/backend/Actions/LearnDash/Routes.php +++ b/backend/Actions/LearnDash/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\LearnDash\LearnDashController; use BitApps\Integrations\Core\Util\Route; -Route::post('learnDash_authorize', [LearnDashController::class, 'authorizeRestrictContent']); Route::post('learDash_fetch_all_course', [LearnDashController::class, 'getCourses']); Route::post('learDash_fetch_all_group', [LearnDashController::class, 'learDashFetchAllGroup']); Route::post('learDash_fetch_all_course_of_lesson', [LearnDashController::class, 'learDashFetchAllCourseOfLesson']); diff --git a/backend/Actions/Lemlist/LemlistController.php b/backend/Actions/Lemlist/LemlistController.php index 107c7ef59..66b0bca1e 100644 --- a/backend/Actions/Lemlist/LemlistController.php +++ b/backend/Actions/Lemlist/LemlistController.php @@ -2,11 +2,20 @@ namespace BitApps\Integrations\Actions\Lemlist; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; class LemlistController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'lemlist', + 'fields' => [ + 'api_key' => 'value', + ], + ]; + private $integrationID; public function __construct($integrationID) @@ -14,25 +23,6 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function authorization($requestParams) - { - if (empty($requestParams->api_key)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiEndpoint = 'https://api.lemlist.com/api/team'; - $header['Authorization'] = 'Basic ' . base64_encode(":{$requestParams->api_key}"); - $response = HttpHelper::get($apiEndpoint, null, $header); - - if (!isset($response->_id)) { - wp_send_json_error( - empty($response) ? 'Unknown' : $response, - 400 - ); - } - wp_send_json_success(true); - } - public static function getAllCampaign($requestParams) { if (empty($requestParams->api_key)) { diff --git a/backend/Actions/Lemlist/Routes.php b/backend/Actions/Lemlist/Routes.php index 2ac0857b1..410e9da75 100644 --- a/backend/Actions/Lemlist/Routes.php +++ b/backend/Actions/Lemlist/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\Lemlist\LemlistController; use BitApps\Integrations\Core\Util\Route; -Route::post('lemlist_authorize', [LemlistController::class, 'authorization']); Route::post('lemlist_campaigns', [LemlistController::class, 'getAllCampaign']); diff --git a/backend/Actions/LifterLms/LifterLmsController.php b/backend/Actions/LifterLms/LifterLmsController.php index 9c01db46d..8f0c05d2e 100644 --- a/backend/Actions/LifterLms/LifterLmsController.php +++ b/backend/Actions/LifterLms/LifterLmsController.php @@ -17,15 +17,6 @@ public static function pluginActive($option = null) return false; } - public static function authorizeLifterLms() - { - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'LifterLms')); - } - public static function getAllLesson() { $lessonParams = [ diff --git a/backend/Actions/LifterLms/Routes.php b/backend/Actions/LifterLms/Routes.php index 7e7ef7eb6..c973451b1 100644 --- a/backend/Actions/LifterLms/Routes.php +++ b/backend/Actions/LifterLms/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\LifterLms\LifterLmsController; use BitApps\Integrations\Core\Util\Route; -Route::post('lifterLms_authorize', [LifterLmsController::class, 'authorizeLifterLms']); Route::post('lifterLms_fetch_all_lesson', [LifterLmsController::class, 'getAllLesson']); Route::post('lifterLms_fetch_all_section', [LifterLmsController::class, 'getAllSection']); Route::post('lifterLms_fetch_all_course', [LifterLmsController::class, 'getAllLifterLmsCourse']); diff --git a/backend/Actions/Line/LineController.php b/backend/Actions/Line/LineController.php index a2b03359f..8746011a9 100644 --- a/backend/Actions/Line/LineController.php +++ b/backend/Actions/Line/LineController.php @@ -2,28 +2,19 @@ namespace BitApps\Integrations\Actions\Line; -use BitApps\Integrations\Core\Util\HttpHelper; +use BitApps\Integrations\Authorization\AuthorizationType; class LineController { public const APIENDPOINT = 'https://api.line.me/v2/bot'; - public static function authorization($tokenRequestParams) - { - if (empty($tokenRequestParams->accessToken)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $header = ['Authorization' => 'Bearer ' . $tokenRequestParams->accessToken]; - $apiEndpoint = self::APIENDPOINT . '/info'; - $apiResponse = HttpHelper::get($apiEndpoint, null, $header); - - if (is_wp_error($apiResponse) || empty($apiResponse->userId)) { - wp_send_json_error(empty($apiResponse->message) ? 'Unknown' : $apiResponse->message, 400); - } - - wp_send_json_success($apiResponse, 200); - } + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'line', + 'fields' => [ + 'accessToken' => 'token', + ], + ]; public function execute($integrationData, $fieldValues) { diff --git a/backend/Actions/Line/Routes.php b/backend/Actions/Line/Routes.php index b4beb193d..5c539fc24 100644 --- a/backend/Actions/Line/Routes.php +++ b/backend/Actions/Line/Routes.php @@ -3,8 +3,3 @@ if (!defined('ABSPATH')) { exit; } - -use BitApps\Integrations\Actions\Line\LineController; -use BitApps\Integrations\Core\Util\Route; - -Route::post('line_authorization', [LineController::class, 'authorization']); diff --git a/backend/Actions/LionDesk/LionDeskController.php b/backend/Actions/LionDesk/LionDeskController.php index 3a43eddad..1e27f3767 100644 --- a/backend/Actions/LionDesk/LionDeskController.php +++ b/backend/Actions/LionDesk/LionDeskController.php @@ -6,7 +6,9 @@ namespace BitApps\Integrations\Actions\LionDesk; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; +use BitApps\Integrations\Flow\FlowController; use WP_Error; /** @@ -14,7 +16,15 @@ */ class LionDeskController { - protected $_defaultHeader; + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'liondesk', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires', 'expires_in', 'generated_at']], + ], + ]; protected $apiEndpoint; @@ -23,125 +33,145 @@ public function __construct() $this->apiEndpoint = 'https://api-v2.liondesk.com/'; } - /** - * Process ajax request for generate_token - * - * @param $requestsParams Mandatory params for generate tokens - * - * @return JSON LionDesk api response and status - */ - public function generateTokens($requestsParams) + public function getCustomFields($fieldsRequestParams) { - if ( - empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $apiEndpoint = $this->apiEndpoint . '/oauth2/token'; - $requestParams = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => urldecode($requestsParams->redirectURI), - 'code' => $requestsParams->code - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); + $tokenDetails = self::normalizeConnectionToken($fieldsRequestParams->tokenDetails ?? $fieldsRequestParams->token_details ?? null); + $isConnectionAuth = !empty($fieldsRequestParams->connection_id); + $oldToken = $tokenDetails->access_token ?? ''; + + if (empty($oldToken)) { + wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public function getCustomFields($fieldsRequestParams) - { - $response = []; - if (strtotime($fieldsRequestParams->token_details->expires) < time()) { - $response['tokenDetails'] = $this->_refreshAccessToken($fieldsRequestParams); - $fieldsRequestParams->token_details = $response['tokenDetails']; + if (!$isConnectionAuth && self::isTokenExpired($tokenDetails)) { + $refreshedToken = self::_refreshAccessToken((object) [ + 'clientId' => $fieldsRequestParams->clientId ?? $fieldsRequestParams->client_id ?? '', + 'clientSecret' => $fieldsRequestParams->clientSecret ?? $fieldsRequestParams->client_secret ?? '', + 'redirectURI' => $fieldsRequestParams->redirectURI ?? $fieldsRequestParams->redirect_uri ?? '', + 'tokenDetails' => $tokenDetails, + ]); + + if (!$refreshedToken) { + wp_send_json_error(__('Failed to refresh access token', 'bit-integrations'), 400); + } + $tokenDetails = $refreshedToken; } - $this->checkValidation($fieldsRequestParams); - $access_token = $fieldsRequestParams->token_details->access_token; $apiEndpoint = $this->apiEndpoint . '/custom-fields'; - $headers = $this->setHeaders($access_token); - $response = HttpHelper::get($apiEndpoint, null, $headers); - if (isset($response)) { - if (isset($response->data)) { - foreach ($response->data as $customField) { - $customFields[] = [ - 'key' => $customField->id, - 'label' => $customField->name, - ]; - } - wp_send_json_success($customFields, 200); - } else { - wp_send_json_error($response->message, 400); + $headers = $this->setHeaders($tokenDetails->access_token); + $apiResponse = HttpHelper::get($apiEndpoint, null, $headers); + + if (!isset($apiResponse) || isset($apiResponse->message) && !isset($apiResponse->data)) { + wp_send_json_error($apiResponse->message ?? __('Custom field fetching failed', 'bit-integrations'), 400); + } + + $customFields = []; + if (!empty($apiResponse->data)) { + foreach ($apiResponse->data as $customField) { + $customFields[] = [ + 'key' => $customField->id, + 'label' => $customField->name, + ]; } - } else { - wp_send_json_error(__('Custom field fetching failed', 'bit-integrations'), 400); } + + if (!$isConnectionAuth && $oldToken !== ($tokenDetails->access_token ?? '') && !empty($fieldsRequestParams->id)) { + self::saveRefreshedToken($fieldsRequestParams->id, $tokenDetails); + } + + wp_send_json_success( + [ + 'customFields' => $customFields, + 'tokenDetails' => $tokenDetails, + ], + 200 + ); } public function getAllTags($fieldsRequestParams) { - $response = []; - if (strtotime($fieldsRequestParams->token_details->expires) < time()) { - $response['tokenDetails'] = $this->_refreshAccessToken($fieldsRequestParams); - $fieldsRequestParams->token_details = $response['tokenDetails']; + $tokenDetails = self::normalizeConnectionToken($fieldsRequestParams->tokenDetails ?? $fieldsRequestParams->token_details ?? null); + $isConnectionAuth = !empty($fieldsRequestParams->connection_id); + $oldToken = $tokenDetails->access_token ?? ''; + + if (empty($oldToken)) { + wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); + } + + if (!$isConnectionAuth && self::isTokenExpired($tokenDetails)) { + $refreshedToken = self::_refreshAccessToken((object) [ + 'clientId' => $fieldsRequestParams->clientId ?? $fieldsRequestParams->client_id ?? '', + 'clientSecret' => $fieldsRequestParams->clientSecret ?? $fieldsRequestParams->client_secret ?? '', + 'redirectURI' => $fieldsRequestParams->redirectURI ?? $fieldsRequestParams->redirect_uri ?? '', + 'tokenDetails' => $tokenDetails, + ]); + + if (!$refreshedToken) { + wp_send_json_error(__('Failed to refresh access token', 'bit-integrations'), 400); + } + $tokenDetails = $refreshedToken; } - $this->checkValidation($fieldsRequestParams); - $access_token = $fieldsRequestParams->token_details->access_token; $apiEndpoint = $this->apiEndpoint . '/tags'; - $headers = $this->setHeaders($access_token); - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (isset($response)) { - if (isset($response->data)) { - foreach ($response->data as $tag) { - $tags[] = [ - 'tag' => $tag->content - ]; - } - wp_send_json_success($tags, 200); - } else { - wp_send_json_error($response->message, 400); + $headers = $this->setHeaders($tokenDetails->access_token); + $apiResponse = HttpHelper::get($apiEndpoint, null, $headers); + + if (!isset($apiResponse) || isset($apiResponse->message) && !isset($apiResponse->data)) { + wp_send_json_error($apiResponse->message ?? __('Tags fetching failed', 'bit-integrations'), 400); + } + + $tags = []; + if (!empty($apiResponse->data)) { + foreach ($apiResponse->data as $tag) { + $tags[] = [ + 'tag' => $tag->content + ]; } - } else { - wp_send_json_error(__('Tags fetching failed', 'bit-integrations'), 400); } + + if (!$isConnectionAuth && $oldToken !== ($tokenDetails->access_token ?? '') && !empty($fieldsRequestParams->id)) { + self::saveRefreshedToken($fieldsRequestParams->id, $tokenDetails); + } + + wp_send_json_success( + [ + 'tags' => $tags, + 'tokenDetails' => $tokenDetails, + ], + 200 + ); } public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; - $tokenDetails = $integrationDetails->tokenDetails; + $tokenDetails = self::normalizeConnectionToken($integrationDetails->tokenDetails ?? null); $fieldMap = $integrationDetails->field_map; $actionName = $integrationDetails->actionName; + $isConnectionAuth = !empty($integrationDetails->connection_id); + $oldToken = $tokenDetails->access_token ?? ''; - if (empty($fieldMap) || empty($tokenDetails) || empty($actionName)) { + if (empty($fieldMap) || empty($oldToken) || empty($actionName)) { // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'LionDesk')); } - $response = []; - if (strtotime($tokenDetails->expires) < time()) { - $response['tokenDetails'] = $this->_refreshAccessToken($tokenDetails); - $tokenDetails = $response['tokenDetails']; + if (!$isConnectionAuth && self::isTokenExpired($tokenDetails)) { + $tokenDetails = self::_refreshAccessToken((object) [ + 'clientId' => $integrationDetails->clientId ?? '', + 'clientSecret' => $integrationDetails->clientSecret ?? '', + 'redirectURI' => $integrationDetails->redirectURI ?? '', + 'tokenDetails' => $tokenDetails, + ]); + + if (!$tokenDetails || empty($tokenDetails->access_token)) { + return new WP_Error('AUTH_FAILED', __('Failed to refresh access token', 'bit-integrations')); + } + } + + if (!$isConnectionAuth && $oldToken !== ($tokenDetails->access_token ?? '')) { + self::saveRefreshedToken($integId, $tokenDetails); } $recordApiHelper = new RecordApiHelper($integrationDetails, $integId, $tokenDetails); @@ -159,46 +189,95 @@ public function execute($integrationData, $fieldValues) * * @param object $apiData Contains required data for refresh access token * - * @return JSON $tokenDetails API token details + * @return object|false $tokenDetails API token details */ - protected function _refreshAccessToken($apiData) + protected static function _refreshAccessToken($apiData) { - if ( - empty($apiData->client_id) - || empty($apiData->client_secret) - || empty($apiData->token_details) - || empty($apiData->redirect_uri) + if (empty($apiData->clientId) + || empty($apiData->clientSecret) + || empty($apiData->tokenDetails) + || empty($apiData->tokenDetails->refresh_token) ) { return false; } - $apiEndpoint = $this->apiEndpoint . '/oauth2/token'; + $apiEndpoint = 'https://api-v2.liondesk.com/oauth2/token'; $requestParams = [ - 'refresh_token' => $apiData->token_details->refresh_token, - 'client_id' => $apiData->client_id, - 'client_secret' => $apiData->client_secret, - 'redirect_uri' => $apiData->redirect_uri, + 'refresh_token' => $apiData->tokenDetails->refresh_token, + 'client_id' => $apiData->clientId, + 'client_secret' => $apiData->clientSecret, 'grant_type' => 'refresh_token', ]; + if (!empty($apiData->redirectURI)) { + $requestParams['redirect_uri'] = $apiData->redirectURI; + } + $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); + return false; } - $apiResponse->generates_on = time(); + $tokenDetails = $apiData->tokenDetails; + $tokenDetails->access_token = $apiResponse->access_token ?? $tokenDetails->access_token; + $tokenDetails->token_type = $apiResponse->token_type ?? ($tokenDetails->token_type ?? 'Bearer'); + $tokenDetails->expires_in = $apiResponse->expires_in ?? ($tokenDetails->expires_in ?? 0); + $tokenDetails->expires = $apiResponse->expires ?? ($tokenDetails->expires ?? ''); + $tokenDetails->refresh_token = $apiResponse->refresh_token ?? $tokenDetails->refresh_token; + $tokenDetails->generates_on = time(); + $tokenDetails->generated_at = $tokenDetails->generates_on; - return $apiResponse; + return $tokenDetails; } - private function checkValidation($fieldsRequestParams, $customParam = '**') + private static function isTokenExpired($tokenDetails) { - if (empty($fieldsRequestParams->token_details->access_token) || empty($customParam)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); + if (empty($tokenDetails)) { + return true; + } + + if (!empty($tokenDetails->expires)) { + $expiresOn = strtotime($tokenDetails->expires); + if ($expiresOn && $expiresOn < time()) { + return true; + } } + + $generatedOn = (int) ($tokenDetails->generates_on ?? $tokenDetails->generated_at ?? 0); + $expiresIn = (int) ($tokenDetails->expires_in ?? 0); + + return $generatedOn > 0 && $expiresIn > 0 && ($generatedOn + ($expiresIn - 300)) < time(); + } + + private static function normalizeConnectionToken($token) + { + if (!\is_object($token)) { + $token = (object) []; + } + + if (empty($token->generates_on) && !empty($token->generated_at)) { + $token->generates_on = (int) $token->generated_at; + } + + return $token; + } + + private static function saveRefreshedToken($integrationID, $tokenDetails) + { + if (empty($integrationID)) { + return; + } + + $flow = new FlowController(); + $lionDeskDetails = $flow->get(['id' => $integrationID]); + + if (is_wp_error($lionDeskDetails)) { + return; + } + + $newDetails = json_decode($lionDeskDetails[0]->flow_details); + $newDetails->tokenDetails = $tokenDetails; + $flow->update($integrationID, ['flow_details' => wp_json_encode($newDetails)]); } private function setHeaders($access_token) diff --git a/backend/Actions/LionDesk/Routes.php b/backend/Actions/LionDesk/Routes.php index 6244d9829..6d974534b 100644 --- a/backend/Actions/LionDesk/Routes.php +++ b/backend/Actions/LionDesk/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\LionDesk\LionDeskController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('lionDesk_generate_token', [LionDeskController::class, 'generateTokens']); Route::post('lionDesk_fetch_custom_fields', [LionDeskController::class, 'getCustomFields']); Route::post('lionDesk_fetch_all_tags', [LionDeskController::class, 'getAllTags']); diff --git a/backend/Actions/Livestorm/LivestormController.php b/backend/Actions/Livestorm/LivestormController.php index c65f4d389..61dc1b211 100644 --- a/backend/Actions/Livestorm/LivestormController.php +++ b/backend/Actions/Livestorm/LivestormController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Livestorm; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,14 @@ */ class LivestormController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'livestorm', + 'fields' => [ + 'api_key' => 'value', + ], + ]; + protected $_defaultHeader; protected $_apiEndpoint; @@ -23,22 +32,6 @@ public function __construct() $this->_apiEndpoint = 'https://api.livestorm.co/v1'; } - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->setHeaders($fieldsRequestParams->api_key); - $apiEndpoint = $this->_apiEndpoint . '/ping'; - $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); - - if (!\count((array) $response)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } elseif (isset($response->errors) && $response->errors[0]->title === 'Workspace blocked') { - wp_send_json_error($response->errors[0]->detail, 400); - } else { - wp_send_json_error(__('Authorized failed, Please enter valid API Key', 'bit-integrations'), 400); - } - } - public function getAllEvents($fieldsRequestParams) { $this->checkValidation($fieldsRequestParams); diff --git a/backend/Actions/Livestorm/Routes.php b/backend/Actions/Livestorm/Routes.php index 6e1e90a4e..d6e06912e 100644 --- a/backend/Actions/Livestorm/Routes.php +++ b/backend/Actions/Livestorm/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Livestorm\LivestormController; use BitApps\Integrations\Core\Util\Route; -Route::post('livestorm_authentication', [LivestormController::class, 'authentication']); Route::post('livestorm_fetch_all_events', [LivestormController::class, 'getAllEvents']); Route::post('livestorm_fetch_all_sessions', [LivestormController::class, 'getAllSessions']); diff --git a/backend/Actions/MailBluster/MailBlusterController.php b/backend/Actions/MailBluster/MailBlusterController.php index a17d13e91..654dc24af 100644 --- a/backend/Actions/MailBluster/MailBlusterController.php +++ b/backend/Actions/MailBluster/MailBlusterController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\MailBluster; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,11 +15,19 @@ */ class MailBlusterController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'mailbluster', + 'fields' => [ + 'auth_token' => 'value', + ], + ]; + protected $_defaultHeader; private $baseUrl = 'https://api.mailbluster.com/api/'; - public function authentication($fieldsRequestParams) + public function fetchCustomFields($fieldsRequestParams) { if (empty($fieldsRequestParams->auth_token)) { wp_send_json_error( diff --git a/backend/Actions/MailBluster/Routes.php b/backend/Actions/MailBluster/Routes.php index 9a5b84170..fb07b8645 100644 --- a/backend/Actions/MailBluster/Routes.php +++ b/backend/Actions/MailBluster/Routes.php @@ -7,4 +7,4 @@ use BitApps\Integrations\Actions\MailBluster\MailBlusterController; use BitApps\Integrations\Core\Util\Route; -Route::post('mailBluster_authentication', [MailBlusterController::class, 'authentication']); +Route::post('mailBluster_fetch_custom_fields', [MailBlusterController::class, 'fetchCustomFields']); diff --git a/backend/Actions/MailChimp/MailChimpController.php b/backend/Actions/MailChimp/MailChimpController.php index 467d0a4fc..a3cf67b17 100644 --- a/backend/Actions/MailChimp/MailChimpController.php +++ b/backend/Actions/MailChimp/MailChimpController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\MailChimp; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\Helper; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -15,6 +16,16 @@ */ class MailChimpController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'mailchimp', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on', 'dc']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -65,58 +76,6 @@ public static function refreshModules() return $allModules; } - /** - * Process ajax request for generate_token - * - * @param object $requestsParams Params for generate token - * - * @return JSON zoho crm api response and status - */ - public static function generateTokens($requestsParams) - { - if ( - empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = 'https://login.mailchimp.com/oauth2/token'; - $authorizationHeader['Content-Type'] = 'application/x-www-form-urlencoded'; - $requestParams = [ - 'code' => $requestsParams->code, - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => $requestsParams->redirectURI, - 'grant_type' => 'authorization_code' - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams, $authorizationHeader); - - $metaDataEndPoint = 'https://login.mailchimp.com/oauth2/metadata'; - - $authorizationHeader['Authorization'] = "Bearer {$apiResponse->access_token}"; - $metaData = HttpHelper::post($metaDataEndPoint, null, $authorizationHeader); - - $apiResponse->dc = $metaData->dc; - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - /** * Process ajax request for refresh MailChimp Audience list * @@ -126,12 +85,7 @@ public static function generateTokens($requestsParams) */ public static function refreshAudience($queryParams) { - if ( - empty($queryParams->tokenDetails) - || empty($queryParams->clientId) - || empty($queryParams->clientSecret) - || empty($queryParams->tokenDetails->dc) - ) { + if (empty($queryParams->tokenDetails)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -140,10 +94,17 @@ public static function refreshAudience($queryParams) 400 ); } + + $tokenDetails = self::resolveTokenDetails($queryParams->tokenDetails); + + if (empty($tokenDetails->dc) || empty($tokenDetails->access_token)) { + wp_send_json_error(__('Authorization info is missing. please authorize again', 'bit-integrations'), 400); + } + $response = []; - $apiEndpoint = self::apiEndPoint($queryParams->tokenDetails->dc) . '/lists?count=1000&offset=0'; + $apiEndpoint = self::apiEndPoint($tokenDetails->dc) . '/lists?count=1000&offset=0'; - $authorizationHeader['Authorization'] = "Bearer {$queryParams->tokenDetails->access_token}"; + $authorizationHeader['Authorization'] = "Bearer {$tokenDetails->access_token}"; $audienceResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); $allList = []; @@ -159,6 +120,7 @@ public static function refreshAudience($queryParams) uksort($allList, 'strnatcasecmp'); $response['audiencelist'] = $allList; + $response['tokenDetails'] = $tokenDetails; } else { wp_send_json_error( $audienceResponse->response->error->message, @@ -180,7 +142,6 @@ public static function refreshAudienceFields($queryParams) if ( empty($queryParams->tokenDetails) || empty($queryParams->listId) - || empty($queryParams->tokenDetails->dc) ) { wp_send_json_error( __( @@ -191,6 +152,12 @@ public static function refreshAudienceFields($queryParams) ); } + $tokenDetails = self::resolveTokenDetails($queryParams->tokenDetails); + + if (empty($tokenDetails->dc) || empty($tokenDetails->access_token)) { + wp_send_json_error(__('Authorization info is missing. please authorize again', 'bit-integrations'), 400); + } + if (isset($queryParams->module) && ($queryParams->module == 'add_tag_to_a_member' || $queryParams->module == 'remove_tag_from_a_member')) { $fields['Email'] = (object) ['tag' => 'email_address', 'name' => 'Email', 'required' => true]; $response['audienceField'] = $fields; @@ -199,8 +166,8 @@ public static function refreshAudienceFields($queryParams) return; } - $apiEndpoint = self::apiEndPoint($queryParams->tokenDetails->dc) . "/lists/{$queryParams->listId}/merge-fields?count=1000&offset=0"; - $authorizationHeader['Authorization'] = "Bearer {$queryParams->tokenDetails->access_token}"; + $apiEndpoint = self::apiEndPoint($tokenDetails->dc) . "/lists/{$queryParams->listId}/merge-fields?count=1000&offset=0"; + $authorizationHeader['Authorization'] = "Bearer {$tokenDetails->access_token}"; $mergeFieldResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); $fields = []; @@ -218,6 +185,7 @@ public static function refreshAudienceFields($queryParams) } $fields['Email'] = (object) ['tag' => 'email_address', 'name' => 'Email', 'required' => true]; $response['audienceField'] = $fields; + $response['tokenDetails'] = $tokenDetails; wp_send_json_success($response); } } @@ -234,7 +202,6 @@ public static function refreshTags($queryParams) if ( empty($queryParams->tokenDetails) || empty($queryParams->listId) - || empty($queryParams->tokenDetails->dc) ) { wp_send_json_error( __( @@ -244,8 +211,15 @@ public static function refreshTags($queryParams) 400 ); } - $apiEndpoint = self::apiEndPoint($queryParams->tokenDetails->dc) . "/lists/{$queryParams->listId}/segments?count=1000&offset=0"; - $authorizationHeader['Authorization'] = "Bearer {$queryParams->tokenDetails->access_token}"; + + $tokenDetails = self::resolveTokenDetails($queryParams->tokenDetails); + + if (empty($tokenDetails->dc) || empty($tokenDetails->access_token)) { + wp_send_json_error(__('Authorization info is missing. please authorize again', 'bit-integrations'), 400); + } + + $apiEndpoint = self::apiEndPoint($tokenDetails->dc) . "/lists/{$queryParams->listId}/segments?count=1000&offset=0"; + $authorizationHeader['Authorization'] = "Bearer {$tokenDetails->access_token}"; $tagsList = HttpHelper::get($apiEndpoint, null, $authorizationHeader); $allList = []; @@ -257,6 +231,7 @@ public static function refreshTags($queryParams) } uksort($allList, 'strnatcasecmp'); $response['audienceTags'] = $allList; + $response['tokenDetails'] = $tokenDetails; wp_send_json_success($response); } @@ -272,7 +247,7 @@ public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; - $tokenDetails = $integrationDetails->tokenDetails; + $tokenDetails = self::resolveTokenDetails($integrationDetails->tokenDetails); $listId = $integrationDetails->listId; $module = isset($integrationDetails->module) ? $integrationDetails->module : ''; $tags = $integrationDetails->tags; @@ -283,6 +258,8 @@ public function execute($integrationData, $fieldValues) if ( empty($tokenDetails) + || empty($tokenDetails->access_token) + || empty($tokenDetails->dc) || empty($listId) || empty($fieldMap) || empty($defaultDataConf) @@ -308,4 +285,23 @@ public function execute($integrationData, $fieldValues) return $mChimpApiResponse; } + + private static function resolveTokenDetails($tokenDetails) + { + if (empty($tokenDetails) || !\is_object($tokenDetails) || empty($tokenDetails->access_token) || !empty($tokenDetails->dc)) { + return $tokenDetails; + } + + $metadataResponse = HttpHelper::get( + 'https://login.mailchimp.com/oauth2/metadata', + null, + ['Authorization' => "Bearer {$tokenDetails->access_token}"] + ); + + if (!is_wp_error($metadataResponse) && empty($metadataResponse->error) && !empty($metadataResponse->dc)) { + $tokenDetails->dc = $metadataResponse->dc; + } + + return $tokenDetails; + } } diff --git a/backend/Actions/MailChimp/Routes.php b/backend/Actions/MailChimp/Routes.php index e1f9eb5e3..0ede1216b 100644 --- a/backend/Actions/MailChimp/Routes.php +++ b/backend/Actions/MailChimp/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\MailChimp\MailChimpController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('mChimp_generate_token', [MailChimpController::class, 'generateTokens']); Route::post('mChimp_refresh_audience', [MailChimpController::class, 'refreshAudience']); Route::post('mChimp_refresh_fields', [MailChimpController::class, 'refreshAudienceFields']); Route::post('mChimp_refresh_tags', [MailChimpController::class, 'refreshTags']); diff --git a/backend/Actions/MailMint/MailMintController.php b/backend/Actions/MailMint/MailMintController.php index 6b42446e8..d3b4c2a11 100644 --- a/backend/Actions/MailMint/MailMintController.php +++ b/backend/Actions/MailMint/MailMintController.php @@ -17,16 +17,6 @@ public static function pluginActive() ; } - public static function authorizeMailMint() - { - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - // translators: %s: Placeholder value - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'Mail Mint')); - } - public static function allCustomFields() { if (class_exists('Mint\MRM\DataBase\Models\ContactGroupModel')) { diff --git a/backend/Actions/MailMint/Routes.php b/backend/Actions/MailMint/Routes.php index ca2c9bdba..fe4eb915a 100644 --- a/backend/Actions/MailMint/Routes.php +++ b/backend/Actions/MailMint/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\MailMint\MailMintController; use BitApps\Integrations\Core\Util\Route; -Route::post('mailmint_authorize', [MailMintController::class, 'authorizeMailMint']); Route::post('fetch_all_mail_mint_list', [MailMintController::class, 'getAllList']); Route::post('fetch_all_mail_mint_tags', [MailMintController::class, 'getAllTags']); Route::post('fetch_all_mail_mint_custom_fields', [MailMintController::class, 'allCustomFields']); diff --git a/backend/Actions/MailPoet/MailPoetController.php b/backend/Actions/MailPoet/MailPoetController.php index 634600cc2..7e89bb98a 100644 --- a/backend/Actions/MailPoet/MailPoetController.php +++ b/backend/Actions/MailPoet/MailPoetController.php @@ -34,17 +34,6 @@ public static function isExists() } } - /** - * Process ajax request for generate_token - * - * @return JSON zoho crm api response and status - */ - public static function mailPoetAuthorize() - { - self::isExists(); - wp_send_json_success(true); - } - /** * Process ajax request for refresh crm modules * diff --git a/backend/Actions/MailPoet/Routes.php b/backend/Actions/MailPoet/Routes.php index 63682515d..4f5930d0b 100644 --- a/backend/Actions/MailPoet/Routes.php +++ b/backend/Actions/MailPoet/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\MailPoet\MailPoetController; use BitApps\Integrations\Core\Util\Route; -Route::post('mail_poet_authorize', [MailPoetController::class, 'mailPoetAuthorize']); Route::post('refresh_news_letter', [MailPoetController::class, 'refreshNeswLetter']); Route::post('mail_poet_list_headers', [MailPoetController::class, 'mailPoetListHeaders']); diff --git a/backend/Actions/MailRelay/MailRelayController.php b/backend/Actions/MailRelay/MailRelayController.php index 62162a734..12a286542 100644 --- a/backend/Actions/MailRelay/MailRelayController.php +++ b/backend/Actions/MailRelay/MailRelayController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\MailRelay; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,11 +15,23 @@ */ class MailRelayController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'mailrelay', + 'fields' => [ + 'auth_token' => 'value', + 'domain' => 'domain', + ], + ]; + protected $_defaultHeader; - public function authentication($fieldsRequestParams) + public function getCustomFields($fieldsRequestParams) { - if (empty($fieldsRequestParams->auth_token) && empty($fieldsRequestParams->domain)) { + $authToken = $this->extractAuthToken($fieldsRequestParams); + $domain = $this->extractDomain($fieldsRequestParams); + + if (empty($authToken) || empty($domain)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -28,12 +41,10 @@ public function authentication($fieldsRequestParams) ); } - $domain = $fieldsRequestParams->domain; $baseUrl = "https://{$domain}.ipzmarketing.com/api/v1/"; $apiEndpoints = $baseUrl . 'custom_fields'; - $apiKey = $fieldsRequestParams->auth_token; $header = [ - 'X-AUTH-TOKEN' => $apiKey + 'X-AUTH-TOKEN' => $authToken ]; $response = HttpHelper::get($apiEndpoints, null, $header); @@ -56,7 +67,10 @@ public function authentication($fieldsRequestParams) public function getAllGroups($fieldsRequestParams) { - if (empty($fieldsRequestParams->auth_token) && empty($fieldsRequestParams->domain)) { + $authToken = $this->extractAuthToken($fieldsRequestParams); + $domain = $this->extractDomain($fieldsRequestParams); + + if (empty($authToken) || empty($domain)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -66,12 +80,10 @@ public function getAllGroups($fieldsRequestParams) ); } - $domain = $fieldsRequestParams->domain; $baseUrl = "https://{$domain}.ipzmarketing.com/api/v1/"; $apiEndpoints = $baseUrl . 'groups?page=1&&per_page=1000'; - $apiKey = $fieldsRequestParams->auth_token; $header = [ - 'X-AUTH-TOKEN' => $apiKey + 'X-AUTH-TOKEN' => $authToken ]; $response = HttpHelper::get($apiEndpoints, null, $header); @@ -95,7 +107,7 @@ public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; - $auth_token = $integrationDetails->auth_token; + $auth_token = !empty($integrationDetails->auth_token) ? $integrationDetails->auth_token : (isset($integrationDetails->api_key) ? $integrationDetails->api_key : ''); $selectedGroups = $integrationDetails->selectedGroups; $fieldMap = $integrationDetails->field_map; $status = $integrationDetails->status; @@ -122,4 +134,22 @@ public function execute($integrationData, $fieldValues) return $mailRelayApiResponse; } + + private function extractAuthToken($fieldsRequestParams) + { + if (!empty($fieldsRequestParams->auth_token)) { + return $fieldsRequestParams->auth_token; + } + + if (!empty($fieldsRequestParams->api_key)) { + return $fieldsRequestParams->api_key; + } + + return ''; + } + + private function extractDomain($fieldsRequestParams) + { + return !empty($fieldsRequestParams->domain) ? $fieldsRequestParams->domain : ''; + } } diff --git a/backend/Actions/MailRelay/Routes.php b/backend/Actions/MailRelay/Routes.php index f3a04dd97..070efc323 100644 --- a/backend/Actions/MailRelay/Routes.php +++ b/backend/Actions/MailRelay/Routes.php @@ -7,5 +7,5 @@ use BitApps\Integrations\Actions\MailRelay\MailRelayController; use BitApps\Integrations\Core\Util\Route; -Route::post('mailRelay_authentication', [MailRelayController::class, 'authentication']); +Route::post('mailRelay_fetch_custom_fields', [MailRelayController::class, 'getCustomFields']); Route::post('mailRelay_fetch_all_groups', [MailRelayController::class, 'getAllGroups']); diff --git a/backend/Actions/MailerLite/MailerLiteController.php b/backend/Actions/MailerLite/MailerLiteController.php index 636195a1e..4853f181c 100644 --- a/backend/Actions/MailerLite/MailerLiteController.php +++ b/backend/Actions/MailerLite/MailerLiteController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\MailerLite; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,15 @@ */ class MailerLiteController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'mailerlite', + 'fields' => [ + 'auth_token' => 'value', + 'version' => 'version', + ], + ]; + protected $_defaultHeader; private static $_baseUrlV1 = 'https://api.mailerlite.com/api/v2/'; @@ -78,34 +88,6 @@ public function fetchAllGroups($refreshFieldsRequestParams) } } - public function authorization($refreshFieldsRequestParams) - { - if (empty($refreshFieldsRequestParams->auth_token) || empty($refreshFieldsRequestParams->version)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $api = self::getApiVersionInfo($refreshFieldsRequestParams); - - $response = HttpHelper::get($api['endpoint'], null, $api['header']); - - if (HttpHelper::$responseCode == 200) { - wp_send_json_success('Authorization Successful', 200); - - return; - } - - wp_send_json_error( - $response->message ?? $response ?? 'Authorization Failed', - 400 - ); - } - public function mailerliteRefreshFields($refreshFieldsRequestParams) { if (empty($refreshFieldsRequestParams->auth_token)) { @@ -222,22 +204,4 @@ public function execute($integrationData, $fieldValues) return $mailerliteApiResponse; } - private static function getApiVersionInfo($refreshFieldsRequestParams) - { - if ('v2' === $refreshFieldsRequestParams->version) { - return [ - 'endpoint' => self::$_baseUrlV2 . 'subscribers', - 'header' => [ - 'Authorization' => 'Bearer ' . $refreshFieldsRequestParams->auth_token, - ] - ]; - } - - return [ - 'endpoint' => self::$_baseUrlV1 . 'me', - 'header' => [ - 'X-Mailerlite-Apikey' => $refreshFieldsRequestParams->auth_token, - ] - ]; - } } diff --git a/backend/Actions/MailerLite/Routes.php b/backend/Actions/MailerLite/Routes.php index 69e2a195a..4bdcf5825 100644 --- a/backend/Actions/MailerLite/Routes.php +++ b/backend/Actions/MailerLite/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\MailerLite\MailerLiteController; use BitApps\Integrations\Core\Util\Route; -Route::post('mailerlite_authorization', [MailerLiteController::class, 'authorization']); Route::post('mailerlite_fetch_all_groups', [MailerLiteController::class, 'fetchAllGroups']); Route::post('mailerlite_refresh_fields', [MailerLiteController::class, 'mailerliteRefreshFields']); diff --git a/backend/Actions/MailerPress/MailerPressController.php b/backend/Actions/MailerPress/MailerPressController.php index 3a2610e4e..e3dd87fa6 100644 --- a/backend/Actions/MailerPress/MailerPressController.php +++ b/backend/Actions/MailerPress/MailerPressController.php @@ -33,17 +33,6 @@ public static function isExists() } } - /** - * Process ajax request for authorization - * - * @return JSON response - */ - public static function mailerPressAuthorize() - { - self::isExists(); - wp_send_json_success(true); - } - /** * Process ajax request for refresh lists * diff --git a/backend/Actions/MailerPress/Routes.php b/backend/Actions/MailerPress/Routes.php index 19708b49f..186e4bb9f 100644 --- a/backend/Actions/MailerPress/Routes.php +++ b/backend/Actions/MailerPress/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\MailerPress\MailerPressController; use BitApps\Integrations\Core\Util\Route; -Route::post('mailer_press_authorize', [MailerPressController::class, 'mailerPressAuthorize']); Route::post('refresh_mailer_press_lists', [MailerPressController::class, 'refreshLists']); Route::post('refresh_mailer_press_tags', [MailerPressController::class, 'refreshTags']); diff --git a/backend/Actions/Mailercloud/MailercloudController.php b/backend/Actions/Mailercloud/MailercloudController.php index 318b03596..c4fd6a3b8 100644 --- a/backend/Actions/Mailercloud/MailercloudController.php +++ b/backend/Actions/Mailercloud/MailercloudController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Mailercloud; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,40 +15,20 @@ */ class MailercloudController { - private $baseUrl = 'https://cloudapi.mailercloud.com/v1/'; + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'mailercloud', + 'fields' => [ + 'api_key' => 'value', + ], + ]; - public function handleAuthorize($requestParams) - { - if (empty($requestParams->authKey)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $apiEndpoints = $this->baseUrl . 'client/plan'; - $headers = [ - 'Content-Type' => 'application/json', - 'Authorization' => $requestParams->authKey - ]; - $response = HttpHelper::get($apiEndpoints, null, $headers); - if ($response->code === 'invalid_api_key') { - wp_send_json_error( - __( - 'Invalid token', - 'bit-integrations' - ), - 400 - ); - } - wp_send_json_success($response, 200); - } + private $baseUrl = 'https://cloudapi.mailercloud.com/v1/'; public function getAllLists($requestParams) { - if (empty($requestParams->authKey)) { + $apiKey = $this->extractApiKey($requestParams); + if (empty($apiKey)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -59,7 +40,7 @@ public function getAllLists($requestParams) $apiEndpoints = $this->baseUrl . 'lists/search'; $headers = [ 'Content-Type' => 'application/json', - 'Authorization' => $requestParams->authKey + 'Authorization' => $apiKey ]; $body = [ 'limit' => 100, @@ -82,7 +63,8 @@ public function getAllLists($requestParams) public function getAllFields($requestParams) { - if (empty($requestParams->authKey)) { + $apiKey = $this->extractApiKey($requestParams); + if (empty($apiKey)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -94,7 +76,7 @@ public function getAllFields($requestParams) $apiEndpoints = $this->baseUrl . 'contact/property/search'; $headers = [ 'Content-Type' => 'application/json', - 'Authorization' => $requestParams->authKey + 'Authorization' => $apiKey ]; $body = [ 'limit' => 100, @@ -136,7 +118,7 @@ public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; - $authKey = $integrationDetails->authKey; + $authKey = !empty($integrationDetails->api_key) ? $integrationDetails->api_key : (isset($integrationDetails->authKey) ? $integrationDetails->authKey : ''); $listId = $integrationDetails->listId; $contactType = $integrationDetails->contactType; $field_map = $integrationDetails->field_map; @@ -163,4 +145,9 @@ public function execute($integrationData, $fieldValues) return $mailercloudApiResponse; } + + private function extractApiKey($requestParams) + { + return !empty($requestParams->api_key) ? $requestParams->api_key : (isset($requestParams->authKey) ? $requestParams->authKey : ''); + } } diff --git a/backend/Actions/Mailercloud/Routes.php b/backend/Actions/Mailercloud/Routes.php index 1372cf5e0..04844f1a3 100644 --- a/backend/Actions/Mailercloud/Routes.php +++ b/backend/Actions/Mailercloud/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Mailercloud\MailercloudController; use BitApps\Integrations\Core\Util\Route; -Route::post('mailercloud_handle_authorize', [MailercloudController::class, 'handleAuthorize']); Route::post('mailercloud_get_all_lists', [MailercloudController::class, 'getAllLists']); Route::post('mailercloud_get_all_fields', [MailercloudController::class, 'getAllFields']); diff --git a/backend/Actions/Mailify/MailifyController.php b/backend/Actions/Mailify/MailifyController.php index edda4acf4..86d692eb5 100644 --- a/backend/Actions/Mailify/MailifyController.php +++ b/backend/Actions/Mailify/MailifyController.php @@ -2,11 +2,21 @@ namespace BitApps\Integrations\Actions\Mailify; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; class MailifyController { + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'mailify', + 'fields' => [ + 'account_id' => 'username', + 'api_key' => 'password', + ], + ]; + private $integrationID; public function __construct($integrationID) @@ -14,31 +24,10 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function authorization($requestParams) - { - if (empty($requestParams->account_id) || empty($requestParams->api_key)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiEndpoint = 'https://mailifyapis.com/v1/users'; - $header['Authorization'] = 'Basic ' . base64_encode("{$requestParams->account_id}:{$requestParams->api_key}"); - - $response = HttpHelper::get($apiEndpoint, null, $header); - - if (!isset($response->users)) { - wp_send_json_error( - empty($response->message) ? 'Unknown' : $response->message, - 400 - ); - } - wp_send_json_success(true); - } - public static function mailifyHeaders($requestParams) { - if ( - empty($requestParams->account_id) || empty($requestParams->api_key) - ) { + $credentials = self::extractCredentials($requestParams); + if (empty($credentials['account_id']) || empty($credentials['api_key'])) { wp_send_json_error( __( 'Requested parameter is empty', @@ -51,8 +40,8 @@ public static function mailifyHeaders($requestParams) $listId = $requestParams->list_id; $apiEndpoint = "https://mailifyapis.com/v1/lists/{$listId}/fields"; $headers = [ - 'accountId' => $requestParams->account_id, - 'apiKey' => $requestParams->api_key, + 'accountId' => $credentials['account_id'], + 'apiKey' => $credentials['api_key'], ]; $mailifyResponse = HttpHelper::get($apiEndpoint, null, $headers); @@ -78,13 +67,14 @@ public static function mailifyHeaders($requestParams) public static function getAllList($requestParams) { - if (empty($requestParams->account_id) || empty($requestParams->api_key)) { + $credentials = self::extractCredentials($requestParams); + if (empty($credentials['account_id']) || empty($credentials['api_key'])) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } $headers = [ - 'accountId' => $requestParams->account_id, - 'apiKey' => $requestParams->api_key, + 'accountId' => $credentials['account_id'], + 'apiKey' => $credentials['api_key'], ]; $apiEndpoint = 'https://mailifyapis.com/v1/lists'; $apiResponse = HttpHelper::get($apiEndpoint, null, $headers); @@ -133,4 +123,15 @@ public function execute($integrationData, $fieldValues) return $mailifyApiResponse; } + + private static function extractCredentials($requestParams) + { + $accountId = !empty($requestParams->account_id) ? $requestParams->account_id : (!empty($requestParams->username) ? $requestParams->username : ''); + $apiKey = !empty($requestParams->api_key) ? $requestParams->api_key : (!empty($requestParams->password) ? $requestParams->password : ''); + + return [ + 'account_id' => $accountId, + 'api_key' => $apiKey, + ]; + } } diff --git a/backend/Actions/Mailify/Routes.php b/backend/Actions/Mailify/Routes.php index d8b315df6..b54aeb38b 100644 --- a/backend/Actions/Mailify/Routes.php +++ b/backend/Actions/Mailify/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Mailify\MailifyController; use BitApps\Integrations\Core\Util\Route; -Route::post('mailify_authorize', [MailifyController::class, 'authorization']); Route::post('mailify_lists', [MailifyController::class, 'getAllList']); Route::post('mailify_headers', [MailifyController::class, 'mailifyHeaders']); diff --git a/backend/Actions/Mailjet/MailjetController.php b/backend/Actions/Mailjet/MailjetController.php index b30c8319e..670be4f4f 100644 --- a/backend/Actions/Mailjet/MailjetController.php +++ b/backend/Actions/Mailjet/MailjetController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Mailjet; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,15 +15,24 @@ */ class MailjetController { - public function authentication($fieldsRequestParams) + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'mailjet', + 'fields' => [ + 'apiKey' => 'username', + 'secretKey' => 'password', + ], + ]; + + public function getAllLists($fieldsRequestParams) { - if (empty($fieldsRequestParams->secretKey) && empty($fieldsRequestParams->apiKey)) { + $apiKey = !empty($fieldsRequestParams->apiKey) ? $fieldsRequestParams->apiKey : (!empty($fieldsRequestParams->username) ? $fieldsRequestParams->username : ''); + $secretKey = !empty($fieldsRequestParams->secretKey) ? $fieldsRequestParams->secretKey : (!empty($fieldsRequestParams->password) ? $fieldsRequestParams->password : ''); + if (empty($secretKey) || empty($apiKey)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } $apiEndpoints = 'https://api.mailjet.com/v3/REST/contactslist?Limit=1000'; - $apiKey = $fieldsRequestParams->apiKey; - $secretKey = $fieldsRequestParams->secretKey; $header = [ 'Authorization' => 'Basic ' . base64_encode("{$apiKey}:{$secretKey}") ]; @@ -44,13 +54,13 @@ public function authentication($fieldsRequestParams) public function getCustomFields($fieldsRequestParams) { - if (empty($fieldsRequestParams->secretKey) && empty($fieldsRequestParams->apiKey)) { + $apiKey = !empty($fieldsRequestParams->apiKey) ? $fieldsRequestParams->apiKey : (!empty($fieldsRequestParams->username) ? $fieldsRequestParams->username : ''); + $secretKey = !empty($fieldsRequestParams->secretKey) ? $fieldsRequestParams->secretKey : (!empty($fieldsRequestParams->password) ? $fieldsRequestParams->password : ''); + if (empty($secretKey) || empty($apiKey)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } $apiEndpoints = 'https://api.mailjet.com/v3/REST/contactmetadata?Limit=1000'; - $apiKey = $fieldsRequestParams->apiKey; - $secretKey = $fieldsRequestParams->secretKey; $header = [ 'Authorization' => 'Basic ' . base64_encode("{$apiKey}:{$secretKey}") ]; @@ -76,8 +86,8 @@ public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; - $apiKey = $integrationDetails->apiKey; - $secretKey = $integrationDetails->secretKey; + $apiKey = !empty($integrationDetails->apiKey) ? $integrationDetails->apiKey : (isset($integrationDetails->username) ? $integrationDetails->username : ''); + $secretKey = !empty($integrationDetails->secretKey) ? $integrationDetails->secretKey : (isset($integrationDetails->password) ? $integrationDetails->password : ''); $selectedLists = $integrationDetails->selectedLists; $fieldMap = $integrationDetails->field_map; diff --git a/backend/Actions/Mailjet/Routes.php b/backend/Actions/Mailjet/Routes.php index fe0739982..d76cfa33a 100644 --- a/backend/Actions/Mailjet/Routes.php +++ b/backend/Actions/Mailjet/Routes.php @@ -7,5 +7,5 @@ use BitApps\Integrations\Actions\Mailjet\MailjetController; use BitApps\Integrations\Core\Util\Route; -Route::post('mailjet_authentication', [MailjetController::class, 'authentication']); +Route::post('mailjet_fetch_all_lists', [MailjetController::class, 'getAllLists']); Route::post('mailjet_fetch_all_custom_fields', [MailjetController::class, 'getCustomFields']); diff --git a/backend/Actions/Mailster/MailsterController.php b/backend/Actions/Mailster/MailsterController.php index 2aa7396ee..d6c52e4f6 100644 --- a/backend/Actions/Mailster/MailsterController.php +++ b/backend/Actions/Mailster/MailsterController.php @@ -16,21 +16,6 @@ */ class MailsterController { - public function authentication() - { - if (self::checkedMailsterExists()) { - wp_send_json_success(true); - } else { - wp_send_json_error( - __( - 'Please! Install Mailster', - 'bit-integrations' - ), - 400 - ); - } - } - public static function checkedMailsterExists() { if (!is_plugin_active('mailster/mailster.php')) { diff --git a/backend/Actions/Mailster/Routes.php b/backend/Actions/Mailster/Routes.php index 6ba632c34..16921ed67 100644 --- a/backend/Actions/Mailster/Routes.php +++ b/backend/Actions/Mailster/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Mailster\MailsterController; use BitApps\Integrations\Core\Util\Route; -Route::post('mailster_authentication', [MailsterController::class, 'authentication']); Route::post('mailster_fields', [MailsterController::class, 'getMailsterFields']); Route::post('mailster_lists', [MailsterController::class, 'getMailsterLists']); Route::post('mailster_tags', [MailsterController::class, 'getMailsterTags']); diff --git a/backend/Actions/Mailup/MailupController.php b/backend/Actions/Mailup/MailupController.php index efc6ca541..f68c4de2d 100644 --- a/backend/Actions/Mailup/MailupController.php +++ b/backend/Actions/Mailup/MailupController.php @@ -2,12 +2,23 @@ namespace BitApps\Integrations\Actions\Mailup; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use WP_Error; class MailupController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'mailup', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on']], + ], + ]; + private $integrationID; public function __construct($integrationID) @@ -15,33 +26,6 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function authorization($requestParams) - { - if (empty($requestParams->clientId) || empty($requestParams->clientSecret) || empty($requestParams->code)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $authCode = explode('-', $requestParams->code)[0]; - - $body = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestParams->clientId, - 'client_secret' => $requestParams->clientSecret, - 'code' => $authCode - ]; - - $apiEndpoint = 'https://services.mailup.com/Authorization/OAuth/Token'; - $header['Content-Type'] = 'application/x-www-form-urlencoded'; - $header['Authorization'] = 'Basic ' . base64_encode("{$requestParams->clientId}:{$requestParams->clientSecret}"); - $apiResponse = HttpHelper::post($apiEndpoint, $body, $header); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error(empty($apiResponse->error_description) ? 'Unknown' : $apiResponse->error_description, 400); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public static function getAllField($requestParams) { if (empty($requestParams->tokenDetails) || empty($requestParams->clientId) || empty($requestParams->clientSecret)) { @@ -176,15 +160,21 @@ protected static function tokenExpiryCheck($token, $clientId, $clientSecret) return false; } - if ((\intval($token->generates_on) + (55 * 60)) < time()) { + $generatedOn = !empty($token->generates_on) + ? \intval($token->generates_on) + : \intval($token->generated_at ?? 0); + + if (($generatedOn + (55 * 60)) < time()) { $refreshToken = self::refreshToken($token->refresh_token, $clientId, $clientSecret); if (is_wp_error($refreshToken) || !empty($refreshToken->error)) { return false; } $token->access_token = $refreshToken->access_token; + $token->refresh_token = $refreshToken->refresh_token ?? $token->refresh_token; $token->expires_in = $refreshToken->expires_in; $token->generates_on = $refreshToken->generates_on; + $token->generated_at = $refreshToken->generated_at; } return $token; @@ -206,6 +196,7 @@ protected static function refreshToken($refresh_token, $clientId, $clientSecret) } $token = $apiResponse; $token->generates_on = time(); + $token->generated_at = $token->generates_on; return $token; } diff --git a/backend/Actions/Mailup/Routes.php b/backend/Actions/Mailup/Routes.php index 1b4189f75..c146e0ca2 100644 --- a/backend/Actions/Mailup/Routes.php +++ b/backend/Actions/Mailup/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Mailup\MailupController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('mailup_authorization', [MailupController::class, 'authorization']); Route::post('mailup_fetch_all_list', [MailupController::class, 'getAllList']); Route::post('mailup_fetch_all_group', [MailupController::class, 'getAllGroup']); Route::post('mailup_fetch_all_field', [MailupController::class, 'getAllField']); diff --git a/backend/Actions/MasterStudyLms/MasterStudyLmsController.php b/backend/Actions/MasterStudyLms/MasterStudyLmsController.php index 0fc31372a..8b0d7e6d0 100644 --- a/backend/Actions/MasterStudyLms/MasterStudyLmsController.php +++ b/backend/Actions/MasterStudyLms/MasterStudyLmsController.php @@ -16,16 +16,6 @@ public static function pluginActive() ); } - public static function authorizeMasterStudyLms() - { - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - // translators: %s: Placeholder value - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'MasterStudyLms')); - } - public static function getAllCourse() { if (self::pluginActive()) { diff --git a/backend/Actions/MasterStudyLms/Routes.php b/backend/Actions/MasterStudyLms/Routes.php index 5b6793625..b52146da8 100644 --- a/backend/Actions/MasterStudyLms/Routes.php +++ b/backend/Actions/MasterStudyLms/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\MasterStudyLms\MasterStudyLmsController; use BitApps\Integrations\Core\Util\Route; -Route::post('MasterStudyLms_authorize', [MasterStudyLmsController::class, 'authorizeMasterStudyLms']); Route::post('mslms_fetch_all_course', [MasterStudyLmsController::class, 'getAllCourse']); Route::post('msLms_fetch_all_lesson', [MasterStudyLmsController::class, 'getAllLesson']); Route::post('msLms_fetch_all_quiz', [MasterStudyLmsController::class, 'getAllQuizByCourse']); diff --git a/backend/Actions/Mautic/MauticController.php b/backend/Actions/Mautic/MauticController.php index 85e31f71b..939fd5c49 100644 --- a/backend/Actions/Mautic/MauticController.php +++ b/backend/Actions/Mautic/MauticController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Mautic; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,17 @@ */ class MauticController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'mautic', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + 'baseUrl' => 'baseUrl', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -21,51 +33,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - /** - * Process ajax request for generate_token - * - * @param object $requestsParams Params for generate token - * - * @return JSON zoho crm api response and status - */ - public static function generateTokens($requestsParams) - { - if ( - empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->baseUrl) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $baseUrl = $requestsParams->baseUrl; - $apiEndpoint = "{$baseUrl}/oauth/v2/token"; - $authorizationHeader['Content-Type'] = 'application/x-www-form-urlencoded'; - $requestParams = [ - 'code' => $requestsParams->code, - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => $requestsParams->redirectURI, - 'grant_type' => 'authorization_code' - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams, $authorizationHeader); - if (is_wp_error($apiResponse) || !empty($apiResponse->errors)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - /** * Process ajax request for refresh Mautic Audience Fields * @@ -86,7 +53,7 @@ public static function getAllFields($queryParams) } $mauticUrl = $queryParams->baseUrl; $response = []; - if ((\intval($queryParams->tokenDetails->generates_on) + (55 * 60)) < time()) { + if (static::isTokenExpired($queryParams->tokenDetails)) { $response['tokenDetails'] = static::_refreshAccessToken($queryParams); } $tokenDetails = empty($response['tokenDetails']) ? $queryParams->tokenDetails : $response['tokenDetails']; @@ -121,7 +88,7 @@ public static function getAllTags($queryParams) } $mauticUrl = $queryParams->baseUrl; $response = []; - if ((\intval($queryParams->tokenDetails->generates_on) + (55 * 60)) < time()) { + if (static::isTokenExpired($queryParams->tokenDetails)) { $response['tokenDetails'] = static::_refreshAccessToken($queryParams); } $tokenDetails = empty($response['tokenDetails']) ? $queryParams->tokenDetails : $response['tokenDetails']; @@ -155,7 +122,7 @@ public static function getAllUsers($queryParams) $response = []; - if ((\intval($queryParams->tokenDetails->generates_on) + (55 * 60)) < time()) { + if (static::isTokenExpired($queryParams->tokenDetails)) { $response['tokenDetails'] = static::_refreshAccessToken($queryParams); } @@ -202,7 +169,7 @@ public function execute($integrationData, $fieldValues) // translators: %s: Placeholder value return new WP_Error('REQ_FIELD_EMPTY', wp_sprintf(__('module, fields are required for %s api', 'bit-integrations'), 'mautic')); } - if ((\intval($tokenDetails->generates_on) + (60 * 55)) < time()) { + if (static::isTokenExpired($tokenDetails)) { $requiredParams['clientId'] = $integrationDetails->clientId; $requiredParams['clientSecret'] = $integrationDetails->clientSecret; $requiredParams['baseUrl'] = $integrationDetails->baseUrl; @@ -248,9 +215,33 @@ protected static function _refreshAccessToken($apiData) if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { return false; } - $tokenDetails->generates_on = time(); + $generatedAt = time(); + $tokenDetails->generates_on = $generatedAt; + $tokenDetails->generated_at = $generatedAt; $tokenDetails->access_token = $apiResponse->access_token; + if (!empty($apiResponse->refresh_token)) { + $tokenDetails->refresh_token = $apiResponse->refresh_token; + } + if (isset($apiResponse->expires_in)) { + $tokenDetails->expires_in = $apiResponse->expires_in; + } return $tokenDetails; } + + private static function isTokenExpired($tokenDetails) + { + if (empty($tokenDetails) || !\is_object($tokenDetails)) { + return false; + } + + $generatedAt = empty($tokenDetails->generates_on) ? ($tokenDetails->generated_at ?? 0) : $tokenDetails->generates_on; + $expiresIn = $tokenDetails->expires_in ?? 0; + + if (!empty($generatedAt) && !empty($expiresIn) && (int) $expiresIn > 0) { + return ((int) $generatedAt + (int) $expiresIn - 30) < time(); + } + + return ((int) $generatedAt + (55 * 60)) < time(); + } } diff --git a/backend/Actions/Mautic/Routes.php b/backend/Actions/Mautic/Routes.php index 91cb283ce..8b53da7f7 100644 --- a/backend/Actions/Mautic/Routes.php +++ b/backend/Actions/Mautic/Routes.php @@ -7,8 +7,6 @@ use BitApps\Integrations\Actions\Mautic\MauticController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('mautic_generate_token', [MauticController::class, 'generateTokens']); -// Route::post('mChimp_refresh_audience', [ MailChimpController::class, 'refreshAudience']); Route::post('mautic_get_fields', [MauticController::class, 'getAllFields']); Route::post('mautic_get_tags', [MauticController::class, 'getAllTags']); Route::post('mautic_get_users', [MauticController::class, 'getAllUsers']); diff --git a/backend/Actions/Memberpress/MemberpressController.php b/backend/Actions/Memberpress/MemberpressController.php index 417f1ec1d..38b546c1b 100644 --- a/backend/Actions/Memberpress/MemberpressController.php +++ b/backend/Actions/Memberpress/MemberpressController.php @@ -23,17 +23,6 @@ public static function pluginActive($option = null) return false; } - public static function authorizeMemberpress() - { - include_once ABSPATH . 'wp-admin/includes/plugin.php'; - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - // translators: %s: Placeholder value - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'Memberpress')); - } - public function getAllMembership($label = null, $option_code = 'MPPRODUCT', $args = []) { include_once ABSPATH . 'wp-admin/includes/plugin.php'; diff --git a/backend/Actions/Memberpress/Routes.php b/backend/Actions/Memberpress/Routes.php index d47ee3130..dbe7b5f0d 100644 --- a/backend/Actions/Memberpress/Routes.php +++ b/backend/Actions/Memberpress/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Memberpress\MemberpressController; use BitApps\Integrations\Core\Util\Route; -Route::post('memberpress_authorize', [MemberpressController::class, 'authorizeMemberpress']); Route::post('fetch_all_membership', [MemberpressController::class, 'getAllMembership']); Route::post('fetch_all_payment_gateway', [MemberpressController::class, 'allPaymentGateway']); diff --git a/backend/Actions/MondayCom/MondayComController.php b/backend/Actions/MondayCom/MondayComController.php index 9a3f01a77..c7edf29ec 100644 --- a/backend/Actions/MondayCom/MondayComController.php +++ b/backend/Actions/MondayCom/MondayComController.php @@ -6,31 +6,24 @@ namespace BitApps\Integrations\Actions\MondayCom; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; class MondayComController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'mondaycom', + 'fields' => [ + 'apiToken' => 'value', + ], + ]; + private const API_URL = 'https://api.monday.com/v2'; private const API_VERSION = '2023-10'; - public static function authentication($requestParams) - { - if (empty($requestParams->apiToken)) { - wp_send_json_error(__('API Token is empty', 'bit-integrations'), 400); - } - - $query = 'query { me { id name email } }'; - $response = self::request($requestParams->apiToken, $query); - - if (!self::hasErrors($response) && isset($response->data->me->id)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } - - wp_send_json_error(self::errorMessage($response, __('Authentication failed', 'bit-integrations')), 400); - } - public function getBoards($requestParams) { self::validateToken($requestParams); diff --git a/backend/Actions/MondayCom/Routes.php b/backend/Actions/MondayCom/Routes.php index 73fe8153c..9da8d37fa 100644 --- a/backend/Actions/MondayCom/Routes.php +++ b/backend/Actions/MondayCom/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\MondayCom\MondayComController; use BitApps\Integrations\Core\Util\Route; -Route::post('mondayCom_authentication', [MondayComController::class, 'authentication']); Route::post('mondayCom_fetch_boards', [MondayComController::class, 'getBoards']); Route::post('mondayCom_fetch_groups', [MondayComController::class, 'getGroups']); Route::post('mondayCom_fetch_columns', [MondayComController::class, 'getColumns']); diff --git a/backend/Actions/Moosend/MoosendController.php b/backend/Actions/Moosend/MoosendController.php index 304df1b33..acc6c7450 100644 --- a/backend/Actions/Moosend/MoosendController.php +++ b/backend/Actions/Moosend/MoosendController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Moosend; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,11 +15,20 @@ */ class MoosendController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'moosend', + 'fields' => [ + 'authKey' => 'value', + ], + ]; + private $baseUrl = 'https://api.moosend.com/v3/'; - public function handleAuthorize($requestParams) + public function getAllLists($requestParams) { - if (empty($requestParams->authKey)) { + $authKey = !empty($requestParams->authKey) ? $requestParams->authKey : (!empty($requestParams->api_key) ? $requestParams->api_key : ''); + if (empty($authKey)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -27,7 +37,7 @@ public function handleAuthorize($requestParams) 400 ); } - $apiEndpoints = $this->baseUrl . 'lists/1/1000.json?apikey=' . $requestParams->authKey; + $apiEndpoints = $this->baseUrl . 'lists/1/1000.json?apikey=' . $authKey; $headers = [ 'Content-Type' => 'application/json', 'Accept' => 'application/json', @@ -49,7 +59,7 @@ public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; - $authKey = $integrationDetails->authKey; + $authKey = !empty($integrationDetails->authKey) ? $integrationDetails->authKey : (isset($integrationDetails->api_key) ? $integrationDetails->api_key : ''); $listId = $integrationDetails->listId; $method = $integrationDetails->method; $field_map = $integrationDetails->field_map; diff --git a/backend/Actions/Moosend/Routes.php b/backend/Actions/Moosend/Routes.php index 10bfc550d..3e0ac5708 100644 --- a/backend/Actions/Moosend/Routes.php +++ b/backend/Actions/Moosend/Routes.php @@ -7,4 +7,4 @@ use BitApps\Integrations\Actions\Moosend\MoosendController; use BitApps\Integrations\Core\Util\Route; -Route::post('moosend_handle_authorize', [MoosendController::class, 'handleAuthorize']); +Route::post('moosend_lists', [MoosendController::class, 'getAllLists']); diff --git a/backend/Actions/MoxieCRM/MoxieCRMController.php b/backend/Actions/MoxieCRM/MoxieCRMController.php index 555e728a6..773244575 100644 --- a/backend/Actions/MoxieCRM/MoxieCRMController.php +++ b/backend/Actions/MoxieCRM/MoxieCRMController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\MoxieCRM; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,15 @@ */ class MoxieCRMController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'moxiecrm', + 'fields' => [ + 'api_key' => 'value', + 'api_url' => 'api_url', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; @@ -23,28 +33,6 @@ class MoxieCRMController // $this->apiEndpoint = "https://api.moxie.com/developer_api/v1"; // } - public function authentication($fieldsRequestParams) - { - if (empty($fieldsRequestParams->api_key)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiKey = $fieldsRequestParams->api_key; - $apiEndpoint = 'https://' . $fieldsRequestParams->api_url . '/api/public/action/users/list'; - $headers = [ - 'X-API-KEY' => $apiKey, - 'Content-Type' => 'application/json' - ]; - - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (!isset($response->error)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API key', 'bit-integrations'), 400); - } - } - // public function getCustomFields($fieldsRequestParams) // { // if (empty($fieldsRequestParams->api_key)) { diff --git a/backend/Actions/MoxieCRM/Routes.php b/backend/Actions/MoxieCRM/Routes.php index 70192e20b..68c77b676 100644 --- a/backend/Actions/MoxieCRM/Routes.php +++ b/backend/Actions/MoxieCRM/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\MoxieCRM\MoxieCRMController; use BitApps\Integrations\Core\Util\Route; -Route::post('moxiecrm_authentication', [MoxieCRMController::class, 'authentication']); // Route::post('moxiecrm_fetch_custom_fields', [MoxieCRMController::class, 'getCustomFields']); Route::post('moxiecrm_fetch_all_opportunities', [MoxieCRMController::class, 'getAllOpportunities']); Route::post('moxiecrm_fetch_all_clients', [MoxieCRMController::class, 'getAllClients']); diff --git a/backend/Actions/Newsletter/NewsletterController.php b/backend/Actions/Newsletter/NewsletterController.php index 016a6bcd7..8eb043bc5 100644 --- a/backend/Actions/Newsletter/NewsletterController.php +++ b/backend/Actions/Newsletter/NewsletterController.php @@ -13,21 +13,6 @@ */ class NewsletterController { - public function authentication() - { - if (self::checkedNewsletterExists()) { - wp_send_json_success(true); - } else { - wp_send_json_error( - __( - 'Please! Install Newsletter', - 'bit-integrations' - ), - 400 - ); - } - } - public static function checkedNewsletterExists() { if (!is_plugin_active('newsletter/plugin.php')) { diff --git a/backend/Actions/Newsletter/Routes.php b/backend/Actions/Newsletter/Routes.php deleted file mode 100644 index 797f4cea0..000000000 --- a/backend/Actions/Newsletter/Routes.php +++ /dev/null @@ -1,10 +0,0 @@ - AuthorizationType::API_KEY, + 'slug' => 'nimble', + 'fields' => [ + 'api_key' => 'value', + ], + ]; + protected $_defaultHeader; protected $_apiEndpoint; @@ -23,24 +32,11 @@ public function __construct() $this->_apiEndpoint = 'https://app.nimble.com/api/v1'; } - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->setHeaders($fieldsRequestParams->api_key); - $apiEndpoint = $this->_apiEndpoint . '/myself'; - $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); - - if (isset($response->user_id)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API key', 'bit-integrations'), 400); - } - } - public function getAllFields($fieldsRequestParams) { - $this->checkValidation($fieldsRequestParams); - $this->setHeaders($fieldsRequestParams->api_key); + $apiKey = !empty($fieldsRequestParams->api_key) ? $fieldsRequestParams->api_key : (!empty($fieldsRequestParams->value) ? $fieldsRequestParams->value : ''); + $this->checkValidation($apiKey); + $this->setHeaders($apiKey); $apiEndpoint = $this->_apiEndpoint . '/contacts/fields'; $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); @@ -114,7 +110,7 @@ public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; - $apiKey = $integrationDetails->api_key; + $apiKey = !empty($integrationDetails->api_key) ? $integrationDetails->api_key : (isset($integrationDetails->value) ? $integrationDetails->value : ''); $fieldMap = $integrationDetails->field_map; $actionName = $integrationDetails->actionName; @@ -133,9 +129,9 @@ public function execute($integrationData, $fieldValues) return $nimbleApiResponse; } - private function checkValidation($fieldsRequestParams, $customParam = '**') + private function checkValidation($apiKey, $customParam = '**') { - if (empty($fieldsRequestParams->api_key) || empty($customParam)) { + if (empty($apiKey) || empty($customParam)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } } diff --git a/backend/Actions/Nimble/Routes.php b/backend/Actions/Nimble/Routes.php index 326fdc406..048e87d8a 100644 --- a/backend/Actions/Nimble/Routes.php +++ b/backend/Actions/Nimble/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Nimble\NimbleController; use BitApps\Integrations\Core\Util\Route; -Route::post('nimble_authentication', [NimbleController::class, 'authentication']); Route::post('nimble_fetch_all_fields', [NimbleController::class, 'getAllFields']); Route::post('nimble_fetch_all_sessions', [NimbleController::class, 'getAllSessions']); diff --git a/backend/Actions/NinjaTables/NinjaTablesController.php b/backend/Actions/NinjaTables/NinjaTablesController.php index a629b12a0..25b7da05c 100644 --- a/backend/Actions/NinjaTables/NinjaTablesController.php +++ b/backend/Actions/NinjaTables/NinjaTablesController.php @@ -42,17 +42,6 @@ public static function isExists() return true; } - /** - * Authorize Ninja Tables integration - * - * @return void - */ - public static function ninjaTablesAuthorize() - { - self::isExists(); - wp_send_json_success(true); - } - /** * Get all published Ninja Tables * diff --git a/backend/Actions/NinjaTables/Routes.php b/backend/Actions/NinjaTables/Routes.php index a1d59d2d8..0a53913dd 100644 --- a/backend/Actions/NinjaTables/Routes.php +++ b/backend/Actions/NinjaTables/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\NinjaTables\NinjaTablesController; use BitApps\Integrations\Core\Util\Route; -Route::post('ninja_tables_authorize', [NinjaTablesController::class, 'ninjaTablesAuthorize']); Route::post('refresh_ninja_tables', [NinjaTablesController::class, 'refreshTables']); Route::post('refresh_ninja_tables_rows', [NinjaTablesController::class, 'refreshRows']); Route::post('refresh_ninja_tables_users', [NinjaTablesController::class, 'refreshUsers']); diff --git a/backend/Actions/NotificationX/NotificationXController.php b/backend/Actions/NotificationX/NotificationXController.php index 1e428abdf..67ccc1a6e 100644 --- a/backend/Actions/NotificationX/NotificationXController.php +++ b/backend/Actions/NotificationX/NotificationXController.php @@ -26,12 +26,6 @@ public static function isExists() } } - public static function notificationXAuthorize() - { - self::isExists(); - wp_send_json_success(true); - } - public static function getNotificationsBySource($requestsParams) { self::isExists(); diff --git a/backend/Actions/NotificationX/Routes.php b/backend/Actions/NotificationX/Routes.php index 340d4dff4..cdc6084bc 100644 --- a/backend/Actions/NotificationX/Routes.php +++ b/backend/Actions/NotificationX/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\NotificationX\NotificationXController; use BitApps\Integrations\Core\Util\Route; -Route::post('notificationx_authorize', [NotificationXController::class, 'notificationXAuthorize']); Route::post('notificationx_get_notifications_by_source', [NotificationXController::class, 'getNotificationsBySource']); diff --git a/backend/Actions/Notion/NotionController.php b/backend/Actions/Notion/NotionController.php index b3a8c60b7..a5aaa8ae5 100644 --- a/backend/Actions/Notion/NotionController.php +++ b/backend/Actions/Notion/NotionController.php @@ -2,44 +2,30 @@ namespace BitApps\Integrations\Actions\Notion; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; class NotionController { - private $baseurl = 'https://api.notion.com/v1/'; - - public function authorization($requestParams) - { - if (empty($requestParams->clientId) || empty($requestParams->clientSecret) || empty($requestParams->code) || empty($requestParams->redirectURI)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $body = [ - 'redirect_uri' => urldecode($requestParams->redirectURI), - 'grant_type' => 'authorization_code', - 'code' => $requestParams->code - ]; - $apiEndpoint = "{$this->baseurl}oauth/token"; - - $clientId = $requestParams->clientId; - $clientSecret = $requestParams->clientSecret; + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'notion', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; - $header['Content-Type'] = 'application/json'; - $header['Authorization'] = 'Basic ' . base64_encode("{$clientId}:{$clientSecret}"); - - $apiResponse = HttpHelper::post($apiEndpoint, wp_json_encode($body), $header); - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error(empty($apiResponse->error_description) ? 'Unknown' : $apiResponse->error_description, 400); - } - $apiResponse->generates_on = time(); - - wp_send_json_success($apiResponse, 200); - } + private $baseurl = 'https://api.notion.com/v1/'; public function getAllDatabaseLists($requestParams) { - if (empty($requestParams->accessToken)) { + $accessToken = $requestParams->accessToken + ?? ($requestParams->tokenDetails->access_token ?? ($requestParams->access_token ?? '')); + + if (empty($accessToken)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -50,7 +36,7 @@ public function getAllDatabaseLists($requestParams) } $apiEndpoints = "{$this->baseurl}search"; $headers = [ - 'Authorization' => 'Bearer ' . $requestParams->accessToken, + 'Authorization' => 'Bearer ' . $accessToken, 'Notion-Version' => '2021-08-16' ]; $response = HttpHelper::post($apiEndpoints, null, $headers); @@ -68,7 +54,10 @@ public function getAllDatabaseLists($requestParams) public function getFieldsProperties($requestParams) { - if (empty($requestParams->accessToken)) { + $accessToken = $requestParams->accessToken + ?? ($requestParams->tokenDetails->access_token ?? ($requestParams->access_token ?? '')); + + if (empty($accessToken)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -79,7 +68,7 @@ public function getFieldsProperties($requestParams) } $apiEndpoints = "{$this->baseurl}databases/{$requestParams->databaseId}"; $headers = [ - 'Authorization' => 'Bearer ' . $requestParams->accessToken, + 'Authorization' => 'Bearer ' . $accessToken, 'Notion-Version' => '2021-08-16' ]; $response = HttpHelper::get($apiEndpoints, null, $headers); @@ -101,9 +90,9 @@ public function execute($integrationData, $fieldValues) $integId = $integrationData->id; $databaseId = $integrationDetails->databaseId; $notionFields = $integrationDetails->notionFields; - $tokenDetails = $integrationDetails->tokenDetails; - $accessToken = $tokenDetails->access_token; - $tokenType = $tokenDetails->token_type; + $tokenDetails = $integrationDetails->tokenDetails ?? null; + $accessToken = $tokenDetails->access_token ?? ($integrationDetails->access_token ?? ''); + $tokenType = $tokenDetails->token_type ?? ($integrationDetails->token_type ?? 'Bearer'); $field_map = $integrationDetails->field_map; if ( diff --git a/backend/Actions/Notion/Routes.php b/backend/Actions/Notion/Routes.php index 0300eb863..b7f119083 100644 --- a/backend/Actions/Notion/Routes.php +++ b/backend/Actions/Notion/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Notion\NotionController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('notion_authorization', [NotionController::class, 'authorization']); Route::post('notion_database_lists', [NotionController::class, 'getAllDatabaseLists']); Route::post('notion_database_properties', [NotionController::class, 'getFieldsProperties']); diff --git a/backend/Actions/NutshellCRM/NutshellCRMController.php b/backend/Actions/NutshellCRM/NutshellCRMController.php index a81a10c26..0a0b93785 100644 --- a/backend/Actions/NutshellCRM/NutshellCRMController.php +++ b/backend/Actions/NutshellCRM/NutshellCRMController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\NutshellCRM; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,31 +15,19 @@ */ class NutshellCRMController { + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'nutshellcrm', + 'fields' => [ + 'user_name' => 'username', + 'api_token' => 'password', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $userName = $fieldsRequestParams->user_name; - $apiToken = $fieldsRequestParams->api_token; - $apiEndpoint = $this->setApiEndpoint(); - $headers = $this->setHeaders($userName, $apiToken); - $body = [ - 'method' => 'getUser', - 'id' => 'randomstring', - ]; - - $response = HttpHelper::post($apiEndpoint, wp_json_encode($body), $headers); - - if (isset($response->result)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid User Name & Secret or Access Api URL', 'bit-integrations'), 400); - } - } - public function getCompanies($fieldsRequestParams) { if (empty($fieldsRequestParams->user_name || $fieldsRequestParams->api_token)) { diff --git a/backend/Actions/NutshellCRM/Routes.php b/backend/Actions/NutshellCRM/Routes.php index 6ff9d9a30..007f781d4 100644 --- a/backend/Actions/NutshellCRM/Routes.php +++ b/backend/Actions/NutshellCRM/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\NutshellCRM\NutshellCRMController; use BitApps\Integrations\Core\Util\Route; -Route::post('nutshellcrm_authentication', [NutshellCRMController::class, 'authentication']); Route::post('nutshellcrm_fetch_all_contacts', [NutshellCRMController::class, 'getContacts']); Route::post('nutshellcrm_fetch_all_products', [NutshellCRMController::class, 'getProducts']); Route::post('nutshellcrm_fetch_all_sources', [NutshellCRMController::class, 'getSources']); diff --git a/backend/Actions/OmniSend/OmniSendController.php b/backend/Actions/OmniSend/OmniSendController.php index b0b871841..2a07550db 100644 --- a/backend/Actions/OmniSend/OmniSendController.php +++ b/backend/Actions/OmniSend/OmniSendController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\OmniSend; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,39 +15,18 @@ */ class OmniSendController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'omnisend', + 'fields' => [ + 'api_key' => 'value', + ], + ]; + protected $_defaultHeader; private $baseUrl = 'https://api.omnisend.com/v3/'; - public function authorization($requestParams) - { - if (empty($requestParams->api_key)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoints = $this->baseUrl . 'contacts'; - - $header = [ - 'X-API-KEY' => $requestParams->api_key, - ]; - - $response = HttpHelper::get($apiEndpoints, null, $header); - if (isset($response->contacts)) { - wp_send_json_success('', 200); - } else { - wp_send_json_error( - 'The token is invalid', - 400 - ); - } - } - public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; diff --git a/backend/Actions/OmniSend/Routes.php b/backend/Actions/OmniSend/Routes.php index 9d121d959..026fbbb69 100644 --- a/backend/Actions/OmniSend/Routes.php +++ b/backend/Actions/OmniSend/Routes.php @@ -6,5 +6,3 @@ use BitApps\Integrations\Actions\OmniSend\OmniSendController; use BitApps\Integrations\Core\Util\Route; - -Route::post('Omnisend_authorization', [OmniSendController::class, 'authorization']); diff --git a/backend/Actions/OneDrive/OneDriveController.php b/backend/Actions/OneDrive/OneDriveController.php index 813a21648..ec1d8a4a7 100644 --- a/backend/Actions/OneDrive/OneDriveController.php +++ b/backend/Actions/OneDrive/OneDriveController.php @@ -3,12 +3,23 @@ namespace BitApps\Integrations\Actions\OneDrive; use BitApps\Integrations\Actions\OneDrive\RecordApiHelper as OneDriveRecordApiHelper; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use BitApps\Integrations\Log\LogHandler; class OneDriveController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'onedrive', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; + private $integrationID; public function __construct($integrationID) @@ -16,42 +27,31 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function authorization($requestParams) + public static function getAllFolders($queryParams) { - if (empty($requestParams->clientId) || empty($requestParams->clientSecret) || empty($requestParams->code) || empty($requestParams->redirectURI)) { + $tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $isConnectionAuth = !empty($queryParams->connection_id); + $clientId = $queryParams->clientId ?? ''; + $clientSecret = $queryParams->clientSecret ?? ''; + + if (empty($tokenDetails->access_token)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $body = [ - 'client_id' => $requestParams->clientId, - 'redirect_uri' => urldecode($requestParams->redirectURI), - 'client_secret' => $requestParams->clientSecret, - 'grant_type' => 'authorization_code', - 'code' => urldecode($requestParams->code) - ]; - - $apiEndpoint = 'https://login.live.com/oauth20_token.srf'; - $header['Content-Type'] = 'application/x-www-form-urlencoded'; - $apiResponse = HttpHelper::post($apiEndpoint, $body, $header); - if (is_wp_error($apiResponse) || !empty($apiResponse->error) || HttpHelper::$responseCode !== 200) { - wp_send_json_error(empty($apiResponse->error_description) ? 'Unknown' : $apiResponse->error_description, 400); + $oldToken = $tokenDetails->access_token; + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $clientId, $clientSecret); } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public static function getAllFolders($queryParams) - { - if (empty($queryParams->tokenDetails) || empty($queryParams->clientId) || empty($queryParams->clientSecret)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + wp_send_json_error(__('Authorization failed', 'bit-integrations'), 400); } - $token = self::tokenExpiryCheck($queryParams->tokenDetails, $queryParams->clientId, $queryParams->clientSecret); - if ($token->access_token !== $queryParams->tokenDetails->access_token) { - self::saveRefreshedToken($queryParams->flowID, $token); + if (!$isConnectionAuth && !empty($queryParams->flowID) && $tokenDetails->access_token !== $oldToken) { + self::saveRefreshedToken($queryParams->flowID, $tokenDetails); } - $folders = self::getOneDriveFoldersList($token->access_token); + $folders = self::getOneDriveFoldersList($tokenDetails->access_token); $foldersOnly = $folders->value; $data = []; @@ -63,7 +63,7 @@ public static function getAllFolders($queryParams) } } $response['oneDriveFoldersList'] = $data; - $response['tokenDetails'] = $token; + $response['tokenDetails'] = $tokenDetails; wp_send_json_success($response, 200); } @@ -85,20 +85,33 @@ public static function getOneDriveFoldersList($token) public static function singleOneDriveFolderList($queryParams) { - if (empty($queryParams->tokenDetails) || empty($queryParams->clientId) || empty($queryParams->clientSecret)) { + $tokenDetails = self::normalizeConnectionToken($queryParams->tokenDetails ?? null); + $isConnectionAuth = !empty($queryParams->connection_id); + $clientId = $queryParams->clientId ?? ''; + $clientSecret = $queryParams->clientSecret ?? ''; + + if (empty($tokenDetails->access_token)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } $ids = explode('!', $queryParams->folder); - $token = self::tokenExpiryCheck($queryParams->tokenDetails, $queryParams->clientId, $queryParams->clientSecret); - if ($token->access_token !== $queryParams->tokenDetails->access_token) { - self::saveRefreshedToken($queryParams->flowID, $token); + $oldToken = $tokenDetails->access_token; + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $clientId, $clientSecret); + } + + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + wp_send_json_error(__('Authorization failed', 'bit-integrations'), 400); + } + + if (!$isConnectionAuth && !empty($queryParams->flowID) && $tokenDetails->access_token !== $oldToken) { + self::saveRefreshedToken($queryParams->flowID, $tokenDetails); } $headers = [ 'Accept' => 'application/json', 'Content-Type' => 'application/json;', - 'Authorization' => 'bearer ' . $queryParams->tokenDetails->access_token, + 'Authorization' => 'bearer ' . $tokenDetails->access_token, ]; $apiEndpoint = 'https://api.onedrive.com/v1.0/drives/' . $ids[0] . '/items/' . $queryParams->folder . '/children'; $apiResponse = HttpHelper::get($apiEndpoint, [], $headers); @@ -112,7 +125,7 @@ public static function singleOneDriveFolderList($queryParams) } } $response['folders'] = $data; - $response['tokenDetails'] = $token; + $response['tokenDetails'] = $tokenDetails; wp_send_json_success($response, 200); } @@ -129,11 +142,25 @@ public function execute($integrationData, $fieldValues) $actions = $integrationDetails->actions; $folderId = $integrationDetails->folder; // $fieldMap = $integrationDetails->field_map; - $tokenDetails = self::tokenExpiryCheck($integrationDetails->tokenDetails, $integrationDetails->clientId, $integrationDetails->clientSecret); + $isConnectionAuth = !empty($integrationDetails->connection_id); + $tokenDetails = self::normalizeConnectionToken($integrationDetails->tokenDetails ?? null); + $oldToken = $tokenDetails->access_token ?? ''; + + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $integrationDetails->clientId ?? '', $integrationDetails->clientSecret ?? ''); + } + + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + // translators: %s: Service name + LogHandler::save($this->integrationID, wp_json_encode(['type' => 'oneDrive', 'type_name' => 'file_upload']), 'error', wp_sprintf(__('Not Authorization By %s', 'bit-integrations'), 'OneDrive')); + + return false; + } // folderMap need check $parentId = $integrationData->flow_details->folderMap[1]; $fieldMap = null; - if ($tokenDetails->access_token !== $integrationDetails->tokenDetails->access_token) { + + if (!$isConnectionAuth && $tokenDetails->access_token !== $oldToken) { self::saveRefreshedToken($this->integrationID, $tokenDetails); } @@ -148,7 +175,9 @@ private static function tokenExpiryCheck($token, $clientId, $clientSecret) return false; } - if ((\intval($token->generates_on) + (55 * 60)) < time()) { + $generatedOn = !empty($token->generates_on) ? (int) $token->generates_on : (int) ($token->generated_at ?? 0); + + if ($generatedOn > 0 && ($generatedOn + (55 * 60)) < time()) { $refreshToken = self::refreshToken($token->refresh_token, $clientId, $clientSecret); if (is_wp_error($refreshToken) || !empty($refreshToken->error)) { return false; @@ -156,6 +185,8 @@ private static function tokenExpiryCheck($token, $clientId, $clientSecret) $token->access_token = $refreshToken->access_token; $token->expires_in = $refreshToken->expires_in; $token->generates_on = $refreshToken->generates_on; + $token->generated_at = $refreshToken->generated_at; + $token->refresh_token = $refreshToken->refresh_token; } return $token; @@ -177,6 +208,20 @@ private static function refreshToken($refresh_token, $clientId, $clientSecret) } $token = $apiResponse; $token->generates_on = time(); + $token->generated_at = $token->generates_on; + + return $token; + } + + private static function normalizeConnectionToken($token) + { + if (!\is_object($token)) { + $token = (object) []; + } + + if (empty($token->generates_on) && !empty($token->generated_at)) { + $token->generates_on = (int) $token->generated_at; + } return $token; } diff --git a/backend/Actions/OneDrive/Routes.php b/backend/Actions/OneDrive/Routes.php index 030066433..046e17d31 100644 --- a/backend/Actions/OneDrive/Routes.php +++ b/backend/Actions/OneDrive/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\OneDrive\OneDriveController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('oneDrive_authorization', [OneDriveController::class, 'authorization']); Route::post('oneDrive_get_all_folders', [OneDriveController::class, 'getAllFolders']); Route::post('oneDrive_get_single_folder', [OneDriveController::class, 'singleOneDriveFolderList']); diff --git a/backend/Actions/OneHashCRM/OneHashCRMController.php b/backend/Actions/OneHashCRM/OneHashCRMController.php index d7285d08b..1152fec3e 100644 --- a/backend/Actions/OneHashCRM/OneHashCRMController.php +++ b/backend/Actions/OneHashCRM/OneHashCRMController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\OneHashCRM; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,29 +15,22 @@ */ class OneHashCRMController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'onehashcrm', + 'fields' => [ + 'api_key' => 'value', + 'api_secret' => 'api_secret', + 'domain' => 'domain', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; protected $domain; - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->domain = $fieldsRequestParams->domain; - $apiKey = $fieldsRequestParams->api_key; - $apiSecret = $fieldsRequestParams->api_secret; - $apiEndpoint = $this->setApiEndpoint() . '/Lead'; - $headers = $this->setHeaders($apiKey, $apiSecret); - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (isset($response->data)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API Key & Secret or Access Api URL', 'bit-integrations'), 400); - } - } - public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; diff --git a/backend/Actions/OneHashCRM/Routes.php b/backend/Actions/OneHashCRM/Routes.php index 3cacbe6e3..b8636c828 100644 --- a/backend/Actions/OneHashCRM/Routes.php +++ b/backend/Actions/OneHashCRM/Routes.php @@ -6,5 +6,3 @@ use BitApps\Integrations\Actions\OneHashCRM\OneHashCRMController; use BitApps\Integrations\Core\Util\Route; - -Route::post('onehashcrm_authentication', [OneHashCRMController::class, 'authentication']); diff --git a/backend/Actions/PCloud/PCloudController.php b/backend/Actions/PCloud/PCloudController.php index 2ca7321f7..7377cf5fe 100644 --- a/backend/Actions/PCloud/PCloudController.php +++ b/backend/Actions/PCloud/PCloudController.php @@ -3,12 +3,23 @@ namespace BitApps\Integrations\Actions\PCloud; use BitApps\Integrations\Actions\PCloud\RecordApiHelper as PCloudRecordApiHelper; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Log\LogHandler; use WP_Error; class PCloudController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'pcloud', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; + private $integrationID; public function __construct($integrationID) @@ -16,37 +27,16 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function authorization($requestParams) - { - if (empty($requestParams->clientId) || empty($requestParams->clientSecret) || empty($requestParams->code)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $body = [ - 'client_id' => $requestParams->clientId, - 'client_secret' => $requestParams->clientSecret, - 'code' => $requestParams->code - ]; - - $apiEndpoint = 'https://api.pcloud.com/oauth2_token'; - $header['Content-Type'] = 'application/x-www-form-urlencoded'; - $apiResponse = HttpHelper::post($apiEndpoint, $body, $header); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error(empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, 400); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public static function getAllFolders($queryParams) { - if (empty($queryParams->tokenDetails)) { + $accessToken = $queryParams->tokenDetails->access_token ?? ($queryParams->access_token ?? ''); + + if (empty($accessToken)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } $apiEndpoint = 'https://api.pcloud.com/listfolder?folderid=0'; - $header['Authorization'] = 'Bearer ' . $queryParams->tokenDetails->access_token; + $header['Authorization'] = 'Bearer ' . $accessToken; $apiResponse = HttpHelper::get($apiEndpoint, null, $header); @@ -67,7 +57,10 @@ public static function getAllFolders($queryParams) public function execute($integrationData, $fieldValues) { - if (empty($integrationData->flow_details->tokenDetails->access_token)) { + $accessToken = $integrationData->flow_details->tokenDetails->access_token + ?? ($integrationData->flow_details->access_token ?? ''); + + if (empty($accessToken)) { // translators: %s: Service name LogHandler::save($this->integrationID, wp_json_encode(['type' => 'pCloud', 'type_name' => 'file_upload']), 'error', wp_sprintf(__('Not Authorization By %s', 'bit-integrations'), 'PCloud')); @@ -77,7 +70,6 @@ public function execute($integrationData, $fieldValues) $integrationDetails = $integrationData->flow_details; $actions = $integrationDetails->actions; $fieldMap = $integrationDetails->field_map; - $accessToken = $integrationDetails->tokenDetails->access_token; if (empty($fieldMap)) { $error = new WP_Error('REQ_FIELD_EMPTY', __('Required fields not mapped', 'bit-integrations')); diff --git a/backend/Actions/PCloud/Routes.php b/backend/Actions/PCloud/Routes.php index ffd20e1dd..822dbbaf0 100644 --- a/backend/Actions/PCloud/Routes.php +++ b/backend/Actions/PCloud/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\PCloud\PCloudController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('pCloud_authorization', [PCloudController::class, 'authorization']); Route::post('pCloud_get_all_folders', [PCloudController::class, 'getAllFolders']); diff --git a/backend/Actions/PaidMembershipPro/PaidMembershipProController.php b/backend/Actions/PaidMembershipPro/PaidMembershipProController.php index 03d55232f..fd0571845 100644 --- a/backend/Actions/PaidMembershipPro/PaidMembershipProController.php +++ b/backend/Actions/PaidMembershipPro/PaidMembershipProController.php @@ -16,15 +16,6 @@ public static function pluginActive($option = null) return false; } - public static function authorizeMemberpress() - { - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'Paid Membership')); - } - public static function getAllPaidMembershipProLevel() { global $wpdb; diff --git a/backend/Actions/PaidMembershipPro/Routes.php b/backend/Actions/PaidMembershipPro/Routes.php index 3d42c0bab..d9a2eb876 100644 --- a/backend/Actions/PaidMembershipPro/Routes.php +++ b/backend/Actions/PaidMembershipPro/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\PaidMembershipPro\PaidMembershipProController; use BitApps\Integrations\Core\Util\Route; -Route::post('paid_membership_pro_authorize', [PaidMembershipProController::class, 'authorizeMemberpress']); Route::post('fetch_all_paid_membership_pro_level', [PaidMembershipProController::class, 'getAllPaidMembershipProLevel']); diff --git a/backend/Actions/PeepSo/PeepSoController.php b/backend/Actions/PeepSo/PeepSoController.php index 77fa89a82..e27348989 100644 --- a/backend/Actions/PeepSo/PeepSoController.php +++ b/backend/Actions/PeepSo/PeepSoController.php @@ -26,12 +26,6 @@ public static function isExists() } } - public static function peepSoAuthorize() - { - self::isExists(); - wp_send_json_success(true); - } - public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; diff --git a/backend/Actions/PeepSo/Routes.php b/backend/Actions/PeepSo/Routes.php deleted file mode 100644 index 1c41afea4..000000000 --- a/backend/Actions/PeepSo/Routes.php +++ /dev/null @@ -1,10 +0,0 @@ - AuthorizationType::API_KEY, + 'slug' => 'perfexcrm', + 'fields' => [ + 'api_token' => 'value', + 'domain' => 'domain', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; protected $domain; - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->domain = $fieldsRequestParams->domain; - $apiToken = $fieldsRequestParams->api_token; - $apiEndpoint = $this->setApiEndpoint() . '/staffs'; - $headers = $this->setHeaders($apiToken); - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (\is_string($response) || isset($response->errors) || (isset($response->status) && !$response->status)) { - wp_send_json_error(\is_string($response) ? $response : (!empty($response->message) ? $response->message : 'Please enter valid API Token or Access Api URL'), 400); - } else { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } - } - public function getCustomFields($fieldsRequestParams) { $this->checkValidation($fieldsRequestParams, $fieldsRequestParams->action_name); diff --git a/backend/Actions/PerfexCRM/Routes.php b/backend/Actions/PerfexCRM/Routes.php index b5c2ffbb9..03ec63b20 100644 --- a/backend/Actions/PerfexCRM/Routes.php +++ b/backend/Actions/PerfexCRM/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\PerfexCRM\PerfexCRMController; use BitApps\Integrations\Core\Util\Route; -Route::post('perfexcrm_authentication', [PerfexCRMController::class, 'authentication']); Route::post('perfexcrm_custom_fields', [PerfexCRMController::class, 'getCustomFields']); Route::post('perfexcrm_fetch_all_customers', [PerfexCRMController::class, 'getAllCustomer']); Route::post('perfexcrm_fetch_all_leads', [PerfexCRMController::class, 'getAllLead']); diff --git a/backend/Actions/PipeDrive/PipeDriveController.php b/backend/Actions/PipeDrive/PipeDriveController.php index 47932fe95..fb79ce073 100644 --- a/backend/Actions/PipeDrive/PipeDriveController.php +++ b/backend/Actions/PipeDrive/PipeDriveController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\PipeDrive; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Config; use BitApps\Integrations\Core\Util\Hooks; use BitApps\Integrations\Core\Util\HttpHelper; @@ -16,11 +17,20 @@ */ class PipeDriveController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'pipedrive', + 'fields' => [ + 'api_key' => 'value', + ], + ]; + private $baseUrl = 'https://api.pipedrive.com/v1/'; public function getMetaData($requestParams) { - if (empty($requestParams->api_key)) { + $apiKey = !empty($requestParams->api_key) ? $requestParams->api_key : (!empty($requestParams->value) ? $requestParams->value : ''); + if (empty($apiKey)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -30,7 +40,7 @@ public function getMetaData($requestParams) ); } - $apiEndpoints = $this->baseUrl . $requestParams->type . '?api_token=' . $requestParams->api_key; + $apiEndpoints = $this->baseUrl . $requestParams->type . '?api_token=' . $apiKey; $response = HttpHelper::get($apiEndpoints, null); $formattedResponse = []; @@ -63,7 +73,8 @@ public function getMetaData($requestParams) public function getFields($requestParams) { - if (empty($requestParams->api_key)) { + $apiKey = !empty($requestParams->api_key) ? $requestParams->api_key : (!empty($requestParams->value) ? $requestParams->value : ''); + if (empty($apiKey)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -106,7 +117,7 @@ public function getFields($requestParams) ]; - $apiEndpoints = $this->baseUrl . $requestModule . '?limit=500&api_token=' . $requestParams->api_key; + $apiEndpoints = $this->baseUrl . $requestModule . '?limit=500&api_token=' . $apiKey; $response = HttpHelper::get($apiEndpoints, null); $formattedResponse = []; @@ -160,7 +171,7 @@ public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; - $api_key = $integrationDetails->api_key; + $api_key = !empty($integrationDetails->api_key) ? $integrationDetails->api_key : (isset($integrationDetails->value) ? $integrationDetails->value : ''); $fieldMap = $integrationDetails->field_map; $module = strtolower($integrationDetails->moduleData->module); @@ -183,13 +194,13 @@ public function execute($integrationData, $fieldValues) } if (isset($pipeDriveApiResponse->success, $pipeDriveApiResponse->data) && $pipeDriveApiResponse->success && \count($integrationDetails->relatedlists)) { - Hooks::run(Config::withPrefix('pipedrive_store_related_list'), $pipeDriveApiResponse, $integrationDetails, $fieldValues, $module, $integrationDetails->api_key, $integId); + Hooks::run(Config::withPrefix('pipedrive_store_related_list'), $pipeDriveApiResponse, $integrationDetails, $fieldValues, $module, $api_key, $integId); /** * @deprecated 2.7.8 Use `bit_integrations_pipedrive_store_related_list` action instead. * @since 2.7.8 */ - Hooks::run('btcbi_pipedrive_store_related_list', $pipeDriveApiResponse, $integrationDetails, $fieldValues, $module, $integrationDetails->api_key, $integId); + Hooks::run('btcbi_pipedrive_store_related_list', $pipeDriveApiResponse, $integrationDetails, $fieldValues, $module, $api_key, $integId); } return $pipeDriveApiResponse; diff --git a/backend/Actions/PropovoiceCRM/PropovoiceCRMController.php b/backend/Actions/PropovoiceCRM/PropovoiceCRMController.php index ecbc9807e..ab852a4ab 100644 --- a/backend/Actions/PropovoiceCRM/PropovoiceCRMController.php +++ b/backend/Actions/PropovoiceCRM/PropovoiceCRMController.php @@ -13,16 +13,6 @@ public static function pluginActive() ; } - public static function authorizePropovoiceCrm() - { - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - // translators: %s: Placeholder value - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'Propovoice CRM')); - } - public static function leadTags() { global $wpdb; diff --git a/backend/Actions/PropovoiceCRM/Routes.php b/backend/Actions/PropovoiceCRM/Routes.php index 1fa0e61eb..db9d0e136 100644 --- a/backend/Actions/PropovoiceCRM/Routes.php +++ b/backend/Actions/PropovoiceCRM/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\PropovoiceCRM\PropovoiceCRMController; use BitApps\Integrations\Core\Util\Route; -Route::post('propovoice_authorize', [PropovoiceCRMController::class, 'authorizePropovoiceCrm']); Route::post('propovoice_crm_lead_tags', [PropovoiceCRMController::class, 'leadTags']); Route::post('propovoice_crm_lead_label', [PropovoiceCRMController::class, 'leadLabel']); diff --git a/backend/Actions/Rapidmail/RapidmailController.php b/backend/Actions/Rapidmail/RapidmailController.php index 38768a68a..ddaaf202d 100644 --- a/backend/Actions/Rapidmail/RapidmailController.php +++ b/backend/Actions/Rapidmail/RapidmailController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Rapidmail; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Actions\Rapidmail\RecordApiHelper as RapidmailRecordApiHelper; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Log\LogHandler; @@ -15,6 +16,15 @@ final class RapidmailController { public static $apiBaseUri = 'https://apiv3.emailsys.net/v1'; + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'rapidmail', + 'fields' => [ + 'username' => 'username', + 'password' => 'password', + ], + ]; + protected $_defaultHeader; private $_integrationID; @@ -24,40 +34,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - public static function checkAuthorization($tokenRequestParams) - { - if ( - empty($tokenRequestParams->username) - || empty($tokenRequestParams->password) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $header = [ - 'Authorization' => 'Basic ' . base64_encode("{$tokenRequestParams->username}:{$tokenRequestParams->password}"), - 'Accept' => '*/*', - 'verify' => false - ]; - $apiEndpoint = self::$apiBaseUri . '/apiusers'; - - $apiResponse = HttpHelper::get($apiEndpoint, null, $header); - if (!(property_exists($apiResponse, '_embedded') && property_exists($apiResponse->_embedded, 'apiusers'))) { - wp_send_json_error( - // empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 'Unauthorize', - 400 - ); - } else { - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - } - /** * Process request for getting recipientlists from rapidmail * diff --git a/backend/Actions/Rapidmail/Routes.php b/backend/Actions/Rapidmail/Routes.php index fdf011292..63edd3ae9 100644 --- a/backend/Actions/Rapidmail/Routes.php +++ b/backend/Actions/Rapidmail/Routes.php @@ -8,6 +8,5 @@ use BitApps\Integrations\Core\Util\Route; // Rapidmail -Route::post('rapidmail_authorization', [RapidmailController::class, 'checkAuthorization']); Route::get('rapidmail_get_all_recipients', [RapidmailController::class, 'getAllRecipients']); Route::get('rapidmail_get_all_fields', [RapidmailController::class, 'getAllFields']); diff --git a/backend/Actions/RestrictContent/RestrictContentController.php b/backend/Actions/RestrictContent/RestrictContentController.php index 234b9fcdb..e038e15ca 100644 --- a/backend/Actions/RestrictContent/RestrictContentController.php +++ b/backend/Actions/RestrictContent/RestrictContentController.php @@ -33,17 +33,6 @@ public static function pluginActive($option = null) return false; } - public static function authorizeRestrictContent() - { - include_once ABSPATH . 'wp-admin/includes/plugin.php'; - if (self::pluginActive()) { - wp_send_json_success(true, 200); - } - // translators: %s: Plugin name - // translators: %s: Placeholder value - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'Restrict Content')); - } - public static function getAllLevels() { $levels = rcp_get_membership_levels(['number' => 999]); diff --git a/backend/Actions/RestrictContent/Routes.php b/backend/Actions/RestrictContent/Routes.php index aea4a11bb..c6df4993e 100644 --- a/backend/Actions/RestrictContent/Routes.php +++ b/backend/Actions/RestrictContent/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\RestrictContent\RestrictContentController; use BitApps\Integrations\Core\Util\Route; -Route::post('restrict_authorize', [RestrictContentController::class, 'authorizeRestrictContent']); Route::get('restrict_get_all_levels', [RestrictContentController::class, 'getAllLevels']); diff --git a/backend/Actions/Salesflare/Routes.php b/backend/Actions/Salesflare/Routes.php index f6148765a..2390a216b 100644 --- a/backend/Actions/Salesflare/Routes.php +++ b/backend/Actions/Salesflare/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Salesflare\SalesflareController; use BitApps\Integrations\Core\Util\Route; -Route::post('salesflare_authentication', [SalesflareController::class, 'authentication']); Route::post('Salesflare_custom_fields', [SalesflareController::class, 'customFields']); Route::post('Salesflare_fetch_all_tags', [SalesflareController::class, 'getAllTags']); Route::post('Salesflare_fetch_all_account', [SalesflareController::class, 'getAllAccounts']); diff --git a/backend/Actions/Salesflare/SalesflareController.php b/backend/Actions/Salesflare/SalesflareController.php index d0292ff41..cdb574613 100644 --- a/backend/Actions/Salesflare/SalesflareController.php +++ b/backend/Actions/Salesflare/SalesflareController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Salesflare; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,27 +15,20 @@ */ class SalesflareController { + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'salesflare', + 'fields' => [ + 'api_key' => 'token', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; protected $domain; - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $apiKey = $fieldsRequestParams->api_key; - $apiEndpoint = $this->setApiEndpoint() . '/accounts'; - $headers = $this->setHeaders($apiKey); - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (!isset($response->error)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API key', 'bit-integrations'), 400); - } - } - public function customFields($fieldsRequestParams) { $this->checkValidation($fieldsRequestParams, $fieldsRequestParams->action_name); diff --git a/backend/Actions/Salesforce/Routes.php b/backend/Actions/Salesforce/Routes.php index 81e9b97ff..59966dbaf 100644 --- a/backend/Actions/Salesforce/Routes.php +++ b/backend/Actions/Salesforce/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Salesforce\SalesforceController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('selesforce_generate_token', [SalesforceController::class, 'generateTokens']); Route::post('selesforce_custom_action', [SalesforceController::class, 'customActions']); Route::post('selesforce_campaign_list', [SalesforceController::class, 'selesforceCampaignList']); Route::post('selesforce_lead_list', [SalesforceController::class, 'selesforceLeadList']); diff --git a/backend/Actions/Salesforce/SalesforceController.php b/backend/Actions/Salesforce/SalesforceController.php index bab7d9381..5cc029784 100644 --- a/backend/Actions/Salesforce/SalesforceController.php +++ b/backend/Actions/Salesforce/SalesforceController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Salesforce; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Config; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Core\Util\Hooks; @@ -14,6 +15,16 @@ class SalesforceController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'salesforce', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'instance_url']], + ], + ]; + public static $actions = [ 'contact-create' => 'Contact', 'lead-create' => 'Lead', @@ -27,47 +38,6 @@ class SalesforceController private $_integrationID; - public static function generateTokens($requestsParams) - { - if ( - empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = 'https://login.salesforce.com/services/oauth2/token?grant_type=authorization_code&client_id=' . $requestsParams->clientId . '&client_secret=' . $requestsParams->clientSecret . '&redirect_uri=' . $requestsParams->redirectURI . '&code=' . $requestsParams->code; - $requestParams = [ - 'grant_type' => 'authorization_code', - 'code' => explode('#', $requestsParams->code)[0], - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => urldecode($requestsParams->redirectURI), - 'format' => 'json', - ]; - - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - - $apiResponse->generates_on = time(); - - wp_send_json_success($apiResponse, 200); - } - public function customActions($params) { if ( @@ -423,7 +393,7 @@ public function execute($integrationData, $fieldValues) return new WP_Error('REQ_FIELD_EMPTY', __('list are required for zoho desk api', 'bit-integrations')); } - if ((\intval($tokenDetails->generates_on) + (55 * 60)) < time()) { + if (self::isTokenExpired($tokenDetails)) { $newTokenDetails = self::refreshAccessToken((object) [ 'clientId' => $integrationDetails->clientId, 'clientSecret' => $integrationDetails->clientSecret, @@ -456,7 +426,7 @@ public static function refreshTokenDetails($params) { $response = ['tokenDetails' => $params->tokenDetails]; - if ((\intval($params->tokenDetails->generates_on) + (55 * 60)) < time()) { + if (self::isTokenExpired($params->tokenDetails)) { $response['tokenDetails'] = self::refreshAccessToken($params); } @@ -518,8 +488,16 @@ protected static function refreshAccessToken($apiData) return false; } - $tokenDetails->generates_on = time(); + $generatedAt = time(); + $tokenDetails->generates_on = $generatedAt; + $tokenDetails->generated_at = $generatedAt; $tokenDetails->access_token = $apiResponse->access_token; + if (!empty($apiResponse->refresh_token)) { + $tokenDetails->refresh_token = $apiResponse->refresh_token; + } + if (isset($apiResponse->expires_in)) { + $tokenDetails->expires_in = $apiResponse->expires_in; + } return $tokenDetails; } @@ -536,6 +514,22 @@ private static function isRequiredField($key, $action) return \in_array($key, $requiredFields[$action] ?? ['Name']); } + private static function isTokenExpired($tokenDetails) + { + if (empty($tokenDetails) || !\is_object($tokenDetails)) { + return false; + } + + $generatedAt = empty($tokenDetails->generates_on) ? ($tokenDetails->generated_at ?? 0) : $tokenDetails->generates_on; + $expiresIn = $tokenDetails->expires_in ?? 0; + + if (!empty($generatedAt) && !empty($expiresIn) && (int) $expiresIn > 0) { + return ((int) $generatedAt + (int) $expiresIn - 30) < time(); + } + + return ((int) $generatedAt + (55 * 60)) < time(); + } + private static function getCaseMetaData($params, $module) { if ( diff --git a/backend/Actions/Salesmate/Routes.php b/backend/Actions/Salesmate/Routes.php index a39b62e9d..ea7d91c98 100644 --- a/backend/Actions/Salesmate/Routes.php +++ b/backend/Actions/Salesmate/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Salesmate\SalesmateController; use BitApps\Integrations\Core\Util\Route; -Route::post('salesmate_authentication', [SalesmateController::class, 'authentication']); Route::post('Salesmate_fields', [SalesmateController::class, 'getAllFields']); Route::post('salesmate_fetch_all_CRMSources', [SalesmateController::class, 'getAllCRMSources']); Route::post('salesmate_fetch_all_currencies', [SalesmateController::class, 'getAllCurrencies']); diff --git a/backend/Actions/Salesmate/SalesmateController.php b/backend/Actions/Salesmate/SalesmateController.php index e487d2fb4..8aa05b993 100644 --- a/backend/Actions/Salesmate/SalesmateController.php +++ b/backend/Actions/Salesmate/SalesmateController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Salesmate; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,29 +15,21 @@ */ class SalesmateController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'salesmate', + 'fields' => [ + 'session_token' => 'value', + 'link_name' => 'link_name', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; protected $linkName; - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $sessionToken = $fieldsRequestParams->session_token; - $this->linkName = $fieldsRequestParams->link_name; - $apiEndpoint = $this->setApiEndpoint() . 'v1/users/active'; - - $headers = $this->setHeaders($sessionToken); - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (isset($response->Status) && $response->Status === 'success') { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid Session Token or Link Name', 'bit-integrations'), 400); - } - } - public function getAllFields($fieldsRequestParams) { $this->checkValidation($fieldsRequestParams, $fieldsRequestParams->action_id); diff --git a/backend/Actions/Selzy/Routes.php b/backend/Actions/Selzy/Routes.php index 646082810..d5a8cbabe 100644 --- a/backend/Actions/Selzy/Routes.php +++ b/backend/Actions/Selzy/Routes.php @@ -7,6 +7,6 @@ use BitApps\Integrations\Actions\Selzy\SelzyController; use BitApps\Integrations\Core\Util\Route; -Route::post('selzy_handle_authorize', [SelzyController::class, 'handleAuthorize']); +Route::post('selzy_get_all_lists', [SelzyController::class, 'getAllLists']); Route::post('selzy_get_all_tags', [SelzyController::class, 'getAllTags']); Route::post('selzy_get_all_custom_fields', [SelzyController::class, 'getAllCustomFields']); diff --git a/backend/Actions/Selzy/SelzyController.php b/backend/Actions/Selzy/SelzyController.php index b9aca8ba2..4be850b00 100644 --- a/backend/Actions/Selzy/SelzyController.php +++ b/backend/Actions/Selzy/SelzyController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Selzy; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -16,9 +17,19 @@ class SelzyController { private $baseUrl = 'https://api.selzy.com/en/api/'; - public function handleAuthorize($requestParams) + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'selzy', + 'fields' => [ + 'authKey' => 'value', + 'api_key' => 'value', + ], + ]; + + public function getAllLists($requestParams) { - if (empty($requestParams->authKey)) { + $apiKey = $this->resolveApiKey($requestParams); + if (empty($apiKey)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -27,7 +38,7 @@ public function handleAuthorize($requestParams) 400 ); } - $apiEndpoints = $this->baseUrl . 'getLists?format=json&api_key=' . $requestParams->authKey; + $apiEndpoints = $this->baseUrl . 'getLists?format=json&api_key=' . $apiKey; $response = HttpHelper::get($apiEndpoints, null); if ($response->code === 'invalid_api_key') { wp_send_json_error( @@ -43,7 +54,8 @@ public function handleAuthorize($requestParams) public function getAllTags($requestParams) { - if (empty($requestParams->authKey)) { + $apiKey = $this->resolveApiKey($requestParams); + if (empty($apiKey)) { wp_send_json_error( __( 'Requested parameter is empty', @@ -52,7 +64,7 @@ public function getAllTags($requestParams) 400 ); } - $apiEndpoints = $this->baseUrl . 'getTags?format=json&api_key=' . $requestParams->authKey; + $apiEndpoints = $this->baseUrl . 'getTags?format=json&api_key=' . $apiKey; $response = HttpHelper::get($apiEndpoints, null); if ($response->code === 'invalid_api_key') { wp_send_json_error( @@ -68,11 +80,12 @@ public function getAllTags($requestParams) public function getAllCustomFields($requestParams) { - if (empty($requestParams->authKey)) { + $apiKey = $this->resolveApiKey($requestParams); + if (empty($apiKey)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $apiEndpoint = "https://api.selzy.com/en/api/getFields?format=json&api_key={$requestParams->authKey}"; + $apiEndpoint = "https://api.selzy.com/en/api/getFields?format=json&api_key={$apiKey}"; $response = HttpHelper::get($apiEndpoint, null); @@ -96,7 +109,7 @@ public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integId = $integrationData->id; - $authKey = $integrationDetails->authKey; + $authKey = $integrationDetails->authKey ?: ($integrationDetails->api_key ?: ($integrationDetails->value ?? '')); $listIds = $integrationDetails->listIds; $tags = $integrationDetails->tags; $method = $integrationDetails->method; @@ -137,4 +150,9 @@ public function execute($integrationData, $fieldValues) return $selzyApiResponse; } + + private function resolveApiKey($requestParams) + { + return $requestParams->authKey ?: ($requestParams->api_key ?: ($requestParams->value ?? '')); + } } diff --git a/backend/Actions/SendFox/Routes.php b/backend/Actions/SendFox/Routes.php index dc5aeb632..8da76d7d2 100644 --- a/backend/Actions/SendFox/Routes.php +++ b/backend/Actions/SendFox/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\SendFox\SendFoxController; use BitApps\Integrations\Core\Util\Route; -Route::post('sendFox_authorize', [SendFoxController::class, 'sendFoxAuthorize']); Route::post('sendfox_fetch_all_list', [SendFoxController::class, 'fetchContactLists']); diff --git a/backend/Actions/SendFox/SendFoxController.php b/backend/Actions/SendFox/SendFoxController.php index 4a3ebb175..df5c24c0a 100644 --- a/backend/Actions/SendFox/SendFoxController.php +++ b/backend/Actions/SendFox/SendFoxController.php @@ -6,41 +6,21 @@ namespace BitApps\Integrations\Actions\SendFox; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; class SendFoxController { - private $baseUrl = 'https://api.sendfox.com/'; - - public function sendFoxAuthorize($requestParams) - { - if (empty($requestParams->access_token)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $apiEndpoints = $this->baseUrl . 'me'; - - $requestParams = [ - 'Authorization' => "Bearer {$requestParams->access_token}", - 'Accept' => 'application/json', - ]; + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'sendfox', + 'fields' => [ + 'access_token' => 'token', + ], + ]; - $response = HttpHelper::get($apiEndpoints, null, $requestParams); - if ($response->message !== 'Unauthenticated.') { - wp_send_json_success($response, 200); - } else { - wp_send_json_error( - 'The token is invalid', - 400 - ); - } - } + private $baseUrl = 'https://api.sendfox.com/'; public function fetchContactLists($requestParams) { diff --git a/backend/Actions/SendGrid/Routes.php b/backend/Actions/SendGrid/Routes.php index f639a9bde..8469e4be0 100644 --- a/backend/Actions/SendGrid/Routes.php +++ b/backend/Actions/SendGrid/Routes.php @@ -7,5 +7,5 @@ use BitApps\Integrations\Actions\SendGrid\SendGridController; use BitApps\Integrations\Core\Util\Route; -Route::post('sendGrid_authentication', [SendGridController::class, 'authentication']); +Route::post('sendGrid_fetch_custom_fields', [SendGridController::class, 'getCustomFields']); Route::post('sendGrid_fetch_all_lists', [SendGridController::class, 'getLists']); diff --git a/backend/Actions/SendGrid/SendGridController.php b/backend/Actions/SendGrid/SendGridController.php index 3854ac84f..e6d24b054 100644 --- a/backend/Actions/SendGrid/SendGridController.php +++ b/backend/Actions/SendGrid/SendGridController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\SendGrid; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,7 +15,15 @@ */ class SendGridController { - public function authentication($fieldsRequestParams) + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'sendgrid', + 'fields' => [ + 'apiKey' => 'token', + ], + ]; + + public function getCustomFields($fieldsRequestParams) { if (empty($fieldsRequestParams->apiKey)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); @@ -28,6 +37,7 @@ public function authentication($fieldsRequestParams) $response = HttpHelper::get($apiEndpoints, null, $header); if (!isset($response->errors)) { + $customFields = []; foreach ($response->custom_fields as $customField) { $customFields[] = [ 'key' => $customField->id, @@ -37,7 +47,7 @@ public function authentication($fieldsRequestParams) } wp_send_json_success($customFields, 200); } else { - wp_send_json_error($response->errors[0]->message ?? __('Please enter valid API key', 'bit-integrations'), 400); + wp_send_json_error($response->errors[0]->message ?? __('Custom fields fetch failed', 'bit-integrations'), 400); } } diff --git a/backend/Actions/SendPulse/Routes.php b/backend/Actions/SendPulse/Routes.php index a7aa12caa..273180be6 100644 --- a/backend/Actions/SendPulse/Routes.php +++ b/backend/Actions/SendPulse/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\SendPulse\SendPulseController; use BitApps\Integrations\Core\Util\Route; -Route::post('sendPulse_authorize', [SendPulseController::class, 'authorization']); Route::post('sendPulse_lists', [SendPulseController::class, 'getAllList']); Route::post('sendPulse_headers', [SendPulseController::class, 'sendPulseHeaders']); diff --git a/backend/Actions/SendPulse/SendPulseController.php b/backend/Actions/SendPulse/SendPulseController.php index 886fc03cb..fa39d73be 100644 --- a/backend/Actions/SendPulse/SendPulseController.php +++ b/backend/Actions/SendPulse/SendPulseController.php @@ -2,6 +2,7 @@ namespace BitApps\Integrations\Actions\SendPulse; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Config; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Core\Util\Hooks; @@ -10,6 +11,16 @@ class SendPulseController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'sendpulse', + 'fields' => [ + 'client_id' => 'client_id', + 'client_secret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'expires_in', 'generated_at', 'generates_on']], + ], + ]; + private $integrationID; public function __construct($integrationID) @@ -17,30 +28,6 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function authorization($requestParams) - { - if (empty($requestParams->client_id) || empty($requestParams->client_secret)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $body = [ - 'grant_type' => 'client_credentials', - 'client_id' => $requestParams->client_id, - 'client_secret' => $requestParams->client_secret, - ]; - - $apiEndpoint = 'https://api.sendpulse.com/oauth/access_token'; - - $apiResponse = HttpHelper::post($apiEndpoint, $body); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error(empty($apiResponse->error_description) ? 'Unknown' : $apiResponse->error_description, 400); - } - $apiResponse->generates_on = time(); - - wp_send_json_success($apiResponse, 200); - } - public static function sendPulseHeaders($requestParams) { if (empty($requestParams->client_id) || empty($requestParams->client_secret) @@ -110,7 +97,15 @@ public function execute($integrationData, $fieldValues) $selectedList = $integrationDetails->listId; $fieldMap = $integrationDetails->field_map; $tokenDetails = self::tokenExpiryCheck($integrationDetails->tokenDetails, $integrationData->flow_details->client_id, $integrationData->flow_details->client_secret); - if ($tokenDetails->access_token !== $integrationDetails->tokenDetails->access_token) { + + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + return new WP_Error('TOKEN_REFRESH_FAILED', __('Token refresh failed for SendPulse', 'bit-integrations')); + } + + if ( + empty($integrationDetails->connection_id) + && $tokenDetails->access_token !== $integrationDetails->tokenDetails->access_token + ) { $this->saveRefreshedToken($this->integrationID, $tokenDetails); } @@ -140,7 +135,11 @@ protected static function tokenExpiryCheck($token, $clientId, $clientSecret) return false; } - if ((\intval($token->generates_on) + (55 * 60)) < time()) { + $generatedOn = isset($token->generates_on) && $token->generates_on !== '' + ? \intval($token->generates_on) + : \intval($token->generated_at ?? 0); + + if (($generatedOn + (55 * 60)) < time()) { $refreshToken = self::refreshToken($clientId, $clientSecret); if (is_wp_error($refreshToken) || !empty($refreshToken->error)) { return false; @@ -148,6 +147,7 @@ protected static function tokenExpiryCheck($token, $clientId, $clientSecret) $token->access_token = $refreshToken->access_token; $token->expires_in = $refreshToken->expires_in; + $token->generated_at = $refreshToken->generated_at; $token->generates_on = $refreshToken->generates_on; } @@ -169,6 +169,7 @@ protected static function refreshToken($clientId, $clientSecret) return false; } $token = $apiResponse; + $token->generated_at = time(); $token->generates_on = time(); return $token; diff --git a/backend/Actions/SendinBlue/Routes.php b/backend/Actions/SendinBlue/Routes.php index 66a3c1534..781737742 100644 --- a/backend/Actions/SendinBlue/Routes.php +++ b/backend/Actions/SendinBlue/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\SendinBlue\SendinBlueController; use BitApps\Integrations\Core\Util\Route; -Route::post('sblue_authorize', [SendinBlueController::class, 'sendinBlueAuthorize']); Route::post('sblue_refresh_lists', [SendinBlueController::class, 'refreshlists']); Route::post('sblue_headers', [SendinBlueController::class, 'sendinblueHeaders']); Route::post('sblue_refresh_template', [SendinBlueController::class, 'refreshTemplate']); diff --git a/backend/Actions/SendinBlue/SendinBlueController.php b/backend/Actions/SendinBlue/SendinBlueController.php index 4b5476a94..95225d236 100644 --- a/backend/Actions/SendinBlue/SendinBlueController.php +++ b/backend/Actions/SendinBlue/SendinBlueController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\SendinBlue; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -16,39 +17,13 @@ class SendinBlueController { public const APIENDPOINT = 'https://api.sendinblue.com/v3'; - /** - * Process ajax request for generate_token - * - * @param object $requestsParams Params to Authorize - * - * @return JSON zoho crm api response and status - */ - public static function sendinBlueAuthorize($requestsParams) - { - if (empty($requestsParams->api_key)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = self::APIENDPOINT . '/account'; - $authorizationHeader['Accept'] = 'application/json'; - $authorizationHeader['api-key'] = $requestsParams->api_key; - $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); - - if (is_wp_error($apiResponse) || $apiResponse->code === 'unauthorized') { - wp_send_json_error( - empty($apiResponse->code) ? 'Unknown' : $apiResponse->message, - 400 - ); - } - - wp_send_json_success(true); - } + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'sendinblue', + 'fields' => [ + 'api_key' => 'value', + ], + ]; /** * Process ajax request for refresh crm modules diff --git a/backend/Actions/Sendy/RecordApiHelper.php b/backend/Actions/Sendy/RecordApiHelper.php index 7282f9c40..11853db88 100644 --- a/backend/Actions/Sendy/RecordApiHelper.php +++ b/backend/Actions/Sendy/RecordApiHelper.php @@ -60,7 +60,7 @@ public function execute($integId, $integrationDetails, $fieldValues, $fieldMap, $listId = $integrationDetails->list_id; $sendyUrl = $integrationDetails->sendy_url; - $apiKey = $integrationDetails->api_key; + $apiKey = $integrationDetails->api_key ?: ($integrationDetails->value ?? ''); $finalData['list'] = $listId; $finalData['boolean'] = true; $finalData['api_key'] = $apiKey; diff --git a/backend/Actions/Sendy/Routes.php b/backend/Actions/Sendy/Routes.php index 615dfb60d..b6c25277d 100644 --- a/backend/Actions/Sendy/Routes.php +++ b/backend/Actions/Sendy/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Sendy\SendyController; use BitApps\Integrations\Core\Util\Route; -Route::post('sendy_authorize', [SendyController::class, 'sendyAuthorize']); Route::post('get_all_brands', [SendyController::class, 'getAllBrands']); Route::post('get_all_lists_from_sendy', [SendyController::class, 'getAllLists']); // Route::post('mautic_get_fields', [ MauticController::class, 'getAllFields']); diff --git a/backend/Actions/Sendy/SendyController.php b/backend/Actions/Sendy/SendyController.php index 799e4fa32..b669e72cf 100644 --- a/backend/Actions/Sendy/SendyController.php +++ b/backend/Actions/Sendy/SendyController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Sendy; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Log\LogHandler; use WP_Error; @@ -15,46 +16,23 @@ */ class SendyController { - /** - * Process ajax request for generate_token. - * - * @param object $requestsParams Params for generate token - * - * @return JSON zoho crm api response and status - */ - public static function sendyAuthorize($requestsParams) - { - if (empty($requestsParams->api_key) || empty($requestsParams->sendy_url)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = "{$requestsParams->sendy_url}/api/brands/get-brands.php"; - $authorizationHeader = ['Accept' => 'application/json']; - $requestsParams = ['api_key' => $requestsParams->api_key]; - - $apiResponse = HttpHelper::post($apiEndpoint, $requestsParams, $authorizationHeader); - - if (HttpHelper::$responseCode !== 200 && (is_wp_error($apiResponse) || !\is_array($apiResponse))) { - wp_send_json_error( - empty($apiResponse->message) ? $apiResponse : $apiResponse->message, - 400 - ); - } - - wp_send_json_success(true); - } + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'sendy', + 'fields' => [ + 'api_key' => 'value', + 'sendy_url' => 'sendy_url', + ], + ]; public function getAllBrands($queryParams) { + $apiKey = $queryParams->api_key ?: ($queryParams->value ?? ''); + $sendy_url = $queryParams->sendy_url ?? ''; + if ( - empty($queryParams->api_key) - || empty($queryParams->sendy_url) + empty($apiKey) + || empty($sendy_url) ) { wp_send_json_error( __( @@ -64,8 +42,6 @@ public function getAllBrands($queryParams) 400 ); } - $apiKey = $queryParams->api_key; - $sendy_url = $queryParams->sendy_url; $apiEndpoint = "{$sendy_url}/api/brands/get-brands.php"; $authorizationHeader['Accept'] = 'application/json'; $requestsParams = [ @@ -85,9 +61,12 @@ public function getAllBrands($queryParams) public function getAllLists($queryParams) { + $apiKey = $queryParams->api_key ?: ($queryParams->value ?? ''); + $sendy_url = $queryParams->sendy_url ?? ''; + if ( - empty($queryParams->api_key) - || empty($queryParams->sendy_url) + empty($apiKey) + || empty($sendy_url) ) { wp_send_json_error( __( @@ -97,8 +76,6 @@ public function getAllLists($queryParams) 400 ); } - $apiKey = $queryParams->api_key; - $sendy_url = $queryParams->sendy_url; $brand_id = $queryParams->brand_id; $apiEndpoint = "{$sendy_url}/api/lists/get-lists.php"; $authorizationHeader['Accept'] = 'application/json'; @@ -123,7 +100,7 @@ public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $fieldMap = $integrationDetails->field_map; - $apiKey = $integrationDetails->api_key; + $apiKey = $integrationDetails->api_key ?: ($integrationDetails->value ?? ''); $integId = $integrationData->id; if ( diff --git a/backend/Actions/SeoPress/Routes.php b/backend/Actions/SeoPress/Routes.php index f67670882..56954c52a 100644 --- a/backend/Actions/SeoPress/Routes.php +++ b/backend/Actions/SeoPress/Routes.php @@ -6,5 +6,3 @@ use BitApps\Integrations\Actions\SeoPress\SeoPressController; use BitApps\Integrations\Core\Util\Route; - -Route::post('seopress_authorize', [SeoPressController::class, 'seoPressAuthorize']); diff --git a/backend/Actions/SeoPress/SeoPressController.php b/backend/Actions/SeoPress/SeoPressController.php index a9ee6d120..8ce6cd63b 100644 --- a/backend/Actions/SeoPress/SeoPressController.php +++ b/backend/Actions/SeoPress/SeoPressController.php @@ -32,17 +32,6 @@ public static function isExists() } } - /** - * Process ajax request for authorization - * - * @return JSON response - */ - public static function seoPressAuthorize() - { - self::isExists(); - wp_send_json_success(true); - } - /** * Execute action * diff --git a/backend/Actions/Slack/Routes.php b/backend/Actions/Slack/Routes.php index 56e7ea290..03639e133 100644 --- a/backend/Actions/Slack/Routes.php +++ b/backend/Actions/Slack/Routes.php @@ -8,4 +8,4 @@ use BitApps\Integrations\Core\Util\Route; // Slack -Route::post('slack_authorization_and_fetch_channels', [SlackController::class, 'checkAuthorizationAndFetchChannels']); +Route::post('slack_fetch_channels', [SlackController::class, 'fetchChannels']); diff --git a/backend/Actions/Slack/SlackController.php b/backend/Actions/Slack/SlackController.php index bdcfe403a..526c82de4 100644 --- a/backend/Actions/Slack/SlackController.php +++ b/backend/Actions/Slack/SlackController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Slack; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -16,6 +17,14 @@ class SlackController { public const APIENDPOINT = 'https://slack.com/api'; + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'slack', + 'fields' => [ + 'accessToken' => 'token', + ], + ]; + /** * Process ajax request for generate_token * @@ -24,7 +33,7 @@ class SlackController * * @return JSON slack api response and status */ - public static function checkAuthorizationAndFetchChannels($tokenRequestParams) + public static function fetchChannels($tokenRequestParams) { if ( empty($tokenRequestParams->accessToken) @@ -52,8 +61,20 @@ public static function checkAuthorizationAndFetchChannels($tokenRequestParams) 400 ); } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); + + $channels = []; + if (!empty($apiResponse->channels) && \is_array($apiResponse->channels)) { + foreach ($apiResponse->channels as $channel) { + if (!empty($channel->id) && !empty($channel->name)) { + $channels[] = [ + 'id' => $channel->id, + 'name' => $channel->name, + ]; + } + } + } + + wp_send_json_success(['channels' => $channels], 200); } public function execute($integrationData, $fieldValues) diff --git a/backend/Actions/SliceWp/Routes.php b/backend/Actions/SliceWp/Routes.php deleted file mode 100644 index 9445e363f..000000000 --- a/backend/Actions/SliceWp/Routes.php +++ /dev/null @@ -1,10 +0,0 @@ -flow_details; diff --git a/backend/Actions/Smaily/Routes.php b/backend/Actions/Smaily/Routes.php index 5fbcf3225..5c539fc24 100644 --- a/backend/Actions/Smaily/Routes.php +++ b/backend/Actions/Smaily/Routes.php @@ -3,8 +3,3 @@ if (!defined('ABSPATH')) { exit; } - -use BitApps\Integrations\Actions\Smaily\SmailyController; -use BitApps\Integrations\Core\Util\Route; - -Route::post('smaily_authentication', [SmailyController::class, 'authentication']); diff --git a/backend/Actions/Smaily/SmailyController.php b/backend/Actions/Smaily/SmailyController.php index 8bc8d775a..485b01f0b 100644 --- a/backend/Actions/Smaily/SmailyController.php +++ b/backend/Actions/Smaily/SmailyController.php @@ -6,7 +6,7 @@ namespace BitApps\Integrations\Actions\Smaily; -use BitApps\Integrations\Core\Util\HttpHelper; +use BitApps\Integrations\Authorization\AuthorizationType; use WP_Error; /** @@ -14,28 +14,15 @@ */ class SmailyController { - public function authentication($fieldsRequestParams) - { - if (empty($fieldsRequestParams->subdomain) && empty($fieldsRequestParams->api_user_name) && empty($fieldsRequestParams->api_user_password)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $subdomain = $fieldsRequestParams->subdomain; - $apiEndpoint = "https://{$subdomain}.sendsmaily.net/api/organizations/users.php"; - $apiUserName = $fieldsRequestParams->api_user_name; - $apiUserPassword = $fieldsRequestParams->api_user_password; - $header = [ - 'Authorization' => 'Basic ' . base64_encode("{$apiUserName}:{$apiUserPassword}") - ]; - - $response = HttpHelper::get($apiEndpoint, null, $header); - - if (isset($response[0]->id) && !empty($response)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid subdomain name and api credentials', 'bit-integrations'), 400); - } - } + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'smaily', + 'fields' => [ + 'api_user_name' => 'username', + 'api_user_password' => 'password', + 'subdomain' => 'subdomain', + ], + ]; public function execute($integrationData, $fieldValues) { diff --git a/backend/Actions/SmartSuite/Routes.php b/backend/Actions/SmartSuite/Routes.php index 94c16791d..a90864c4f 100644 --- a/backend/Actions/SmartSuite/Routes.php +++ b/backend/Actions/SmartSuite/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\SmartSuite\SmartSuiteController; use BitApps\Integrations\Core\Util\Route; -Route::post('smartSuite_authentication', [SmartSuiteController::class, 'authentication']); Route::post('smartSuite_fetch_all_solutions', [SmartSuiteController::class, 'getAllSolutions']); Route::post('smartSuite_fetch_all_tables', [SmartSuiteController::class, 'getAllTables']); Route::post('smartSuite_fetch_all_user', [SmartSuiteController::class, 'getAllUser']); diff --git a/backend/Actions/SmartSuite/SmartSuiteController.php b/backend/Actions/SmartSuite/SmartSuiteController.php index 8bbc6367d..1d25e2528 100644 --- a/backend/Actions/SmartSuite/SmartSuiteController.php +++ b/backend/Actions/SmartSuite/SmartSuiteController.php @@ -6,10 +6,20 @@ namespace BitApps\Integrations\Actions\SmartSuite; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; class SmartSuiteController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'smartsuite', + 'fields' => [ + 'apiToken' => 'value', + 'workspaceId' => 'workspaceId', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; @@ -108,19 +118,6 @@ public function execute($integrationData, $fieldValues) return $smartSuiteApiResponse; } - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->setHeaders($fieldsRequestParams->workspaceId, $fieldsRequestParams->apiToken); - $apiEndpoint = $this->apiEndpoint . 'solutions/'; - $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); - if (\is_array($response)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error($response, 400); - } - } - private function checkValidation($fieldsRequestParams) { if (empty($fieldsRequestParams->workspaceId) || empty($fieldsRequestParams->apiToken)) { diff --git a/backend/Actions/SuiteDash/Routes.php b/backend/Actions/SuiteDash/Routes.php index a0350c9a5..e114d1f5d 100644 --- a/backend/Actions/SuiteDash/Routes.php +++ b/backend/Actions/SuiteDash/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\SuiteDash\SuiteDashController; use BitApps\Integrations\Core\Util\Route; -Route::post('suite_dash_authentication', [SuiteDashController::class, 'authentication']); Route::post('suite_dash_fetch_all_fields', [SuiteDashController::class, 'getAllFields']); Route::post('suite_dash_fetch_all_companies', [SuiteDashController::class, 'getAllCompanies']); diff --git a/backend/Actions/SuiteDash/SuiteDashController.php b/backend/Actions/SuiteDash/SuiteDashController.php index fb68dd68e..0cce43f4f 100644 --- a/backend/Actions/SuiteDash/SuiteDashController.php +++ b/backend/Actions/SuiteDash/SuiteDashController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\SuiteDash; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,15 @@ */ class SuiteDashController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'suitedash', + 'fields' => [ + 'public_id' => 'value', + 'secret_key' => 'secret_key', + ], + ]; + protected $_defaultHeader; protected $_apiEndpoint; @@ -23,20 +33,6 @@ public function __construct() $this->_apiEndpoint = 'https://app.suitedash.com/secure-api'; } - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->setHeaders($fieldsRequestParams->public_id, $fieldsRequestParams->secret_key); - $apiEndpoint = $this->_apiEndpoint . '/contacts'; - $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); - - if (isset($response->success) && $response->success) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid Session Token or Link Name', 'bit-integrations'), 400); - } - } - public function getAllFields($fieldsRequestParams) { $this->checkValidation($fieldsRequestParams); diff --git a/backend/Actions/SureCart/RecordApiHelper.php b/backend/Actions/SureCart/RecordApiHelper.php index a81121ae1..a30556396 100644 --- a/backend/Actions/SureCart/RecordApiHelper.php +++ b/backend/Actions/SureCart/RecordApiHelper.php @@ -31,11 +31,11 @@ public function generateReqDataFromFieldMap($data, $fieldMap) return $dataFinal; } - public function createCustomer($finalData, $api_key) + public function createCustomer($finalData, $token) { $requestData = [ 'headers' => [ - 'Authorization' => 'Bearer ' . $api_key, + 'Authorization' => 'Bearer ' . $token, 'User-Agent' => 'bit-integrations', 'Content-Type' => 'application/json', ], @@ -62,7 +62,7 @@ public function createCustomer($finalData, $api_key) } public function execute( - $api_key, + $token, $fieldValues, $fieldMap, $integrationDetails, @@ -71,7 +71,7 @@ public function execute( $finalData = $this->generateReqDataFromFieldMap($fieldValues, $fieldMap); if ($mainAction == '1') { - $apiResponse = $this->createCustomer($finalData, $api_key); + $apiResponse = $this->createCustomer($finalData, $token); if ($apiResponse[1] === 200) { LogHandler::save($this->_integrationID, wp_json_encode(['type' => 'create', 'type_name' => 'create-customer']), 'success', $apiResponse[0]); } else { diff --git a/backend/Actions/SureCart/Routes.php b/backend/Actions/SureCart/Routes.php index 1ba85c02e..5c539fc24 100644 --- a/backend/Actions/SureCart/Routes.php +++ b/backend/Actions/SureCart/Routes.php @@ -3,8 +3,3 @@ if (!defined('ABSPATH')) { exit; } - -use BitApps\Integrations\Actions\SureCart\SureCartController; -use BitApps\Integrations\Core\Util\Route; - -Route::post('sureCart_authorization', [SureCartController::class, 'checkAuthorization']); diff --git a/backend/Actions/SureCart/SureCartController.php b/backend/Actions/SureCart/SureCartController.php index c34aef239..64d3f8777 100644 --- a/backend/Actions/SureCart/SureCartController.php +++ b/backend/Actions/SureCart/SureCartController.php @@ -2,75 +2,29 @@ namespace BitApps\Integrations\Actions\SureCart; +use BitApps\Integrations\Authorization\AuthorizationType; use WP_Error; class SureCartController { - public $api_url = 'https://api.surecart.com/v1/'; - - public function checkAuthorization($tokenRequestParams) - { - if ( - empty($tokenRequestParams->api_key) || empty($tokenRequestParams->auth_url) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $apiKey = $tokenRequestParams->api_key; - $webhook_url = $tokenRequestParams->auth_url . '/surecart/webhooks'; - - $request_data = [ - 'webhook_endpoint' => [ - 'description' => 'Authorization', - 'enabled' => true, - 'destination' => 'wordpress', - 'url' => $webhook_url, - ], - ]; - - $headers = [ - 'headers' => [ - 'Authorization' => 'Bearer ' . $apiKey, - 'User-Agent' => 'bit-integrations', - 'Content-Type' => 'application/json', - ], - 'timeout' => 60, - 'sslverify' => false, - 'data_format' => 'body', - 'body' => wp_json_encode($request_data), - ]; - - $request = wp_remote_post($this->api_url . 'webhook_endpoints', $headers); - if (is_wp_error($request)) { - wp_send_json_error($request->get_error_message(), 400); - } - $request_body = wp_remote_retrieve_body($request); - $request_data = json_decode($request_body); - if (!$request_data || $request_data->code !== 'unauthorized') { - wp_send_json_success($request_body, 200); - } else { - wp_send_json_error( - $request_data->message, - 400 - ); - } - } + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'surecart', + 'fields' => [ + 'token' => 'token', + ], + ]; public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; $integrationId = $integrationData->id; - $api_key = $integrationDetails->api_key; + $token = $integrationDetails->token ?: ($integrationDetails->api_key ?: ($integrationDetails->value ?? '')); $fieldMap = $integrationDetails->field_map; $mainAction = $integrationDetails->mainAction; if ( - empty($api_key) + empty($token) || empty($integrationDetails) || empty($fieldMap) @@ -81,7 +35,7 @@ public function execute($integrationData, $fieldValues) $recordApiHelper = new RecordApiHelper($integrationId); return $recordApiHelper->execute( - $api_key, + $token, $fieldValues, $fieldMap, $integrationDetails, diff --git a/backend/Actions/SureMembers/Routes.php b/backend/Actions/SureMembers/Routes.php index e8ca8aadb..3e06bde46 100644 --- a/backend/Actions/SureMembers/Routes.php +++ b/backend/Actions/SureMembers/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\SureMembers\SureMembersController; use BitApps\Integrations\Core\Util\Route; -Route::post('sureMembers_authentication', [SureMembersController::class, 'authentication']); Route::post('sureMembers_fetch_groups', [SureMembersController::class, 'getGroups']); diff --git a/backend/Actions/SureMembers/SureMembersController.php b/backend/Actions/SureMembers/SureMembersController.php index a15ce0f12..c57952758 100644 --- a/backend/Actions/SureMembers/SureMembersController.php +++ b/backend/Actions/SureMembers/SureMembersController.php @@ -14,21 +14,6 @@ */ class SureMembersController { - public function authentication() - { - if (self::checkedSureMembersExists()) { - wp_send_json_success(true); - } else { - wp_send_json_error( - __( - 'Please! Install SureMembers', - 'bit-integrations' - ), - 400 - ); - } - } - public static function checkedSureMembersExists() { if (!is_plugin_active('suremembers/suremembers.php')) { diff --git a/backend/Actions/SystemeIO/Routes.php b/backend/Actions/SystemeIO/Routes.php index e0a3897b6..9cb7c6541 100644 --- a/backend/Actions/SystemeIO/Routes.php +++ b/backend/Actions/SystemeIO/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\SystemeIO\SystemeIOController; use BitApps\Integrations\Core\Util\Route; -Route::post('systemeIO_authentication', [SystemeIOController::class, 'authentication']); Route::post('systemeIO_fetch_all_fields', [SystemeIOController::class, 'getAllFields']); Route::post('systemeIO_fetch_all_tags', [SystemeIOController::class, 'getAllTags']); diff --git a/backend/Actions/SystemeIO/SystemeIOController.php b/backend/Actions/SystemeIO/SystemeIOController.php index 33c185a70..4f3f5cfc2 100644 --- a/backend/Actions/SystemeIO/SystemeIOController.php +++ b/backend/Actions/SystemeIO/SystemeIOController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\SystemeIO; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,14 @@ */ class SystemeIOController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'systemeio', + 'fields' => [ + 'api_key' => 'value', + ], + ]; + protected $_defaultHeader; protected $_apiEndpoint; @@ -23,20 +32,6 @@ public function __construct() $this->_apiEndpoint = 'https://api.systeme.io/api'; } - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $this->setHeaders($fieldsRequestParams->api_key); - $apiEndpoint = $this->_apiEndpoint . '/contacts'; - $response = HttpHelper::get($apiEndpoint, null, $this->_defaultHeader); - - if (isset($response->items)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API Key & API Secret', 'bit-integrations'), 400); - } - } - public function getAllTags($fieldsRequestParams) { $this->checkValidation($fieldsRequestParams); diff --git a/backend/Actions/TeamsForWooCommerceMemberships/Routes.php b/backend/Actions/TeamsForWooCommerceMemberships/Routes.php index 33fae41b4..dd7a5c12d 100644 --- a/backend/Actions/TeamsForWooCommerceMemberships/Routes.php +++ b/backend/Actions/TeamsForWooCommerceMemberships/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\TeamsForWooCommerceMemberships\TeamsForWooCommerceMembershipsController; use BitApps\Integrations\Core\Util\Route; -Route::post('teams_for_wc_memberships_authorize', [TeamsForWooCommerceMembershipsController::class, 'teamsForWooCommerceMembershipsAuthorize']); Route::post('teams_for_wc_memberships_refresh_teams', [TeamsForWooCommerceMembershipsController::class, 'refreshTeams']); Route::post('teams_for_wc_memberships_refresh_member_roles', [TeamsForWooCommerceMembershipsController::class, 'refreshMemberRoles']); diff --git a/backend/Actions/TeamsForWooCommerceMemberships/TeamsForWooCommerceMembershipsController.php b/backend/Actions/TeamsForWooCommerceMemberships/TeamsForWooCommerceMembershipsController.php index cea18a445..f6760ee40 100644 --- a/backend/Actions/TeamsForWooCommerceMemberships/TeamsForWooCommerceMembershipsController.php +++ b/backend/Actions/TeamsForWooCommerceMemberships/TeamsForWooCommerceMembershipsController.php @@ -26,12 +26,6 @@ public static function isExists() } } - public static function teamsForWooCommerceMembershipsAuthorize() - { - self::isExists(); - wp_send_json_success(true); - } - public function refreshTeams() { self::isExists(); diff --git a/backend/Actions/Telegram/Routes.php b/backend/Actions/Telegram/Routes.php index cf9541976..b65b55009 100644 --- a/backend/Actions/Telegram/Routes.php +++ b/backend/Actions/Telegram/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\Telegram\TelegramController; use BitApps\Integrations\Core\Util\Route; -Route::post('telegram_authorize', [TelegramController::class, 'telegramAuthorize']); Route::post('refresh_get_updates', [TelegramController::class, 'refreshGetUpdates']); diff --git a/backend/Actions/Telegram/TelegramController.php b/backend/Actions/Telegram/TelegramController.php index 849da1313..27c1095f4 100644 --- a/backend/Actions/Telegram/TelegramController.php +++ b/backend/Actions/Telegram/TelegramController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Telegram; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -15,6 +16,13 @@ class TelegramController { public const APIENDPOINT = 'https://api.telegram.org/bot'; + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'telegram', + 'fields' => [ + 'bot_api_key' => 'value', + ], + ]; private $_integrationID; @@ -23,49 +31,6 @@ class TelegramController // $this->_integrationID = $integrationID; // } - /** - * Process ajax request for generate_token - * - * @param object $requestsParams Params to authorize - * - * @return JSON zoho crm api response and status - */ - public static function telegramAuthorize($requestsParams) - { - if (empty($requestsParams->bot_api_key)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = self::APIENDPOINT . $requestsParams->bot_api_key . '/getMe'; - $authorizationHeader['Accept'] = 'application/x-www-form-urlencoded'; - $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); - - if (is_wp_error($apiResponse) || !$apiResponse->ok) { - wp_send_json_error( - empty($apiResponse->error_code) ? 'Unknown' : $apiResponse, - 400 - ); - } - $apiEndpoint = self::APIENDPOINT . $requestsParams->bot_api_key . '/getUpdates'; - $authorizationHeader['Accept'] = 'application/x-www-form-urlencoded'; - $apiResponse = HttpHelper::get($apiEndpoint, null, $authorizationHeader); - - if (is_wp_error($apiResponse) || !$apiResponse->ok) { - wp_send_json_error( - empty($apiResponse->error_code) ? 'Unknown' : $apiResponse, - 400 - ); - } - - wp_send_json_success(true); - } - /** * Process ajax request for refresh telegram get Updates * diff --git a/backend/Actions/TheEventsCalendar/Routes.php b/backend/Actions/TheEventsCalendar/Routes.php index 4dea9e749..99e11cb99 100644 --- a/backend/Actions/TheEventsCalendar/Routes.php +++ b/backend/Actions/TheEventsCalendar/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\TheEventsCalendar\TheEventsCalendarController; use BitApps\Integrations\Core\Util\Route; -Route::post('the_events_calendar_authentication', [TheEventsCalendarController::class, 'authentication']); Route::post('get_the_events_calendar_events', [TheEventsCalendarController::class, 'getAllEvents']); diff --git a/backend/Actions/TheEventsCalendar/TheEventsCalendarController.php b/backend/Actions/TheEventsCalendar/TheEventsCalendarController.php index 8f2b10bea..52eb46444 100644 --- a/backend/Actions/TheEventsCalendar/TheEventsCalendarController.php +++ b/backend/Actions/TheEventsCalendar/TheEventsCalendarController.php @@ -13,15 +13,6 @@ */ class TheEventsCalendarController { - public function authentication() - { - if (self::checkedTheEventsCalendarExists()) { - wp_send_json_success(true); - } else { - wp_send_json_error(__('The Events Calendar and/or Event Tickets are not active or installed!', 'bit-integrations'), 400); - } - } - public static function checkedTheEventsCalendarExists() { if (is_plugin_active('the-events-calendar/the-events-calendar.php') && is_plugin_active('event-tickets/event-tickets.php')) { diff --git a/backend/Actions/Trello/TrelloController.php b/backend/Actions/Trello/TrelloController.php index 2d90f7c9b..16ac60336 100644 --- a/backend/Actions/Trello/TrelloController.php +++ b/backend/Actions/Trello/TrelloController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Trello; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Config; use BitApps\Integrations\Core\Util\Hooks; use BitApps\Integrations\Core\Util\HttpHelper; @@ -16,11 +17,16 @@ */ class TrelloController { - private $baseUrl = 'https://api.trello.com/1/'; - - private $_integrationID; + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH1, + 'slug' => 'trello', + 'fields' => [ + 'clientId' => 'consumer_key', + 'accessToken' => 'access_token', + ], + ]; - private $accessToken; + private $baseUrl = 'https://api.trello.com/1/'; public function fetchAllBoards($queryParams) { diff --git a/backend/Actions/TutorLms/Routes.php b/backend/Actions/TutorLms/Routes.php index a6f2a3030..66471c1a3 100644 --- a/backend/Actions/TutorLms/Routes.php +++ b/backend/Actions/TutorLms/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\TutorLms\TutorLmsController; use BitApps\Integrations\Core\Util\Route; -Route::post('tutor_authorize', [TutorLmsController::class, 'TutorAuthorize']); Route::get('tutor_all_course', [TutorLmsController::class, 'getAllCourse']); Route::get('tutor_all_lesson', [TutorLmsController::class, 'getAllLesson']); diff --git a/backend/Actions/TutorLms/TutorLmsController.php b/backend/Actions/TutorLms/TutorLmsController.php index 6028add83..4ef65d865 100644 --- a/backend/Actions/TutorLms/TutorLmsController.php +++ b/backend/Actions/TutorLms/TutorLmsController.php @@ -20,22 +20,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - /** - * Process ajax request for generate_token - * - * @return JSON zoho crm api response and status - */ - public static function TutorAuthorize() - { - include_once ABSPATH . 'wp-admin/includes/plugin.php'; - if (is_plugin_active('tutor/tutor.php')) { - wp_send_json_success(true, 200); - } - - // translators: %s: Plugin name - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'Tutor LMS')); - } - public static function getAllLesson() { if (!\function_exists('tutor')) { diff --git a/backend/Actions/Twilio/Routes.php b/backend/Actions/Twilio/Routes.php index 8f0637e49..5c539fc24 100644 --- a/backend/Actions/Twilio/Routes.php +++ b/backend/Actions/Twilio/Routes.php @@ -3,9 +3,3 @@ if (!defined('ABSPATH')) { exit; } - -use BitApps\Integrations\Actions\Twilio\TwilioController; -use BitApps\Integrations\Core\Util\Route; - -// Twilio -Route::post('twilio_authorization', [TwilioController::class, 'checkAuthorization']); diff --git a/backend/Actions/Twilio/TwilioController.php b/backend/Actions/Twilio/TwilioController.php index aa8c8deb3..f8a8df2ff 100644 --- a/backend/Actions/Twilio/TwilioController.php +++ b/backend/Actions/Twilio/TwilioController.php @@ -6,7 +6,7 @@ namespace BitApps\Integrations\Actions\Twilio; -use BitApps\Integrations\Core\Util\HttpHelper; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Log\LogHandler; use WP_Error; @@ -14,6 +14,16 @@ final class TwilioController { public static $apiBaseUri = 'https://api.twilio.com/2010-04-01'; + public static array $authConfig = [ + 'authType' => AuthorizationType::BASIC_AUTH, + 'slug' => 'twilio', + 'fields' => [ + 'sid' => 'username', + 'token' => 'password', + 'from_num' => 'from_num', + ], + ]; + protected $_defaultHeader; private $_integrationID; @@ -23,44 +33,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - public static function checkAuthorization($tokenRequestParams) - { - if ( - empty($tokenRequestParams->sid) - || empty($tokenRequestParams->token) - || empty($tokenRequestParams->from_num) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $header = [ - 'Authorization' => 'Basic ' . base64_encode("{$tokenRequestParams->sid}:{$tokenRequestParams->token}"), - 'Accept' => '*/*', - 'verify' => false - ]; - $apiEndpoint = self::$apiBaseUri . '/Accounts'; - - $apiResponse = HttpHelper::get($apiEndpoint, null, $header); - - $xml = simplexml_load_string($apiResponse); - $json = wp_json_encode($xml); - $response = json_decode($json, true); - - if (\array_key_exists('RestException', $response)) { - wp_send_json_error( - 'Unauthorize', - 400 - ); - } else { - wp_send_json_success($apiResponse, 200); - } - } - public function execute($integrationData, $fieldValues) { $integrationDetails = $integrationData->flow_details; diff --git a/backend/Actions/UltimateAffiliatePro/Routes.php b/backend/Actions/UltimateAffiliatePro/Routes.php deleted file mode 100644 index 6af68bddd..000000000 --- a/backend/Actions/UltimateAffiliatePro/Routes.php +++ /dev/null @@ -1,10 +0,0 @@ -flow_details; diff --git a/backend/Actions/UserRegistrationMembership/Routes.php b/backend/Actions/UserRegistrationMembership/Routes.php index 0c166eca8..c95db56df 100644 --- a/backend/Actions/UserRegistrationMembership/Routes.php +++ b/backend/Actions/UserRegistrationMembership/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\UserRegistrationMembership\UserRegistrationMembershipController; use BitApps\Integrations\Core\Util\Route; -Route::post('user_registration_authorize', [UserRegistrationMembershipController::class, 'userRegistrationAuthorize']); Route::post('refresh_user_registration_forms', [UserRegistrationMembershipController::class, 'refreshForms']); Route::post('refresh_user_registration_form_fields', [UserRegistrationMembershipController::class, 'refreshFormFields']); diff --git a/backend/Actions/UserRegistrationMembership/UserRegistrationMembershipController.php b/backend/Actions/UserRegistrationMembership/UserRegistrationMembershipController.php index 1551425f3..ec56e7555 100644 --- a/backend/Actions/UserRegistrationMembership/UserRegistrationMembershipController.php +++ b/backend/Actions/UserRegistrationMembership/UserRegistrationMembershipController.php @@ -9,12 +9,6 @@ */ class UserRegistrationMembershipController { - public static function userRegistrationAuthorize() - { - self::checkPluginExists(); - wp_send_json_success(true); - } - public static function refreshForms() { self::checkPluginExists(); diff --git a/backend/Actions/Vbout/Routes.php b/backend/Actions/Vbout/Routes.php index 9bfcdc00f..058e99686 100644 --- a/backend/Actions/Vbout/Routes.php +++ b/backend/Actions/Vbout/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Vbout\VboutController; use BitApps\Integrations\Core\Util\Route; -Route::post('vbout_handle_authorize', [VboutController::class, 'handleAuthorize']); Route::post('vbout_fetch_all_lists', [VboutController::class, 'fetchAllLists']); Route::post('vbout_refresh_fields', [VboutController::class, 'vboutRefreshFields']); diff --git a/backend/Actions/Vbout/VboutController.php b/backend/Actions/Vbout/VboutController.php index 91fc1fe64..d226e30f6 100644 --- a/backend/Actions/Vbout/VboutController.php +++ b/backend/Actions/Vbout/VboutController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Vbout; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,33 +15,15 @@ */ class VboutController { - private $baseUrl = 'https://api.vbout.com/1/'; - - public function handleAuthorize($requestParams) - { - if (empty($requestParams->auth_token)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $apiEndpoints = $this->baseUrl . 'app/me.json?key=' . $requestParams->auth_token; + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'vbout', + 'fields' => [ + 'auth_token' => 'value', + ], + ]; - $response = HttpHelper::post($apiEndpoints, null); - if ($response->response->header->status !== 'ok') { - wp_send_json_error( - __( - 'Invalid token', - 'bit-integrations' - ), - 400 - ); - } - wp_send_json_success($response, 200); - } + private $baseUrl = 'https://api.vbout.com/1/'; public function fetchAllLists($requestParams) { diff --git a/backend/Actions/Voxel/Routes.php b/backend/Actions/Voxel/Routes.php index 6021c4d9d..7cddc583f 100644 --- a/backend/Actions/Voxel/Routes.php +++ b/backend/Actions/Voxel/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Voxel\VoxelController; use BitApps\Integrations\Core\Util\Route; -Route::post('voxel_authentication', [VoxelController::class, 'authentication']); Route::post('get_voxel_post_types', [VoxelController::class, 'getPostTypes']); Route::post('get_voxel_post_fields', [VoxelController::class, 'getPostFields']); Route::post('get_voxel_posts', [VoxelController::class, 'getPosts']); diff --git a/backend/Actions/Voxel/VoxelController.php b/backend/Actions/Voxel/VoxelController.php index d4d1c8421..ceb06b01e 100644 --- a/backend/Actions/Voxel/VoxelController.php +++ b/backend/Actions/Voxel/VoxelController.php @@ -14,11 +14,6 @@ */ class VoxelController { - public function authentication() - { - return self::checkIfVoxelExists(); - } - public static function checkIfVoxelExists() { if (wp_get_theme()->get_template() === 'voxel') { diff --git a/backend/Actions/WCAffiliate/Routes.php b/backend/Actions/WCAffiliate/Routes.php deleted file mode 100644 index 6e413ab3c..000000000 --- a/backend/Actions/WCAffiliate/Routes.php +++ /dev/null @@ -1,10 +0,0 @@ -flow_details; @@ -45,11 +33,4 @@ public function execute($integrationData, $fieldValues) return $wcAffiliateResponse; } - - private static function isPluginReady() - { - return class_exists('\WC_Affiliate\Models\Affiliate') - && class_exists('\WC_Affiliate\Models\Referral') - && class_exists('\WC_Affiliate\Models\Transaction'); - } } diff --git a/backend/Actions/WPCafe/Routes.php b/backend/Actions/WPCafe/Routes.php deleted file mode 100644 index 8eb32b6b1..000000000 --- a/backend/Actions/WPCafe/Routes.php +++ /dev/null @@ -1,10 +0,0 @@ -flow_details; diff --git a/backend/Actions/WPCourseware/Routes.php b/backend/Actions/WPCourseware/Routes.php index e10ca3417..78341a467 100644 --- a/backend/Actions/WPCourseware/Routes.php +++ b/backend/Actions/WPCourseware/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\WPCourseware\WPCoursewareController; use BitApps\Integrations\Core\Util\Route; -Route::post('wpCourseware_authorize', [WPCoursewareController::class, 'wpCoursewareAuthorize']); Route::post('wpCourseware_courses', [WPCoursewareController::class, 'WPCWCourses']); diff --git a/backend/Actions/WPCourseware/WPCoursewareController.php b/backend/Actions/WPCourseware/WPCoursewareController.php index 5f2929ef6..74a2f99ea 100644 --- a/backend/Actions/WPCourseware/WPCoursewareController.php +++ b/backend/Actions/WPCourseware/WPCoursewareController.php @@ -14,15 +14,6 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function wpCoursewareAuthorize() - { - if (!is_plugin_active('wp-courseware/wp-courseware.php')) { - wp_send_json_error(__('WP Courseware Plugin is not active or installed', 'bit-integrations'), 400); - } else { - wp_send_json_success(true); - } - } - public static function WPCWCourses() { if (!is_plugin_active('wp-courseware/wp-courseware.php')) { diff --git a/backend/Actions/WPForo/Routes.php b/backend/Actions/WPForo/Routes.php index d925f5f93..ce345d0d6 100644 --- a/backend/Actions/WPForo/Routes.php +++ b/backend/Actions/WPForo/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\WPForo\WPForoController; use BitApps\Integrations\Core\Util\Route; -Route::post('wpforo_authentication', [WPForoController::class, 'authentication']); Route::post('wpforo_fetch_reputations', [WPForoController::class, 'getReputations']); Route::post('wpforo_fetch_groups', [WPForoController::class, 'getGroups']); Route::post('wpforo_fetch_forums', [WPForoController::class, 'getForums']); diff --git a/backend/Actions/WPForo/WPForoController.php b/backend/Actions/WPForo/WPForoController.php index 3e5fdafb7..0e006d702 100644 --- a/backend/Actions/WPForo/WPForoController.php +++ b/backend/Actions/WPForo/WPForoController.php @@ -13,21 +13,6 @@ */ class WPForoController { - public function authentication() - { - if (self::checkedWPForoExists()) { - wp_send_json_success(true); - } else { - wp_send_json_error( - __( - 'Please! Install WPForo', - 'bit-integrations' - ), - 400 - ); - } - } - public static function checkedWPForoExists() { if (!is_plugin_active('wpforo/wpforo.php')) { diff --git a/backend/Actions/WeDocs/Routes.php b/backend/Actions/WeDocs/Routes.php index ba462e976..61a1ba7f1 100644 --- a/backend/Actions/WeDocs/Routes.php +++ b/backend/Actions/WeDocs/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\WeDocs\WeDocsController; use BitApps\Integrations\Core\Util\Route; -Route::post('wedocs_authorize', [WeDocsController::class, 'weDocsAuthorize']); Route::post('wedocs_get_documentations', [WeDocsController::class, 'getDocumentations']); Route::post('wedocs_get_sections', [WeDocsController::class, 'getSections']); diff --git a/backend/Actions/WeDocs/WeDocsController.php b/backend/Actions/WeDocs/WeDocsController.php index bb456bb5d..2eea4693f 100644 --- a/backend/Actions/WeDocs/WeDocsController.php +++ b/backend/Actions/WeDocs/WeDocsController.php @@ -13,12 +13,6 @@ class WeDocsController private const ALLOWED_POST_STATUSES = ['publish', 'draft', 'pending', 'private']; - public static function weDocsAuthorize() - { - self::checkPluginExists(); - wp_send_json_success(true); - } - public static function getDocumentations() { self::checkPluginExists(); diff --git a/backend/Actions/WhatsApp/Routes.php b/backend/Actions/WhatsApp/Routes.php index 8a0846131..59098539b 100644 --- a/backend/Actions/WhatsApp/Routes.php +++ b/backend/Actions/WhatsApp/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\WhatsApp\WhatsAppController; use BitApps\Integrations\Core\Util\Route; -Route::post('whats_app_authorization', [WhatsAppController::class, 'authorization']); Route::post('whats_app_all_template', [WhatsAppController::class, 'getAllTemplate']); diff --git a/backend/Actions/WhatsApp/WhatsAppController.php b/backend/Actions/WhatsApp/WhatsAppController.php index 6e5dc9bfd..0d8d888f1 100644 --- a/backend/Actions/WhatsApp/WhatsAppController.php +++ b/backend/Actions/WhatsApp/WhatsAppController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\WhatsApp; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,22 +15,17 @@ */ class WhatsAppController { - private $baseUrl = 'https://graph.facebook.com/v20.0/'; - - public function authorization($requestParams) - { - static::checkValidation($requestParams); - - $headers = static::setHeaders($requestParams->token); - $apiEndpoint = "{$this->baseUrl}{$requestParams->businessAccountID}"; - $response = HttpHelper::get($apiEndpoint, null, $headers); + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'whatsapp', + 'fields' => [ + 'token' => 'token', + 'numberID' => 'numberID', + 'businessAccountID' => 'businessAccountID', + ], + ]; - if (is_wp_error($response) || !isset($response->id)) { - wp_send_json_error(isset($response->error->message) ? $response->error->message : 'Authentication failed', 400); - } else { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } - } + private $baseUrl = 'https://graph.facebook.com/v20.0/'; public function getAllTemplate($requestParams) { @@ -101,7 +97,7 @@ private static function getTemplate($apiEndpoint, $token) private static function checkValidation($requestParams) { - if (empty($requestParams->numberID) || empty($requestParams->businessAccountID || empty($requestParams->token))) { + if (empty($requestParams->numberID) || empty($requestParams->businessAccountID) || empty($requestParams->token)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } } diff --git a/backend/Actions/WishlistMember/Routes.php b/backend/Actions/WishlistMember/Routes.php index 9e467dfe9..64798bded 100644 --- a/backend/Actions/WishlistMember/Routes.php +++ b/backend/Actions/WishlistMember/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\WishlistMember\WishlistMemberController; use BitApps\Integrations\Core\Util\Route; -Route::post('wishlist_authorization', [WishlistMemberController::class, 'authorization']); Route::post('get_wishlist_levels', [WishlistMemberController::class, 'getLevels']); diff --git a/backend/Actions/WishlistMember/WishlistMemberController.php b/backend/Actions/WishlistMember/WishlistMemberController.php index 1196f88fa..83b24fcbe 100644 --- a/backend/Actions/WishlistMember/WishlistMemberController.php +++ b/backend/Actions/WishlistMember/WishlistMemberController.php @@ -23,24 +23,6 @@ public static function isPluginInstalled() return class_exists('WLMAPI') || class_exists('WishListMember'); } - /** - * Validate if WishlistMember plugin exists or not. - */ - public static function authorization() - { - if (!self::isPluginInstalled()) { - wp_send_json_error( - __( - 'WishlistMember is not activated or not installed', - 'bit-integrations' - ), - 400 - ); - } - - wp_send_json_success(true); - } - /** * Get wishlist levels */ diff --git a/backend/Actions/WooCommerce/Routes.php b/backend/Actions/WooCommerce/Routes.php index 5afd5a265..5cdb4abe7 100644 --- a/backend/Actions/WooCommerce/Routes.php +++ b/backend/Actions/WooCommerce/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\WooCommerce\WooCommerceController; use BitApps\Integrations\Core\Util\Route; -Route::post('wc_authorize', [WooCommerceController::class, 'authorizeWC']); Route::post('wc_refresh_fields', [WooCommerceController::class, 'refreshFields']); Route::post('wc_search_products', [WooCommerceController::class, 'searchProjects']); Route::post('wc_get_all_subscriptions_products', [WooCommerceController::class, 'allSubscriptionsProducts']); diff --git a/backend/Actions/WooCommerce/WooCommerceController.php b/backend/Actions/WooCommerce/WooCommerceController.php index ff080981a..e50357338 100644 --- a/backend/Actions/WooCommerce/WooCommerceController.php +++ b/backend/Actions/WooCommerce/WooCommerceController.php @@ -19,18 +19,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - public static function authorizeWC() - { - include_once ABSPATH . 'wp-admin/includes/plugin.php'; - if (is_plugin_active('woocommerce/woocommerce.php')) { - wp_send_json_success(true, 200); - } - - // translators: %s: Plugin name - // translators: %s: Placeholder value - wp_send_json_error(wp_sprintf(__('%s must be activated!', 'bit-integrations'), 'WooCommerce')); - } - public static function refreshFields($queryParams) { if (empty($queryParams->module)) { diff --git a/backend/Actions/Woodpecker/Routes.php b/backend/Actions/Woodpecker/Routes.php index 48374cfe2..ab898e566 100644 --- a/backend/Actions/Woodpecker/Routes.php +++ b/backend/Actions/Woodpecker/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\Woodpecker\WoodpeckerController; use BitApps\Integrations\Core\Util\Route; -Route::post('woodpecker_authentication', [WoodpeckerController::class, 'authentication']); Route::post('woodpecker_fetch_all_campaigns', [WoodpeckerController::class, 'getAllCampagns']); diff --git a/backend/Actions/Woodpecker/WoodpeckerController.php b/backend/Actions/Woodpecker/WoodpeckerController.php index be70ec9ee..9327680a8 100644 --- a/backend/Actions/Woodpecker/WoodpeckerController.php +++ b/backend/Actions/Woodpecker/WoodpeckerController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Woodpecker; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,27 +15,20 @@ */ class WoodpeckerController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'woodpecker', + 'fields' => [ + 'api_key' => 'value', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; protected $domain; - public function authentication($fieldsRequestParams) - { - $this->checkValidation($fieldsRequestParams); - $apiKey = $fieldsRequestParams->api_key; - $apiEndpoint = $this->setApiEndpoint() . '/campaign_list'; - $headers = $this->setHeaders(base64_encode($apiKey)); - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (isset($response->status) && $response->status->status === 'ERROR') { - wp_send_json_error(__('Please enter valid API key', 'bit-integrations'), 400); - } else { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } - } - public function getAllCampagns($fieldsRequestParams) { $this->checkValidation($fieldsRequestParams); diff --git a/backend/Actions/WpErp/Routes.php b/backend/Actions/WpErp/Routes.php index c2e951bc5..2c4b1c0cc 100644 --- a/backend/Actions/WpErp/Routes.php +++ b/backend/Actions/WpErp/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\WpErp\WpErpController; use BitApps\Integrations\Core\Util\Route; -Route::post('wp_erp_authorize', [WpErpController::class, 'wpErpAuthorize']); Route::post('refresh_wp_erp_contact_groups', [WpErpController::class, 'refreshContactGroups']); Route::post('refresh_wp_erp_life_stages', [WpErpController::class, 'refreshLifeStages']); Route::post('refresh_wp_erp_departments', [WpErpController::class, 'refreshDepartments']); diff --git a/backend/Actions/WpErp/WpErpController.php b/backend/Actions/WpErp/WpErpController.php index 5d08cd517..097625f55 100644 --- a/backend/Actions/WpErp/WpErpController.php +++ b/backend/Actions/WpErp/WpErpController.php @@ -17,12 +17,6 @@ public static function isExists() } } - public static function wpErpAuthorize() - { - self::isExists(); - wp_send_json_success(true); - } - public function refreshContactGroups() { self::isExists(); diff --git a/backend/Actions/ZagoMail/Routes.php b/backend/Actions/ZagoMail/Routes.php index 1d4fe59cb..e493fb4ea 100644 --- a/backend/Actions/ZagoMail/Routes.php +++ b/backend/Actions/ZagoMail/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\ZagoMail\ZagoMailController; use BitApps\Integrations\Core\Util\Route; -Route::post('zagoMail_authorize', [ZagoMailController::class, 'zagoMailAuthorize']); Route::post('zagoMail_refresh_fields', [ZagoMailController::class, 'zagoMailRefreshFields']); Route::post('zagoMail_lists', [ZagoMailController::class, 'zagoMailLists']); Route::post('zagoMail_tags', [ZagoMailController::class, 'zagoMailTags']); diff --git a/backend/Actions/ZagoMail/ZagoMailController.php b/backend/Actions/ZagoMail/ZagoMailController.php index 4af28533a..bc906a2dd 100644 --- a/backend/Actions/ZagoMail/ZagoMailController.php +++ b/backend/Actions/ZagoMail/ZagoMailController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ZagoMail; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,14 @@ */ class ZagoMailController { + public static array $authConfig = [ + 'authType' => AuthorizationType::API_KEY, + 'slug' => 'zagomail', + 'fields' => [ + 'api_public_key' => 'value', + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -26,45 +35,6 @@ public static function _apiEndpoint($method) return "https://api.zagomail.com/{$method}"; } - /** - * Process ajax request - * - * @param $requestsParams Params to authorize - * - * @return JSON ZagoMail api response and status - */ - public static function zagoMailAuthorize($requestsParams) - { - if (empty($requestsParams->api_public_key)) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $body = [ - 'publicKey' => $requestsParams->api_public_key - ]; - - $header['Content-Type'] = 'application/json'; - - $apiEndpoint = self::_apiEndpoint('lists/all-lists'); - - $apiResponse = HttpHelper::post($apiEndpoint, wp_json_encode($body), $header); - - if ($apiResponse->status == 'error' || $apiResponse->status !== 'success') { - wp_send_json_error( - empty($apiResponse) ? 'Unknown' : $apiResponse, - 400 - ); - } - - wp_send_json_success(true); - } - /** * Process ajax request for refresh Lists * diff --git a/backend/Actions/Zendesk/Routes.php b/backend/Actions/Zendesk/Routes.php index dbec0f977..05e3e38fe 100644 --- a/backend/Actions/Zendesk/Routes.php +++ b/backend/Actions/Zendesk/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\Zendesk\ZendeskController; use BitApps\Integrations\Core\Util\Route; -Route::post('zendesk_authentication', [ZendeskController::class, 'authentication']); Route::post('zendesk_fetch_custom_fields', [ZendeskController::class, 'getCustomFields']); Route::post('zendesk_fetch_all_leads', [ZendeskController::class, 'getAllLeads']); Route::post('zendesk_fetch_all_parentOrganizations', [ZendeskController::class, 'getAllParentOrganizations']); diff --git a/backend/Actions/Zendesk/ZendeskController.php b/backend/Actions/Zendesk/ZendeskController.php index b158d8eb2..7db24d4b6 100644 --- a/backend/Actions/Zendesk/ZendeskController.php +++ b/backend/Actions/Zendesk/ZendeskController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\Zendesk; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use WP_Error; @@ -14,6 +15,14 @@ */ class ZendeskController { + public static array $authConfig = [ + 'authType' => AuthorizationType::BEARER_TOKEN, + 'slug' => 'zendesk', + 'fields' => [ + 'api_key' => 'token', + ], + ]; + protected $_defaultHeader; protected $apiEndpoint; @@ -23,27 +32,6 @@ public function __construct() $this->apiEndpoint = 'https://api.getbase.com/v2/'; } - public function authentication($fieldsRequestParams) - { - if (empty($fieldsRequestParams->api_key)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiKey = $fieldsRequestParams->api_key; - $apiEndpoint = $this->apiEndpoint . 'accounts/self'; - $headers = [ - 'Authorization' => 'Bearer ' . $apiKey, - ]; - - $response = HttpHelper::get($apiEndpoint, null, $headers); - - if (isset($response->data)) { - wp_send_json_success(__('Authentication successful', 'bit-integrations'), 200); - } else { - wp_send_json_error(__('Please enter valid API key', 'bit-integrations'), 400); - } - } - public function getCustomFields($fieldsRequestParams) { if (empty($fieldsRequestParams->api_key)) { diff --git a/backend/Actions/ZohoAnalytics/Routes.php b/backend/Actions/ZohoAnalytics/Routes.php index d0367e118..6e6294b3f 100644 --- a/backend/Actions/ZohoAnalytics/Routes.php +++ b/backend/Actions/ZohoAnalytics/Routes.php @@ -7,15 +7,13 @@ use BitApps\Integrations\Actions\ZohoAnalytics\ZohoAnalyticsController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('zanalytics_generate_token', [ZohoAnalyticsController::class, 'generateTokens']); Route::post('zanalytics_refresh_workspaces', [ZohoAnalyticsController::class, 'refreshWorkspacesAjaxHelper']); Route::post('zanalytics_refresh_users', [ZohoAnalyticsController::class, 'refreshUsersAjaxHelper']); Route::post('zanalytics_refresh_tables', [ZohoAnalyticsController::class, 'refreshTablesAjaxHelper']); -Route::post('wp_ajax_zanalytics_refresh_table_headers', [ZohoAnalyticsController::class, 'refreshTableHeadersAjaxHelper']); +Route::post('zanalytics_refresh_table_headers', [ZohoAnalyticsController::class, 'refreshTableHeadersAjaxHelper']); // public static function registerAjax() // { -// add_action('wp_ajax_zanalytics_generate_token', array(__CLASS__, 'generateTokens')); // add_action('wp_ajax_zanalytics_refresh_workspaces', array(__CLASS__, 'refreshWorkspacesAjaxHelper')); // add_action('wp_ajax_zanalytics_refresh_users', array(__CLASS__, 'refreshUsersAjaxHelper')); // add_action('wp_ajax_zanalytics_refresh_tables', array(__CLASS__, 'refreshTablesAjaxHelper')); diff --git a/backend/Actions/ZohoAnalytics/ZohoAnalyticsController.php b/backend/Actions/ZohoAnalytics/ZohoAnalyticsController.php index 5da2283f2..789aed3b7 100644 --- a/backend/Actions/ZohoAnalytics/ZohoAnalyticsController.php +++ b/backend/Actions/ZohoAnalytics/ZohoAnalyticsController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ZohoAnalytics; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\ApiResponse as UtilApiResponse; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Core\Util\IpTool; @@ -17,6 +18,18 @@ */ class ZohoAnalyticsController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'zohoanalytics', + 'fields' => [ + 'dataCenter' => 'dataCenter', + 'ownerEmail' => 'ownerEmail', + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -25,58 +38,6 @@ public function __construct($integrationID) $this->_logResponse = new UtilApiResponse(); } - /** - * Process ajax request for generate_token - * - * @param mixed $requestsParams - * - * @return JSON zoho crm api response and status - */ - public static function generateTokens($requestsParams) - { - if (empty($requestsParams->{'accounts-server'}) - || empty($requestsParams->dataCenter) - || empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = urldecode($requestsParams->{'accounts-server'}) . '/oauth/v2/token'; - $requestParams = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => urldecode($requestsParams->redirectURI), - 'code' => $requestsParams->code - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - - /** - * Process ajax request for refresh crm modules - * - * @param mixed $queryParams - * - * @return JSON crm module data - */ public static function refreshWorkspacesAjaxHelper($queryParams) { if (empty($queryParams->tokenDetails) diff --git a/backend/Actions/ZohoBigin/Routes.php b/backend/Actions/ZohoBigin/Routes.php index 82cbd4d5e..b3450a9f1 100644 --- a/backend/Actions/ZohoBigin/Routes.php +++ b/backend/Actions/ZohoBigin/Routes.php @@ -7,10 +7,8 @@ use BitApps\Integrations\Actions\ZohoBigin\ZohoBiginController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('zbigin_generate_token', [ZohoBiginController::class, 'generateTokens']); Route::post('zbigin_refresh_modules', [ZohoBiginController::class, 'refreshModules']); Route::post('zbigin_refresh_playouts', [ZohoBiginController::class, 'refreshPLayouts']); -Route::post('zbigin_refresh_notetypes', [ZohoBiginController::class, 'refreshNoteTypes']); Route::post('zbigin_refresh_related_lists', [ZohoBiginController::class, 'refreshRelatedModules']); Route::post('zbigin_refresh_fields', [ZohoBiginController::class, 'getFields']); Route::post('zbigin_refresh_tags', [ZohoBiginController::class, 'getTagList']); diff --git a/backend/Actions/ZohoBigin/ZohoBiginController.php b/backend/Actions/ZohoBigin/ZohoBiginController.php index 88033501c..f88d23885 100644 --- a/backend/Actions/ZohoBigin/ZohoBiginController.php +++ b/backend/Actions/ZohoBigin/ZohoBiginController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ZohoBigin; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Config; use BitApps\Integrations\Core\Util\Hooks; use BitApps\Integrations\Core\Util\HttpHelper; @@ -18,6 +19,17 @@ */ class ZohoBiginController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'zohobigin', + 'fields' => [ + 'dataCenter' => 'dataCenter', + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on', 'api_domain']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -25,59 +37,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - /** - * Process ajax request for generate_token - * - * @param object $requestsParams Params to generate token - * - * @return JSON zoho bigin api response and status - */ - public static function generateTokens($requestsParams) - { - if ( - empty($requestsParams->{'accounts-server'}) - || empty($requestsParams->dataCenter) - || empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = urldecode($requestsParams->{'accounts-server'}) . '/oauth/v2/token'; - $requestParams = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => urldecode($requestsParams->redirectURI), - 'code' => $requestsParams->code - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - - /** - * Process ajax request for refresh bigin modules - * - * @param object $queryParams Params to refresh modules - * - * @return JSON bigin module data - */ public static function refreshModules($queryParams) { if ( @@ -372,7 +331,7 @@ public static function getTagList($queryParams) wp_send_json_success($response, 200); } - public function getUsers($queryParams) + public static function getUsers($queryParams) { if ( empty($queryParams->tokenDetails) diff --git a/backend/Actions/ZohoCRM/Routes.php b/backend/Actions/ZohoCRM/Routes.php index f74faf65b..f772b8af3 100644 --- a/backend/Actions/ZohoCRM/Routes.php +++ b/backend/Actions/ZohoCRM/Routes.php @@ -11,9 +11,5 @@ Route::post('zcrm_get_tags', [ZohoCRMController::class, 'refreshTagListAjaxHelper']); Route::post('zcrm_get_assignment_rules', [ZohoCRMController::class, 'getAssignmentRulesAjaxHelper']); Route::post('zcrm_get_related_lists', [ZohoCRMController::class, 'getRelatedListsAjaxHelper']); -Route::no_sanitize()->post('zcrm_generate_token', [ZohoCRMController::class, 'generateTokens']); Route::post('zcrm_refresh_modules', [ZohoCRMController::class, 'refreshModulesAjaxHelper']); Route::post('zcrm_refresh_layouts', [ZohoCRMController::class, 'refreshLayoutsAjaxHelper']); - -// Rapidmail -// Route::post('rapidmail_authorization', [RapidmailController::class, 'checkAuthorization']); diff --git a/backend/Actions/ZohoCRM/ZohoCRMController.php b/backend/Actions/ZohoCRM/ZohoCRMController.php index ae4359eba..61d55ce73 100644 --- a/backend/Actions/ZohoCRM/ZohoCRMController.php +++ b/backend/Actions/ZohoCRM/ZohoCRMController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ZohoCRM; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use BitApps\Integrations\Log\LogHandler; @@ -17,6 +18,17 @@ */ final class ZohoCRMController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'zohocrm', + 'fields' => [ + 'dataCenter' => 'dataCenter', + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on', 'api_domain']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -24,57 +36,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - /** - * Process ajax request for generate_token - * - * @param $requestsParams Mandatory params for generate tokens - * - * @return JSON zoho crm api response and status - */ - public static function generateTokens($requestsParams) - { - if ( - empty($requestsParams->{'accounts-server'}) - || empty($requestsParams->dataCenter) - || empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - $apiEndpoint = urldecode($requestsParams->{'accounts-server'}) . '/oauth/v2/token'; - $requestParams = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => urldecode($requestsParams->redirectURI), - 'code' => $requestsParams->code - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - - /** - * Process ajax request for refresh crm modules - * - * @param $queryParams Mandatory params to get modules - * - * @return JSON crm module data - */ public static function refreshModulesAjaxHelper($queryParams) { if ( diff --git a/backend/Actions/ZohoCampaigns/Routes.php b/backend/Actions/ZohoCampaigns/Routes.php index 70cd74e34..4e3b42899 100644 --- a/backend/Actions/ZohoCampaigns/Routes.php +++ b/backend/Actions/ZohoCampaigns/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\ZohoCampaigns\ZohoCampaignsController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('zcampaigns_generate_token', [ZohoCampaignsController::class, 'generateTokens']); Route::post('zcampaigns_refresh_lists', [ZohoCampaignsController::class, 'refreshLists']); Route::post('zcampaigns_refresh_contact_fields', [ZohoCampaignsController::class, 'refreshContactFields']); diff --git a/backend/Actions/ZohoCampaigns/ZohoCampaignsController.php b/backend/Actions/ZohoCampaigns/ZohoCampaignsController.php index e90746f52..ff1df3dec 100644 --- a/backend/Actions/ZohoCampaigns/ZohoCampaignsController.php +++ b/backend/Actions/ZohoCampaigns/ZohoCampaignsController.php @@ -6,8 +6,8 @@ namespace BitApps\Integrations\Actions\ZohoCampaigns; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\ApiResponse as UtilApiResponse; - use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use BitApps\Integrations\Log\LogHandler; @@ -18,6 +18,17 @@ */ class ZohoCampaignsController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'zohocampaigns', + 'fields' => [ + 'dataCenter' => 'dataCenter', + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -26,58 +37,6 @@ public function __construct($integrationID) // $this->_logResponse = new UtilApiResponse(); } - /** - * Process ajax request for generate_token - * - * @param object $requestsParams Params to generate token - * - * @return JSON zoho crm api response and status - */ - public static function generateTokens($requestsParams) - { - if (empty($requestsParams->{'accounts-server'}) - || empty($requestsParams->dataCenter) - || empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = urldecode($requestsParams->{'accounts-server'}) . '/oauth/v2/token'; - $requestParams = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => urldecode($requestsParams->redirectURI), - 'code' => $requestsParams->code - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - - /** - * Process ajax request for refresh crm modules - * - * @param object $queryParams Params to fetch campaign list - * - * @return JSON crm module data - */ public static function refreshLists($queryParams) { if (empty($queryParams->tokenDetails) diff --git a/backend/Actions/ZohoCreator/Routes.php b/backend/Actions/ZohoCreator/Routes.php index 563f7253c..71081eab8 100644 --- a/backend/Actions/ZohoCreator/Routes.php +++ b/backend/Actions/ZohoCreator/Routes.php @@ -7,19 +7,13 @@ use BitApps\Integrations\Actions\ZohoCreator\ZohoCreatorController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('zcreator_generate_token', [ZohoCreatorController::class, 'generateTokens']); Route::post('zcreator_refresh_applications', [ZohoCreatorController::class, 'refreshApplicationsAjaxHelper']); Route::post('zcreator_refresh_forms', [ZohoCreatorController::class, 'refreshFormsAjaxHelper']); Route::post('zcreator_refresh_fields', [ZohoCreatorController::class, 'refreshFieldsAjaxHelper']); -Route::post('zcreator_refresh_owners', [ZohoCreatorController::class, 'refreshTicketOwnersAjaxHelper']); -Route::post('zcreator_refresh_products', [ZohoCreatorController::class, 'refreshProductsAjaxHelper']); // public static function registerAjax() // { -// add_action('wp_ajax_zcreator_generate_token', array(__CLASS__, 'generateTokens')); // add_action('wp_ajax_zcreator_refresh_applications', array(__CLASS__, 'refreshApplicationsAjaxHelper')); // add_action('wp_ajax_zcreator_refresh_forms', array(__CLASS__, 'refreshFormsAjaxHelper')); // add_action('wp_ajax_zcreator_refresh_fields', array(__CLASS__, 'refreshFieldsAjaxHelper')); -// add_action('wp_ajax_zcreator_refresh_owners', array(__CLASS__, 'refreshTicketOwnersAjaxHelper')); -// add_action('wp_ajax_zcreator_refresh_products', array(__CLASS__, 'refreshProductsAjaxHelper')); // } diff --git a/backend/Actions/ZohoCreator/ZohoCreatorController.php b/backend/Actions/ZohoCreator/ZohoCreatorController.php index a48533b7e..70a04c44d 100644 --- a/backend/Actions/ZohoCreator/ZohoCreatorController.php +++ b/backend/Actions/ZohoCreator/ZohoCreatorController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ZohoCreator; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Core\Util\IpTool; @@ -14,6 +15,18 @@ */ class ZohoCreatorController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'zohocreator', + 'fields' => [ + 'dataCenter' => 'dataCenter', + 'accountOwner' => 'accountOwner', + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -21,51 +34,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - /** - * Process ajax request for generate_token - * - * @param mixed $requestsParams - * - * @return JSON zoho crm api response and status - */ - public static function generateTokens($requestsParams) - { - if (empty($requestsParams->{'accounts-server'}) - || empty($requestsParams->dataCenter) - || empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = urldecode($requestsParams->{'accounts-server'}) . '/oauth/v2/token'; - $requestParams = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => urldecode($requestsParams->redirectURI), - 'code' => $requestsParams->code - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public static function refreshApplicationsAjaxHelper($queryParams) { if (empty($queryParams->tokenDetails) diff --git a/backend/Actions/ZohoDesk/Routes.php b/backend/Actions/ZohoDesk/Routes.php index 99e7bfd6f..caffa7a30 100644 --- a/backend/Actions/ZohoDesk/Routes.php +++ b/backend/Actions/ZohoDesk/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\ZohoDesk\ZohoDeskController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('zdesk_generate_token', [ZohoDeskController::class, 'generateTokens']); Route::post('zdesk_refresh_organizations', [ZohoDeskController::class, 'refreshOrganizations']); Route::post('zdesk_refresh_departments', [ZohoDeskController::class, 'refreshDepartments']); Route::post('zdesk_refresh_fields', [ZohoDeskController::class, 'refreshFields']); diff --git a/backend/Actions/ZohoDesk/ZohoDeskController.php b/backend/Actions/ZohoDesk/ZohoDeskController.php index 9980abf04..17d07d15a 100644 --- a/backend/Actions/ZohoDesk/ZohoDeskController.php +++ b/backend/Actions/ZohoDesk/ZohoDeskController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ZohoDesk; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use WP_Error; @@ -15,6 +16,17 @@ */ class ZohoDeskController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'zohodesk', + 'fields' => [ + 'dataCenter' => 'dataCenter', + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -22,51 +34,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - /** - * Process ajax request for generate_token - * - * @param mixed $requestsParams - * - * @return JSON zoho crm api response and status - */ - public static function generateTokens($requestsParams) - { - if (empty($requestsParams->{'accounts-server'}) - || empty($requestsParams->dataCenter) - || empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = urldecode($requestsParams->{'accounts-server'}) . '/oauth/v2/token'; - $requestParams = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => urldecode($requestsParams->redirectURI), - 'code' => $requestsParams->code - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public static function refreshOrganizations($queryParams) { if (empty($queryParams->tokenDetails) diff --git a/backend/Actions/ZohoMarketingHub/Routes.php b/backend/Actions/ZohoMarketingHub/Routes.php index 6d88c08e5..e59a9827d 100644 --- a/backend/Actions/ZohoMarketingHub/Routes.php +++ b/backend/Actions/ZohoMarketingHub/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\ZohoMarketingHub\ZohoMarketingHubController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('zmarketingHub_generate_token', [ZohoMarketingHubController::class, 'generateTokens']); Route::post('zmarketingHub_refresh_lists', [ZohoMarketingHubController::class, 'refreshLists']); Route::post('zmarketingHub_refresh_contact_fields', [ZohoMarketingHubController::class, 'refreshContactFields']); diff --git a/backend/Actions/ZohoMarketingHub/ZohoMarketingHubController.php b/backend/Actions/ZohoMarketingHub/ZohoMarketingHubController.php index a4c309b6f..003dd36d1 100644 --- a/backend/Actions/ZohoMarketingHub/ZohoMarketingHubController.php +++ b/backend/Actions/ZohoMarketingHub/ZohoMarketingHubController.php @@ -6,8 +6,8 @@ namespace BitApps\Integrations\Actions\ZohoMarketingHub; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; - use BitApps\Integrations\Flow\FlowController; use WP_Error; @@ -16,6 +16,17 @@ */ class ZohoMarketingHubController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'zohomarketinghub', + 'fields' => [ + 'dataCenter' => 'dataCenter', + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -23,58 +34,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - /** - * Process ajax request for generate_token - * - * @param object $requestsParams Params to generate token - * - * @return JSON zoho crm api response and status - */ - public static function generateTokens($requestsParams) - { - if (empty($requestsParams->{'accounts-server'}) - || empty($requestsParams->dataCenter) - || empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = urldecode($requestsParams->{'accounts-server'}) . '/oauth/v2/token'; - $requestParams = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => urldecode($requestsParams->redirectURI), - 'code' => $requestsParams->code - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - - /** - * Process ajax request for refresh crm modules - * - * @param object $queryParams Params to refresh lists - * - * @return JSON crm module data - */ public static function refreshLists($queryParams) { if (empty($queryParams->tokenDetails) diff --git a/backend/Actions/ZohoRecruit/Routes.php b/backend/Actions/ZohoRecruit/Routes.php index 9b09ce1be..b8eff2c75 100644 --- a/backend/Actions/ZohoRecruit/Routes.php +++ b/backend/Actions/ZohoRecruit/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\ZohoRecruit\ZohoRecruitController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('zrecruit_generate_token', [ZohoRecruitController::class, 'generateTokens']); Route::post('zrecruit_refresh_modules', [ZohoRecruitController::class, 'refreshModules']); Route::post('zrecruit_refresh_notetypes', [ZohoRecruitController::class, 'refreshNoteTypes']); Route::post('zrecruit_refresh_related_lists', [ZohoRecruitController::class, 'refreshRelatedModules']); diff --git a/backend/Actions/ZohoRecruit/ZohoRecruitController.php b/backend/Actions/ZohoRecruit/ZohoRecruitController.php index dedec4935..30a56b1d6 100644 --- a/backend/Actions/ZohoRecruit/ZohoRecruitController.php +++ b/backend/Actions/ZohoRecruit/ZohoRecruitController.php @@ -6,6 +6,7 @@ namespace BitApps\Integrations\Actions\ZohoRecruit; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use WP_Error; @@ -15,6 +16,17 @@ */ class ZohoRecruitController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'zohorecruit', + 'fields' => [ + 'dataCenter' => 'dataCenter', + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on']], + ], + ]; + private $_integrationID; public function __construct($integrationID) @@ -22,58 +34,6 @@ public function __construct($integrationID) $this->_integrationID = $integrationID; } - /** - * Process ajax request for generate_token - * - * @param object $requestsParams Params to generate token - * - * @return JSON zoho recruit api response and status - */ - public static function generateTokens($requestsParams) - { - if (empty($requestsParams->{'accounts-server'}) - || empty($requestsParams->dataCenter) - || empty($requestsParams->clientId) - || empty($requestsParams->clientSecret) - || empty($requestsParams->redirectURI) - || empty($requestsParams->code) - ) { - wp_send_json_error( - __( - 'Requested parameter is empty', - 'bit-integrations' - ), - 400 - ); - } - - $apiEndpoint = urldecode($requestsParams->{'accounts-server'}) . '/oauth/v2/token'; - $requestParams = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestsParams->clientId, - 'client_secret' => $requestsParams->clientSecret, - 'redirect_uri' => urldecode($requestsParams->redirectURI), - 'code' => $requestsParams->code - ]; - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error( - empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, - 400 - ); - } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - - /** - * Process ajax request for refresh recruit modules - * - * @param object $queryParams Params to refresh module - * - * @return JSON recruit module data - */ public static function refreshModules($queryParams) { if (empty($queryParams->tokenDetails) diff --git a/backend/Actions/ZohoSheet/Routes.php b/backend/Actions/ZohoSheet/Routes.php index 27b10c084..5f4aba860 100644 --- a/backend/Actions/ZohoSheet/Routes.php +++ b/backend/Actions/ZohoSheet/Routes.php @@ -7,7 +7,6 @@ use BitApps\Integrations\Actions\ZohoSheet\ZohoSheetController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('zohoSheet_generate_token', [ZohoSheetController::class, 'generateTokens']); Route::post('zohoSheet_fetch_all_work_books', [ZohoSheetController::class, 'getAllWorkbooks']); Route::post('zohoSheet_fetch_all_work_sheets', [ZohoSheetController::class, 'getAllWorksheets']); Route::post('zohoSheet_fetch_all_work_sheet_header', [ZohoSheetController::class, 'getWorksheetHeader']); diff --git a/backend/Actions/ZohoSheet/ZohoSheetController.php b/backend/Actions/ZohoSheet/ZohoSheetController.php index f22e232e6..b0bedc7a1 100644 --- a/backend/Actions/ZohoSheet/ZohoSheetController.php +++ b/backend/Actions/ZohoSheet/ZohoSheetController.php @@ -2,47 +2,30 @@ namespace BitApps\Integrations\Actions\ZohoSheet; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use WP_Error; class ZohoSheetController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'zohosheet', + 'fields' => [ + 'dataCenter' => 'dataCenter', + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at', 'generates_on']], + ], + ]; + private $integrationID; public function __construct($integrationID) { $this->integrationID = $integrationID; } - - public static function generateTokens($requestParams) - { - if ( - empty($requestParams->{'accounts-server'}) || empty($requestParams->dataCenter) || empty($requestParams->clientId) - || empty($requestParams->clientSecret) || empty($requestParams->redirectURI) || empty($requestParams->code) - ) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $apiEndpoint = urldecode($requestParams->{'accounts-server'}) . '/oauth/v2/token'; - $requestParams = [ - 'grant_type' => 'authorization_code', - 'client_id' => $requestParams->clientId, - 'client_secret' => $requestParams->clientSecret, - 'redirect_uri' => urldecode($requestParams->redirectURI), - 'code' => $requestParams->code - ]; - - $apiResponse = HttpHelper::post($apiEndpoint, $requestParams); - - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error(empty($apiResponse->error) ? 'Unknown' : $apiResponse->error, 400); - } - - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public static function getAllWorkbooks($requestParams) { if ( diff --git a/backend/Actions/Zoom/Routes.php b/backend/Actions/Zoom/Routes.php index 66caeb5dc..2208e06e2 100644 --- a/backend/Actions/Zoom/Routes.php +++ b/backend/Actions/Zoom/Routes.php @@ -7,6 +7,5 @@ use BitApps\Integrations\Actions\Zoom\ZoomController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('zoom_generate_token', [ZoomController::class, 'authorization']); Route::post('zoom_fetch_all_meetings', [ZoomController::class, 'zoomFetchAllMeetings']); Route::post('zoom_fetch_all_fields', [ZoomController::class, 'zoomFetchAllCustomFields']); diff --git a/backend/Actions/Zoom/ZoomController.php b/backend/Actions/Zoom/ZoomController.php index 0bc1d400c..7d2b2d171 100644 --- a/backend/Actions/Zoom/ZoomController.php +++ b/backend/Actions/Zoom/ZoomController.php @@ -2,12 +2,24 @@ namespace BitApps\Integrations\Actions\Zoom; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use WP_Error; class ZoomController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'zoom', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + 'accessToken' => 'access_token', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; + private $integrationID; public function __construct($integrationID) @@ -15,36 +27,28 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function authorization($requestParams) + public static function zoomFetchAllMeetings($requestParams) { - if (empty($requestParams->clientId) || empty($requestParams->clientSecret) || empty($requestParams->code) || empty($requestParams->redirectURI)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); - } - - $body = [ - 'redirect_uri' => urldecode($requestParams->redirectURI), - 'grant_type' => 'authorization_code', - 'code' => urldecode($requestParams->code) - ]; - - $apiEndpoint = 'https://zoom.us/oauth/token'; - $header['Content-Type'] = 'application/x-www-form-urlencoded'; - $header['Authorization'] = 'Basic ' . base64_encode("{$requestParams->clientId}:{$requestParams->clientSecret}"); - $apiResponse = HttpHelper::post($apiEndpoint, $body, $header); - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error(empty($apiResponse->error_description) ? 'Unknown' : $apiResponse->error_description, 400); + $tokenDetails = $requestParams->tokenDetails ?? null; + $clientId = $requestParams->clientId ?? ''; + $clientSecret = $requestParams->clientSecret ?? ''; + + if (empty($tokenDetails) && !empty($requestParams->accessToken)) { + $tokenDetails = (object) [ + 'access_token' => $requestParams->accessToken, + 'refresh_token' => $requestParams->refreshToken ?? '', + ]; } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public static function zoomFetchAllMeetings($requestParams) - { - if (empty($requestParams->tokenDetails) || empty($requestParams->clientId) || empty($requestParams->clientSecret)) { + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $tokenDetails = self::tokenExpiryCheck($requestParams->tokenDetails, $requestParams->clientId, $requestParams->clientSecret); + if (!empty($requestParams->connection_id)) { + $tokenDetails = self::normalizeConnectionToken($tokenDetails); + } else { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $clientId, $clientSecret); + } $header = [ 'Authorization' => 'Bearer ' . $tokenDetails->access_token, 'Content-Type' => 'application/json' @@ -63,11 +67,26 @@ public static function zoomFetchAllMeetings($requestParams) public static function zoomFetchAllCustomFields($requestParams) { - if (empty($requestParams->tokenDetails) || empty($requestParams->clientId) || empty($requestParams->clientSecret) || empty($requestParams->meetingId)) { + $tokenDetails = $requestParams->tokenDetails ?? null; + $clientId = $requestParams->clientId ?? ''; + $clientSecret = $requestParams->clientSecret ?? ''; + + if (empty($tokenDetails) && !empty($requestParams->accessToken)) { + $tokenDetails = (object) [ + 'access_token' => $requestParams->accessToken, + 'refresh_token' => $requestParams->refreshToken ?? '', + ]; + } + + if (empty($tokenDetails) || empty($tokenDetails->access_token) || empty($requestParams->meetingId)) { wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $tokenDetails = self::tokenExpiryCheck($requestParams->tokenDetails, $requestParams->clientId, $requestParams->clientSecret); + if (!empty($requestParams->connection_id)) { + $tokenDetails = self::normalizeConnectionToken($tokenDetails); + } else { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $clientId, $clientSecret); + } $header = [ 'Authorization' => 'Bearer ' . $tokenDetails->access_token, 'Content-Type' => 'application/json' @@ -118,9 +137,16 @@ public function execute($integrationData, $fieldValues) $actions = $integrationDetails->actions; $defaultDataConf = $integrationDetails->default; $selectedAction = $integrationDetails->selectedActions; - $oldToken = $integrationDetails->tokenDetails->access_token; - $tokenDetails = self::tokenExpiryCheck($integrationDetails->tokenDetails, $integrationDetails->clientId, $integrationDetails->clientSecret); - if ($tokenDetails->access_token !== $oldToken) { + + $isConnectionAuth = !empty($integrationDetails->connection_id); + $tokenDetails = self::normalizeConnectionToken($integrationDetails->tokenDetails); + $oldToken = $tokenDetails->access_token ?? ''; + + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $integrationDetails->clientId, $integrationDetails->clientSecret); + } + + if (!$isConnectionAuth && $tokenDetails->access_token !== $oldToken) { self::saveRefreshedToken($this->integrationID, $tokenDetails); } if ( @@ -155,7 +181,9 @@ private static function tokenExpiryCheck($token, $clientId, $clientSecret) return false; } - if ((\intval($token->generates_on) + (55 * 60)) < time()) { + $generatedOn = !empty($token->generates_on) ? (int) $token->generates_on : (int) ($token->generated_at ?? 0); + + if ($generatedOn > 0 && ($generatedOn + (55 * 60)) < time()) { $refreshToken = self::refreshToken($token->refresh_token, $clientId, $clientSecret); if (is_wp_error($refreshToken) || !empty($refreshToken->error)) { return false; @@ -165,6 +193,7 @@ private static function tokenExpiryCheck($token, $clientId, $clientSecret) $token->access_token = $refreshToken->access_token; $token->expires_in = $refreshToken->expires_in; $token->generates_on = $refreshToken->generates_on; + $token->generated_at = $refreshToken->generated_at; $token->refresh_token = $refreshToken->refresh_token; } } @@ -191,6 +220,20 @@ private static function refreshToken($refresh_token, $clientId, $clientSecret) } $token = $apiResponse; $token->generates_on = time(); + $token->generated_at = $token->generates_on; + + return $token; + } + + private static function normalizeConnectionToken($token) + { + if (!\is_object($token)) { + $token = (object) []; + } + + if (empty($token->generates_on) && !empty($token->generated_at)) { + $token->generates_on = (int) $token->generated_at; + } return $token; } diff --git a/backend/Actions/ZoomWebinar/Routes.php b/backend/Actions/ZoomWebinar/Routes.php index 4465fc553..7863ff6fa 100644 --- a/backend/Actions/ZoomWebinar/Routes.php +++ b/backend/Actions/ZoomWebinar/Routes.php @@ -7,5 +7,4 @@ use BitApps\Integrations\Actions\ZoomWebinar\ZoomWebinarController; use BitApps\Integrations\Core\Util\Route; -Route::no_sanitize()->post('zoom_webinar_generate_token', [ZoomWebinarController::class, 'authorization']); Route::post('zoom_webinar_fetch_all_webinar', [ZoomWebinarController::class, 'zoomFetchAllWebinar']); diff --git a/backend/Actions/ZoomWebinar/ZoomWebinarController.php b/backend/Actions/ZoomWebinar/ZoomWebinarController.php index dbaa1116c..aba4c7839 100644 --- a/backend/Actions/ZoomWebinar/ZoomWebinarController.php +++ b/backend/Actions/ZoomWebinar/ZoomWebinarController.php @@ -2,12 +2,24 @@ namespace BitApps\Integrations\Actions\ZoomWebinar; +use BitApps\Integrations\Authorization\AuthorizationType; use BitApps\Integrations\Core\Util\HttpHelper; use BitApps\Integrations\Flow\FlowController; use WP_Error; class ZoomWebinarController { + public static array $authConfig = [ + 'authType' => AuthorizationType::OAUTH2, + 'slug' => 'zoomwebinar', + 'fields' => [ + 'clientId' => 'client_id', + 'clientSecret' => 'client_secret', + 'accessToken' => 'access_token', + '__object' => ['tokenDetails', ['access_token', 'refresh_token', 'token_type', 'expires_in', 'generated_at']], + ], + ]; + private $integrationID; public function __construct($integrationID) @@ -15,36 +27,31 @@ public function __construct($integrationID) $this->integrationID = $integrationID; } - public static function authorization($requestParams) + public static function zoomFetchAllWebinar($requestParams) { - if (empty($requestParams->clientId) || empty($requestParams->clientSecret) || empty($requestParams->code) || empty($requestParams->redirectURI)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); + $tokenDetails = $requestParams->tokenDetails ?? null; + $clientId = $requestParams->clientId ?? ''; + $clientSecret = $requestParams->clientSecret ?? ''; + + if (empty($tokenDetails) && !empty($requestParams->accessToken)) { + $tokenDetails = (object) [ + 'access_token' => $requestParams->accessToken, + 'refresh_token' => $requestParams->refreshToken ?? '', + ]; } - $body = [ - 'redirect_uri' => urldecode($requestParams->redirectURI), - 'grant_type' => 'authorization_code', - 'code' => urldecode($requestParams->code) - ]; - - $apiEndpoint = 'https://zoom.us/oauth/token'; - $header['Content-Type'] = 'application/x-www-form-urlencoded'; - $header['Authorization'] = 'Basic ' . base64_encode("{$requestParams->clientId}:{$requestParams->clientSecret}"); - $apiResponse = HttpHelper::post($apiEndpoint, $body, $header); - if (is_wp_error($apiResponse) || !empty($apiResponse->error)) { - wp_send_json_error(empty($apiResponse->error_description) ? 'Unknown' : $apiResponse->error_description, 400); + if (empty($tokenDetails) || empty($tokenDetails->access_token)) { + wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); } - $apiResponse->generates_on = time(); - wp_send_json_success($apiResponse, 200); - } - public static function zoomFetchAllWebinar($requestParams) - { - if (empty($requestParams->accessToken)) { - wp_send_json_error(__('Requested parameter is empty', 'bit-integrations'), 400); + if (!empty($requestParams->connection_id)) { + $tokenDetails = self::normalizeConnectionToken($tokenDetails); + } else { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $clientId, $clientSecret); } + $header = [ - 'Authorization' => 'Bearer ' . $requestParams->accessToken, + 'Authorization' => 'Bearer ' . $tokenDetails->access_token, 'Content-Type' => 'application/json' ]; @@ -68,9 +75,16 @@ public function execute($integrationData, $fieldValues) $actions = $integrationDetails->actions; $defaultDataConf = $integrationDetails->default; $selectedAction = $integrationDetails->selectedActions; - $oldToken = $integrationDetails->tokenDetails->access_token; - $tokenDetails = self::tokenExpiryCheck($integrationDetails->tokenDetails, $integrationDetails->clientId, $integrationDetails->clientSecret); - if ($tokenDetails->access_token !== $oldToken) { + + $isConnectionAuth = !empty($integrationDetails->connection_id); + $tokenDetails = self::normalizeConnectionToken($integrationDetails->tokenDetails); + $oldToken = $tokenDetails->access_token ?? ''; + + if (!$isConnectionAuth) { + $tokenDetails = self::tokenExpiryCheck($tokenDetails, $integrationDetails->clientId, $integrationDetails->clientSecret); + } + + if (!$isConnectionAuth && $tokenDetails->access_token !== $oldToken) { self::saveRefreshedToken($this->integrationID, $tokenDetails); } if ( @@ -105,7 +119,9 @@ private static function tokenExpiryCheck($token, $clientId, $clientSecret) return false; } - if ((\intval($token->generates_on) + (55 * 60)) < time()) { + $generatedOn = !empty($token->generates_on) ? (int) $token->generates_on : (int) ($token->generated_at ?? 0); + + if ($generatedOn > 0 && ($generatedOn + (55 * 60)) < time()) { $refreshToken = self::refreshToken($token->refresh_token, $clientId, $clientSecret); if (is_wp_error($refreshToken) || !empty($refreshToken->error)) { return false; @@ -115,6 +131,7 @@ private static function tokenExpiryCheck($token, $clientId, $clientSecret) $token->access_token = $refreshToken->access_token; $token->expires_in = $refreshToken->expires_in; $token->generates_on = $refreshToken->generates_on; + $token->generated_at = $refreshToken->generated_at; $token->refresh_token = $refreshToken->refresh_token; } } @@ -141,6 +158,20 @@ private static function refreshToken($refresh_token, $clientId, $clientSecret) } $token = $apiResponse; $token->generates_on = time(); + $token->generated_at = $token->generates_on; + + return $token; + } + + private static function normalizeConnectionToken($token) + { + if (!\is_object($token)) { + $token = (object) []; + } + + if (empty($token->generates_on) && !empty($token->generated_at)) { + $token->generates_on = (int) $token->generated_at; + } return $token; } diff --git a/backend/Admin/Admin_Bar.php b/backend/Admin/Admin_Bar.php index aa842ada0..d70bda6cc 100644 --- a/backend/Admin/Admin_Bar.php +++ b/backend/Admin/Admin_Bar.php @@ -43,6 +43,7 @@ public function AdminMenu() $submenu['bit-integrations'] = [ [__('All Integrations', 'bit-integrations'), $capability, 'admin.php?page=bit-integrations#/'], + [__('Connections', 'bit-integrations'), $capability, 'admin.php?page=bit-integrations#/connections'], [__('Doc & Support', 'bit-integrations'), $capability, 'admin.php?page=bit-integrations#/doc-support'], [__('Settings', 'bit-integrations'), $capability, 'admin.php?page=bit-integrations#/app-settings'], ]; diff --git a/backend/Authorization/AbstractBaseAuthorization.php b/backend/Authorization/AbstractBaseAuthorization.php new file mode 100644 index 000000000..3b1ef440d --- /dev/null +++ b/backend/Authorization/AbstractBaseAuthorization.php @@ -0,0 +1,328 @@ +connectionId = (int) $connectionId; + } + + abstract public function getAccessToken(); + + abstract public function getAuthHeadersOrParams(); + + /** + * Test authorization credentials by calling an API endpoint. + * + * @param null|mixed $payload + */ + public function authorize(string $apiEndpoint, string $method = 'GET', $payload = null, array $headers = []): array + { + $apiEndpoint = trim($apiEndpoint); + $method = strtoupper(trim($method)); + + if ($apiEndpoint === '') { + return [ + 'error' => true, + 'message' => __('API endpoint is required', 'bit-integrations'), + ]; + } + + $authConfig = $this->getAuthHeadersOrParams(); + + if (!\is_array($authConfig)) { + return [ + 'error' => true, + 'message' => __('Invalid authorization config', 'bit-integrations'), + ]; + } + + if (isset($authConfig['error']) && $authConfig['error']) { + return $authConfig; + } + + $url = $apiEndpoint; + $authLocation = $authConfig['authLocation'] ?? 'header'; + $authData = (isset($authConfig['data']) && \is_array($authConfig['data'])) ? $authConfig['data'] : []; + + if (!empty($authData)) { + if ($authLocation === 'query') { + if ($method === 'GET') { + // HttpHelper::get appends $payload as query string. Merge auth data + // into $payload so a single, deduplicated query is emitted. Caller + // payload keys win on collision. + $payload = array_merge($authData, \is_array($payload) ? $payload : []); + } else { + $query = http_build_query($authData); + $separator = strpos($url, '?') !== false ? '&' : '?'; + $url .= $separator . $query; + } + } else { + $headers = array_merge($headers, $authData); + } + } + + $requestOptions = $this->buildRequestOptionsFromAuthDetails(); + $response = $this->sendRequest($url, $method === '' ? 'GET' : $method, $payload, $headers, $requestOptions); + + if (is_wp_error($response)) { + return [ + 'error' => true, + 'message' => $response->get_error_message(), + 'response' => $response, + ]; + } + + if ((\is_object($response) && !empty($response->error)) || (\is_array($response) && !empty($response['error']))) { + $fallback = __('Authorization failed', 'bit-integrations'); + + return [ + 'error' => true, + 'message' => \is_object($response) ? ($response->error ?? $fallback) : ($response['error'] ?? $fallback), + 'response' => $response, + ]; + } + + if (isset(HttpHelper::$responseCode) && ((int) HttpHelper::$responseCode < 200 || (int) HttpHelper::$responseCode >= 300)) { + return [ + 'error' => true, + 'message' => __('Authorization failed', 'bit-integrations'), + 'response' => $response, + ]; + } + + return [ + 'success' => true, + 'response' => $response, + ]; + } + + public function getConnectionId(): int + { + return (int) $this->connectionId; + } + + /** + * Region-aware providers (Zoho .com/.in/.eu, Salesforce instance_url, MailChimp dc-prefix) + * should persist their resolved API base in auth_details under `endpoint_base` or + * `api_domain`. RecordApiHelper reads it via $handler->getEndpointBase() to avoid + * scattering region logic across the codebase. Subclasses may override. + */ + public function getEndpointBase(): ?string + { + $details = $this->getAuthDetails(); + + if (!\is_array($details)) { + return null; + } + + foreach (['endpoint_base', 'api_domain', 'instance_url', 'apiDomain'] as $key) { + if (!empty($details[$key]) && \is_string($details[$key])) { + return rtrim($details[$key], '/'); + } + } + + return null; + } + + public function getLastError(): ?array + { + return $this->lastError; + } + + protected function setLastError(string $message, $response = null): void + { + $this->lastError = [ + 'error' => true, + 'message' => $message, + 'response' => $response, + ]; + } + + protected function clearLastError(): void + { + $this->lastError = null; + } + + public function setAuthDetailsOverride(array $authDetails) + { + $this->authDetailsOverride = $authDetails; + + return $this; + } + + public function clearAuthDetailsOverride() + { + $this->authDetailsOverride = null; + + return $this; + } + + public function getConnection() + { + if ($this->connection === null) { + $this->connection = $this->loadConnection(); + } + + return $this->connection; + } + + public function getAuthDetails() + { + if (\is_array($this->authDetailsOverride)) { + return $this->authDetailsOverride; + } + + $connection = $this->getConnection(); + + if (!$connection) { + return null; + } + + $authDetails = $this->decodeAuthDetails($connection->auth_details ?? null); + + if (empty($authDetails)) { + return $authDetails; + } + + $encryptKeys = AuthDataCodec::parseEncryptKeys($connection->encrypt_keys ?? ''); + + return AuthDataCodec::decryptValues($authDetails, $encryptKeys); + } + + public function isTokenExpired($generatedAt, $expiresIn): bool + { + if (empty($generatedAt) || empty($expiresIn) || (int) $expiresIn <= 0) { + return false; + } + + return time() > ((int) $generatedAt + (int) $expiresIn - 30); + } + + public function updateAuthDetails(array $authDetails): bool + { + $connection = $this->getConnection(); + + if (!$connection) { + return false; + } + + $encryptKeys = AuthDataCodec::parseEncryptKeys($connection->encrypt_keys ?? ''); + $authDetails = AuthDataCodec::encryptValues($authDetails, $encryptKeys); + + $connectionModel = new ConnectionModel(); + $result = $connectionModel->update( + [ + 'auth_details' => wp_json_encode($authDetails), + 'updated_at' => current_time('mysql'), + ], + ['id' => $this->connectionId] + ); + + if (is_wp_error($result) && $result->get_error_code() !== 'result_empty') { + return false; + } + + $this->connection = null; + + return true; + } + + protected function decodeAuthDetails($value): array + { + if (\is_array($value)) { + return $value; + } + + if (\is_object($value)) { + return json_decode(wp_json_encode($value), true) ?: []; + } + + if (\is_string($value) && $value !== '') { + $decoded = json_decode($value, true); + + return \is_array($decoded) ? $decoded : []; + } + + return []; + } + + protected function http() + { + return new HttpHelper(); + } + + protected function sendRequest(string $url, string $method, $payload, array $headers, array $options = []) + { + switch ($method) { + case 'GET': + return HttpHelper::get($url, $payload, $headers, $options); + + case 'POST': + return HttpHelper::post($url, $payload, $headers, $options); + + default: + return HttpHelper::request($url, $method, $payload, $headers, $options); + } + } + + protected function buildRequestOptionsFromAuthDetails(): array + { + $authDetails = $this->getAuthDetails(); + + if (!\is_array($authDetails)) { + return []; + } + + $sslVerify = AuthDataCodec::normalizeSslVerify($authDetails['ssl_verify'] ?? null); + + if ($sslVerify === null) { + return []; + } + + return [ + // WordPress HTTP API option + 'sslverify' => $sslVerify, + // Kept for compatibility with existing code paths using "verify" + 'verify' => $sslVerify, + ]; + } + + private function loadConnection() + { + if ($this->connectionId <= 0) { + return; + } + + $connectionModel = new ConnectionModel(); + $result = $connectionModel->get( + ['id', 'app_slug', 'auth_type', 'connection_name', 'account_name', 'encrypt_keys', 'auth_details', 'status'], + ['id' => $this->connectionId], + 1 + ); + + if (is_wp_error($result) || empty($result[0])) { + return; + } + + return $result[0]; + } +} diff --git a/backend/Authorization/ApiKey/ApiKeyAuthorization.php b/backend/Authorization/ApiKey/ApiKeyAuthorization.php new file mode 100644 index 000000000..1f5da8fca --- /dev/null +++ b/backend/Authorization/ApiKey/ApiKeyAuthorization.php @@ -0,0 +1,46 @@ +getAuthDetails(); + + if (empty($authDetails) || !isset($authDetails['value']) || $authDetails['value'] === '') { + return [ + 'error' => true, + 'message' => __('Token field is missing', 'bit-integrations'), + ]; + } + + return $authDetails['value']; + } + + public function getAuthHeadersOrParams() + { + $authDetails = $this->getAuthDetails(); + + if (empty($authDetails) || !isset($authDetails['value'])) { + return [ + 'error' => true, + 'message' => __('Token field is missing', 'bit-integrations'), + ]; + } + + $key = $authDetails['key'] ?? 'X-API-Key'; + $location = $authDetails['addTo'] ?? 'header'; + + return [ + 'authLocation' => $location, + 'data' => [$key => $authDetails['value']], + ]; + } +} diff --git a/backend/Authorization/AuthorizationFactory.php b/backend/Authorization/AuthorizationFactory.php new file mode 100644 index 000000000..e9e41c259 --- /dev/null +++ b/backend/Authorization/AuthorizationFactory.php @@ -0,0 +1,63 @@ +getAuthDetails(); + + if (empty($authDetails) || empty($authDetails['username'])) { + return [ + 'error' => true, + 'message' => __('username field is missing', 'bit-integrations'), + ]; + } + + $password = $authDetails['password'] ?? ''; + + return 'Basic ' . base64_encode($authDetails['username'] . ':' . $password); + } + + public function getAuthHeadersOrParams() + { + $token = $this->getAccessToken(); + + if (\is_array($token) && !empty($token['error'])) { + return $token; + } + + return [ + 'authLocation' => 'header', + 'data' => ['Authorization' => $token], + ]; + } +} diff --git a/backend/Authorization/Bearer/BearerTokenAuthorization.php b/backend/Authorization/Bearer/BearerTokenAuthorization.php new file mode 100644 index 000000000..1921aec05 --- /dev/null +++ b/backend/Authorization/Bearer/BearerTokenAuthorization.php @@ -0,0 +1,40 @@ +getAuthDetails(); + + if (empty($authDetails) || empty($authDetails['token'])) { + return [ + 'error' => true, + 'message' => __('access token field is missing', 'bit-integrations'), + ]; + } + + return 'Bearer ' . $authDetails['token']; + } + + public function getAuthHeadersOrParams() + { + $token = $this->getAccessToken(); + + if (\is_array($token) && !empty($token['error'])) { + return $token; + } + + return [ + 'authLocation' => 'header', + 'data' => ['Authorization' => $token], + ]; + } +} diff --git a/backend/Authorization/OAuth1/OAuth1Authorization.php b/backend/Authorization/OAuth1/OAuth1Authorization.php new file mode 100644 index 000000000..81526c005 --- /dev/null +++ b/backend/Authorization/OAuth1/OAuth1Authorization.php @@ -0,0 +1,50 @@ +getAuthDetails(); + + if (empty($authDetails) || empty($authDetails['access_token'])) { + return [ + 'error' => true, + 'message' => __('OAuth1 access token field is missing', 'bit-integrations'), + ]; + } + + return $authDetails['access_token']; + } + + public function getAuthHeadersOrParams() + { + $authDetails = $this->getAuthDetails(); + + if (empty($authDetails['consumer_key']) || empty($authDetails['access_token'])) { + return [ + 'error' => true, + 'message' => __('OAuth1 consumer key or access token is missing', 'bit-integrations'), + ]; + } + + $consumerKeyParam = $authDetails['consumer_key_param'] ?? ($authDetails['consumerKeyParam'] ?? 'oauth_consumer_key'); + $tokenParam = $authDetails['token_param'] ?? ($authDetails['tokenParam'] ?? 'oauth_token'); + $location = $authDetails['addTo'] ?? 'query'; + + return [ + 'authLocation' => $location, + 'data' => [ + $consumerKeyParam => $authDetails['consumer_key'], + $tokenParam => $authDetails['access_token'], + ], + ]; + } +} diff --git a/backend/Authorization/OAuth2/OAuth2Authorization.php b/backend/Authorization/OAuth2/OAuth2Authorization.php new file mode 100644 index 000000000..53ad6d6be --- /dev/null +++ b/backend/Authorization/OAuth2/OAuth2Authorization.php @@ -0,0 +1,197 @@ +bodyParams = $bodyParams; + + return $this; + } + + public function setRefreshTokenUrl($refreshTokenUrl) + { + $this->refreshTokenUrl = $refreshTokenUrl; + + return $this; + } + + public function setTokenPrefix($prefix) + { + $this->tokenPrefix = $prefix === null ? '' : (string) $prefix; + + return $this; + } + + public function getAuthDetails(): ?array + { + $this->clearLastError(); + + $authDetails = parent::getAuthDetails(); + + if (empty($authDetails)) { + $this->setLastError(__('Connection auth details are missing', 'bit-integrations')); + + return null; + } + + $generatedAt = $authDetails['generated_at'] ?? null; + $expiresIn = $authDetails['expires_in'] ?? null; + + if ($this->isTokenExpired($generatedAt, $expiresIn)) { + return $this->refreshAccessToken($authDetails); + } + + return $authDetails; + } + + public function getAccessToken() + { + $authDetails = $this->getAuthDetails(); + + if ($authDetails === null) { + return $this->getLastError() ?: [ + 'error' => true, + 'message' => __('Connection auth details are missing', 'bit-integrations'), + ]; + } + + if (empty($authDetails['access_token'])) { + $this->setLastError(__('Access token is missing', 'bit-integrations')); + + return $this->getLastError(); + } + + return $this->tokenPrefix . $authDetails['access_token']; + } + + public function getAuthHeadersOrParams() + { + $token = $this->getAccessToken(); + + if (\is_array($token) && !empty($token['error'])) { + return $token; + } + + return [ + 'authLocation' => 'header', + 'data' => ['Authorization' => $token], + ]; + } + + public function refreshAccessToken(array $authDetails): ?array + { + $url = $this->refreshTokenUrl ?: ($authDetails['refresh_token_url'] ?? ($authDetails['refreshTokenUrl'] ?? '')); + + if (empty($url)) { + $this->setLastError(__('Refresh token endpoint is missing', 'bit-integrations')); + + return null; + } + + $body = $this->bodyParams ?: $this->buildRefreshBody($authDetails); + $headers = $this->buildRefreshHeaders($authDetails); + + $requestOptions = []; + $sslVerify = AuthDataCodec::normalizeSslVerify($authDetails['ssl_verify'] ?? null); + + if ($sslVerify !== null) { + $requestOptions = [ + 'sslverify' => $sslVerify, + 'verify' => $sslVerify, + ]; + } + + $response = HttpHelper::post($url, $body, $headers, $requestOptions); + + if (is_wp_error($response)) { + $this->setLastError($response->get_error_message(), $response); + + return null; + } + + if (HttpHelper::$responseCode < 200 || HttpHelper::$responseCode >= 300 || (\is_object($response) && isset($response->error))) { + $message = \is_object($response) && isset($response->error) + ? $response->error + : __('Token refresh failed', 'bit-integrations'); + $this->setLastError((string) $message, $response); + + return null; + } + + $response = \is_object($response) ? json_decode(wp_json_encode($response), true) : (array) $response; + + $authDetails['access_token'] = $response['access_token'] ?? ($authDetails['access_token'] ?? ''); + + if (!empty($response['refresh_token'])) { + $authDetails['refresh_token'] = $response['refresh_token']; + } + + if (isset($response['expires_in'])) { + $authDetails['expires_in'] = (int) $response['expires_in']; + } + + $authDetails['generated_at'] = time(); + + $this->updateAuthDetails($authDetails); + + return $authDetails; + } + + private function buildRefreshBody(array $authDetails): array + { + $grantType = $authDetails['grant_type'] ?? 'authorization_code'; + $body = [ + 'grant_type' => $grantType === 'client_credentials' ? 'client_credentials' : 'refresh_token', + ]; + + // Body auth is the default. Header auth puts client credentials in Authorization + // header instead — see buildRefreshHeaders. + if ($this->resolveClientAuthMode($authDetails) !== 'header') { + $body['client_id'] = $authDetails['client_id'] ?? ($authDetails['clientId'] ?? ''); + $body['client_secret'] = $authDetails['client_secret'] ?? ($authDetails['clientSecret'] ?? ''); + } + + if (!empty($authDetails['refresh_token'])) { + $body['refresh_token'] = $authDetails['refresh_token']; + } + + return $body; + } + + private function buildRefreshHeaders(array $authDetails): array + { + $headers = ['Content-Type' => 'application/x-www-form-urlencoded']; + + if ($this->resolveClientAuthMode($authDetails) === 'header') { + $clientId = $authDetails['client_id'] ?? ($authDetails['clientId'] ?? ''); + $clientSecret = $authDetails['client_secret'] ?? ($authDetails['clientSecret'] ?? ''); + $headers['Authorization'] = 'Basic ' . base64_encode($clientId . ':' . $clientSecret); + } + + return $headers; + } + + private function resolveClientAuthMode(array $authDetails): string + { + $mode = $authDetails['clientAuthentication'] ?? ($authDetails['client_authentication'] ?? 'body'); + + return $mode === 'header' ? 'header' : 'body'; + } +} diff --git a/backend/Authorization/Support/AuthDataCodec.php b/backend/Authorization/Support/AuthDataCodec.php new file mode 100644 index 000000000..b3795deab --- /dev/null +++ b/backend/Authorization/Support/AuthDataCodec.php @@ -0,0 +1,134 @@ + get_rest_url(), + 'separator' => $wp_rewrite->permalink_structure ? '?' : '&', + ]; + case 'ROOT_URI': return set_url_scheme(plugins_url('', self::get('MAIN_FILE')), wp_parse_url(home_url())['scheme']); @@ -183,14 +191,15 @@ public static function getFrontendConfig() { $frontendConfig = apply_filters( // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- hook is prefixed via Config::VAR_PREFIX. - Config::withPrefix('localized_script'), + self::withPrefix('localized_script'), [ - 'nonce' => wp_create_nonce(Config::withPrefix('nonce')), - 'assetsURL' => Config::get('ASSET_URI'), + 'nonce' => wp_create_nonce(self::withPrefix('nonce')), + 'assetsURL' => self::get('ASSET_URI'), 'baseURL' => get_admin_url(null, 'admin.php?page=bit-integrations#'), 'siteURL' => site_url(), 'ajaxURL' => admin_url('admin-ajax.php'), - 'api' => Config::get('API_URL'), + 'api' => self::get('API_URL'), + 'wp_api_url' => self::get('WP_API_URL'), 'dateFormat' => get_option('date_format'), 'timeFormat' => get_option('time_format'), 'timeZone' => DateTimeHelper::wp_timezone_string(), diff --git a/backend/Core/Database/ConnectionModel.php b/backend/Core/Database/ConnectionModel.php new file mode 100644 index 000000000..8aed36bff --- /dev/null +++ b/backend/Core/Database/ConnectionModel.php @@ -0,0 +1,14 @@ +prefix}btcbi_connections` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `app_slug` varchar(191) NOT NULL, + `auth_type` varchar(50) NOT NULL DEFAULT 'oauth2', + `connection_name` varchar(255) DEFAULT NULL, + `account_name` varchar(255) DEFAULT NULL, + `encrypt_keys` text DEFAULT NULL, + `auth_details` longtext DEFAULT NULL, + `status` tinyint(1) DEFAULT 1, + `user_id` bigint(20) unsigned DEFAULT NULL, + `created_at` datetime DEFAULT NULL, + `updated_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `app_slug` (`app_slug`), + KEY `account_name` (`account_name`), + KEY `status` (`status`) ) {$collate};" ]; diff --git a/backend/Core/Util/CredentialInjector.php b/backend/Core/Util/CredentialInjector.php new file mode 100644 index 000000000..fc91b038e --- /dev/null +++ b/backend/Core/Util/CredentialInjector.php @@ -0,0 +1,66 @@ +connection_id ?? 0); + + if ($connectionId <= 0) { + return; + } + + if (!property_exists($controllerClass, 'authConfig')) { + return; + } + + $config = $controllerClass::$authConfig; + $handler = AuthorizationFactory::getAuthorizationHandler( + $config['authType'], + $connectionId, + $config['slug'] + ); + $authDetails = $handler->getAuthDetails(); + + foreach ($config['fields'] as $oldField => $authKey) { + if ($oldField === '__object') { + // Nested object injection: $authKey = [$targetProp, [$key1, $key2, ...]] + [$targetProp, $keys] = $authKey; + $obj = new \stdClass(); + foreach ($keys as $key) { + if ($key === 'generates_on' && empty($authDetails[$key]) && !empty($authDetails['generated_at'])) { + $obj->$key = (int) $authDetails['generated_at']; + continue; + } + + if ($key === 'generated_at' && empty($authDetails[$key]) && !empty($authDetails['generates_on'])) { + $obj->$key = (int) $authDetails['generates_on']; + continue; + } + + $obj->$key = $authDetails[$key] ?? ''; + } + $target->$targetProp = $obj; + } else { + $target->$oldField = $authDetails[$authKey] ?? ''; + } + } + } +} diff --git a/backend/Core/Util/Hash.php b/backend/Core/Util/Hash.php new file mode 100644 index 000000000..4bc1f45b5 --- /dev/null +++ b/backend/Core/Util/Hash.php @@ -0,0 +1,74 @@ + 'AND' | 'OR', // outer combiner across groups (default AND) + * 'groups' => [ + * [ + * 'logic' => 'AND' | 'OR', // combines this group's checks (default AND) + * 'checks' => [ + * ['type' => 'class', 'value' => 'Foo'], + * ['type' => 'function', 'value' => 'foo_init'], + * ], + * ], + * ], + * ] + * + * Flat shape (single implicit group): + * ['checks' => [...], 'logic' => 'AND' | 'OR'] + */ +final class PluginCheck +{ + private const ALLOWED_TYPES = ['class', 'function', 'constant', 'plugin_file']; + + private const ALLOWED_LOGIC = ['AND', 'OR']; + + /** + * @param array $spec accepts groups OR flat checks; nested values may be stdClass + * + * @return array{available:bool,message?:string} + */ + public static function evaluate(array $spec): array + { + $groups = self::normalizeGroups($spec); + + if (empty($groups)) { + return [ + 'available' => false, + 'message' => __('Plugin checks are required', 'bit-integrations'), + ]; + } + + $outerLogic = self::normalizeLogic($spec['logic'] ?? null); + $groupResults = []; + + foreach ($groups as $group) { + $checkResults = []; + + foreach ($group['checks'] as $check) { + $check = AuthDataCodec::toArray($check); + + $type = $check['type'] ?? null; + $value = $check['value'] ?? null; + + if (!\in_array($type, self::ALLOWED_TYPES, true) || !\is_scalar($value) || $value === '') { + continue; + } + + $checkResults[] = self::matches($type, (string) $value); + } + + if (empty($checkResults)) { + continue; + } + + $groupResults[] = self::combine($checkResults, $group['logic']); + } + + if (empty($groupResults)) { + return [ + 'available' => false, + 'message' => __('No valid Plugin checks were provided', 'bit-integrations'), + ]; + } + + if (self::combine($groupResults, $outerLogic)) { + return ['available' => true]; + } + + return [ + 'available' => false, + 'message' => __('Plugin is not installed or activated', 'bit-integrations'), + ]; + } + + /** + * Normalize spec into a list of groups. A flat `checks` array is treated as + * a single implicit group so callers keep working. + * + * @return array + */ + private static function normalizeGroups(array $spec): array + { + $rawGroups = AuthDataCodec::toArray($spec['groups'] ?? null); + + if (!empty($rawGroups)) { + $groups = []; + + foreach ($rawGroups as $group) { + $group = AuthDataCodec::toArray($group); + $checks = AuthDataCodec::toArray($group['checks'] ?? null); + + if (empty($checks)) { + continue; + } + + $groups[] = [ + 'logic' => self::normalizeLogic($group['logic'] ?? null), + 'checks' => $checks, + ]; + } + + return $groups; + } + + $checks = AuthDataCodec::toArray($spec['checks'] ?? null); + + if (empty($checks)) { + return []; + } + + return [[ + 'logic' => 'AND', + 'checks' => $checks, + ]]; + } + + private static function normalizeLogic($raw): string + { + if (!\is_scalar($raw)) { + return 'AND'; + } + + $normalized = strtoupper((string) $raw); + + return \in_array($normalized, self::ALLOWED_LOGIC, true) ? $normalized : 'AND'; + } + + /** + * @param array $results + */ + private static function combine(array $results, string $logic): bool + { + return $logic === 'OR' + ? \in_array(true, $results, true) + : !\in_array(false, $results, true); + } + + private static function matches(string $type, string $value): bool + { + switch ($type) { + case 'class': + return class_exists($value); + case 'function': + return \function_exists($value); + case 'constant': + return \defined($value); + case 'plugin_file': + if (!\function_exists('is_plugin_active')) { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + } + + return is_plugin_active($value); + default: + return false; + } + } +} diff --git a/backend/Core/Util/Route.php b/backend/Core/Util/Route.php index da67275b2..5fcd769e7 100644 --- a/backend/Core/Util/Route.php +++ b/backend/Core/Util/Route.php @@ -144,6 +144,10 @@ public static function action() : (object) map_deep(wp_unslash($_GET), [__CLASS__, 'sanitizeValue']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended } + if (\is_object($data)) { + CredentialInjector::inject($data, $invokeable[0]); + } + $reflectionMethod = new ReflectionMethod($invokeable[0], $invokeable[1]); $response = $reflectionMethod->invoke($reflectionMethod->isStatic() ? null : new $invokeable[0](), $data); diff --git a/backend/Flow/Flow.php b/backend/Flow/Flow.php index fe20807aa..4eba3bf3d 100644 --- a/backend/Flow/Flow.php +++ b/backend/Flow/Flow.php @@ -4,6 +4,7 @@ use BitApps\Integrations\Core\Util\Capabilities; use BitApps\Integrations\Core\Util\Common; +use BitApps\Integrations\Core\Util\CredentialInjector; use BitApps\Integrations\Core\Util\CustomFuncValidator; use BitApps\Integrations\Core\Util\IpTool; use BitApps\Integrations\Core\Util\SmartTags; @@ -515,6 +516,8 @@ public static function execute($triggered_entity, $triggered_entity_id, $data, $ } if (!\is_null($integrationName) && $integration = static::isActionExists($integrationName)) { + CredentialInjector::inject($flowData->flow_details, $integration); + $handler = new $integration($flowData->id); if (isset($flowData->flow_details->field_map)) { $sptagData = self::specialTagMappingValue($flowData->flow_details->field_map); diff --git a/backend/Routes/ajax.php b/backend/Routes/ajax.php index aab5e879a..25ed56c64 100644 --- a/backend/Routes/ajax.php +++ b/backend/Routes/ajax.php @@ -6,6 +6,7 @@ } use BitApps\Integrations\controller\AuthDataController; +use BitApps\Integrations\controller\ConnectionController; use BitApps\Integrations\controller\IntegrationTagController; use BitApps\Integrations\controller\PostController; use BitApps\Integrations\controller\UserController; @@ -58,3 +59,14 @@ Route::get('auth/get', [AuthDataController::class, 'getAuthData']); Route::get('auth/getbyId', [AuthDataController::class, 'getAuthDataById']); Route::post('auth/account/delete', [AuthDataController::class, 'deleteAuthData']); + +// Connection management (encrypted reusable credentials) +Route::get('connections/list', [ConnectionController::class, 'index']); +Route::get('connections/get', [ConnectionController::class, 'getById']); +Route::post('connections/authorize', [ConnectionController::class, 'authorize']); +Route::post('connections/oauth2/exchange', [ConnectionController::class, 'oauth2Exchange']); +Route::post('connections/verify-plugin-activation', [ConnectionController::class, 'verifyPluginActivation']); +Route::post('connections/save', [ConnectionController::class, 'save']); +Route::post('connections/update', [ConnectionController::class, 'update']); +Route::post('connections/reauthorize', [ConnectionController::class, 'reauthorize']); +Route::post('connections/delete', [ConnectionController::class, 'delete']); diff --git a/backend/controller/ConnectionController.php b/backend/controller/ConnectionController.php new file mode 100644 index 000000000..d6f0f7b7e --- /dev/null +++ b/backend/controller/ConnectionController.php @@ -0,0 +1,892 @@ +guard(); + + $appSlug = $this->sanitizeScalar($request->app_slug ?? ''); + $includeLinkedIntegrations = $this->isTruthy($request->include_linked_integrations ?? false); + $condition = ['status' => ConnectionModel::STATUS_VERIFIED]; + + if ($appSlug !== '') { + $condition['app_slug'] = $appSlug; + } + + $rows = (new ConnectionModel())->get(self::COLUMNS, $condition, null, null, 'id', 'DESC'); + + if (is_wp_error($rows)) { + wp_send_json_success(['data' => []]); + } + + $linkedIntegrationMap = []; + + if ($includeLinkedIntegrations) { + $linkedIntegrationMap = $this->buildLinkedIntegrationMap(); + + if (is_wp_error($linkedIntegrationMap)) { + wp_send_json_error($linkedIntegrationMap->get_error_message()); + } + } + + $payload = []; + + foreach ($rows as $row) { + $linkedIntegrations = $includeLinkedIntegrations + ? ($linkedIntegrationMap[(int) ($row->id ?? 0)] ?? []) + : null; + + $payload[] = $this->formatListRow($row, $linkedIntegrations); + } + + wp_send_json_success(['data' => $payload]); + } + + public function getById($request) + { + $this->guard(); + + $id = $this->normalizeId($request); + + if ($id === 0) { + wp_send_json_error(__('Connection id is required', 'bit-integrations')); + } + + $row = $this->findById($id); + + if (is_wp_error($row)) { + wp_send_json_error($row->get_error_message()); + } + + wp_send_json_success(['data' => $this->formatRow($row)]); + } + + public function save($request) + { + $this->guard(); + + $payload = $this->buildPayload($request, false); + + if (is_wp_error($payload)) { + wp_send_json_error($payload->get_error_message()); + } + + // Upsert policy: same (app_slug, account_name) is treated as the same connection + // and gets refreshed. Prevents accidental duplicates when re-authorizing the same + // account from a flow builder. Skipped when account_name is empty. + $existingId = $this->findExistingIdForAccount($payload['app_slug'], $payload['account_name']); + + if ($existingId > 0) { + $updated = $this->persist($payload, $existingId); + + if (is_wp_error($updated)) { + wp_send_json_error($updated->get_error_message()); + } + + wp_send_json_success(['data' => $this->formatRow($updated)]); + } + + $created = $this->persist($payload, 0); + + if (is_wp_error($created)) { + wp_send_json_error($created->get_error_message()); + } + + wp_send_json_success(['data' => $this->formatRow($created)]); + } + + public function update($request) + { + $this->guard(); + + $id = $this->normalizeId($request); + + if ($id === 0) { + wp_send_json_error(__('Connection id is required', 'bit-integrations')); + } + + $name = $this->sanitizeScalar($request->connection_name ?? ''); + + if ($name === '') { + wp_send_json_error(__('Connection name is required', 'bit-integrations')); + } + + $existing = $this->findById($id); + + if (is_wp_error($existing)) { + wp_send_json_error($existing->get_error_message()); + } + + $update = [ + 'connection_name' => $name, + 'updated_at' => current_time('mysql'), + ]; + + if (isset($request->status)) { + $update['status'] = absint($request->status); + } + + $result = (new ConnectionModel())->update($update, ['id' => $id]); + + if (is_wp_error($result) && $result->get_error_code() !== 'result_empty') { + wp_send_json_error($result->get_error_message()); + } + + $row = $this->findById($id); + + if (is_wp_error($row)) { + wp_send_json_error($row->get_error_message()); + } + + wp_send_json_success(['data' => $this->formatRow($row)]); + } + + public function reauthorize($request) + { + $this->guard(); + + $id = $this->normalizeId($request); + + if ($id === 0) { + wp_send_json_error(__('Connection id is required', 'bit-integrations')); + } + + if (empty($request->auth_details)) { + wp_send_json_error(__('Authorization details are required', 'bit-integrations')); + } + + $existing = $this->findById($id); + + if (is_wp_error($existing)) { + wp_send_json_error($existing->get_error_message()); + } + + $payload = [ + 'app_slug' => $existing->app_slug, + 'auth_type' => (string) $existing->auth_type, + 'connection_name' => $existing->connection_name, + 'account_name' => $existing->account_name, + 'status' => ConnectionModel::STATUS_VERIFIED, + 'auth_details' => $this->normalizeArray($request->auth_details), + 'encrypt_keys' => $this->resolveEncryptKeys($request), + ]; + + if (!empty($request->account_name)) { + $payload['account_name'] = $this->sanitizeScalar($request->account_name); + } + + if (!empty($request->connection_name)) { + $payload['connection_name'] = $this->sanitizeScalar($request->connection_name); + } + + $row = $this->persist($payload, $id); + + if (is_wp_error($row)) { + wp_send_json_error($row->get_error_message()); + } + + wp_send_json_success(['data' => $this->formatRow($row)]); + } + + public function authorize($request) + { + $this->guard(); + + $authType = $this->sanitizeScalar($request->auth_type ?? ''); + + if (empty($authType)) { + wp_send_json_error(__('Auth type is required', 'bit-integrations')); + } + + if (!\in_array($authType, self::ALLOWED_AUTH_TYPES, true)) { + wp_send_json_error(__('Invalid auth type', 'bit-integrations')); + } + + if ($authType === AuthorizationType::WP_PLUGIN_CHECK) { + wp_send_json_error( + __('WP Plugin Check does not use credential authorization. Use Plugin check endpoint instead.', 'bit-integrations'), + 400 + ); + } + + $appSlug = $this->sanitizeScalar($request->app_slug ?? ''); + $apiEndpoint = esc_url_raw((string) ($request->api_endpoint ?? '')); + $method = strtoupper($this->sanitizeScalar($request->method ?? 'GET')); + $payload = isset($request->payload) ? $this->normalizePayload($request->payload) : null; + $headers = $this->normalizeHeaders($request->headers ?? []); + + if (empty($request->auth_details)) { + wp_send_json_error(__('Authorization details are required', 'bit-integrations')); + } + + if (empty($apiEndpoint)) { + wp_send_json_error(__('API endpoint is required', 'bit-integrations')); + } + + if (!$this->isPublicHttpsUrl($apiEndpoint)) { + wp_send_json_error(__('API endpoint must be a public https endpoint', 'bit-integrations'), 400); + } + + $authDetails = $this->normalizeArray($request->auth_details); + + if (empty($authDetails)) { + wp_send_json_error(__('Authorization details are required', 'bit-integrations')); + } + + try { + $handler = AuthorizationFactory::getAuthorizationHandler($authType, 0, $appSlug); + $handler->setAuthDetailsOverride($authDetails); + $result = $handler->authorize($apiEndpoint, $method, $payload, $headers); + } catch (Exception $e) { + wp_send_json_error($e->getMessage()); + } + + if (!empty($result['error'])) { + wp_send_json_error( + [ + 'message' => $result['message'] ?? __('Authorization failed', 'bit-integrations'), + 'response' => $result['response'] ?? null, + ], + 400 + ); + } + + wp_send_json_success( + [ + 'message' => __('Authorization successful', 'bit-integrations'), + 'response' => $result['response'] ?? null, + ], + 200 + ); + } + + public function delete($request) + { + $this->guard(); + + $id = $this->normalizeId($request); + + if ($id === 0) { + wp_send_json_error(__('Connection id is required', 'bit-integrations')); + } + + $linkedIntegrations = $this->getLinkedIntegrations($id); + + if (is_wp_error($linkedIntegrations)) { + wp_send_json_error($linkedIntegrations->get_error_message()); + } + + if (!empty($linkedIntegrations)) { + $linkedIntegrationCount = \count($linkedIntegrations); + wp_send_json_error( + [ + 'message' => wp_sprintf( + __('Connection is linked with %d integration(s). Remove this connection from those integrations before deleting.', 'bit-integrations'), + $linkedIntegrationCount + ), + 'linked_integrations' => $linkedIntegrations, + 'linked_count' => $linkedIntegrationCount, + ], + 409 + ); + } + + $result = (new ConnectionModel())->delete(['id' => $id]); + + if (is_wp_error($result)) { + wp_send_json_error($result->get_error_message()); + } + + wp_send_json_success(['id' => $id]); + } + + /** + * Confirm a WordPress-plugin Plugin is installed/active. No DB persistence — + * caller supplies a check spec (class/function/constant/plugin_file) with + * AND/OR logic (optionally grouped) and PluginCheck evaluates it. + * + * Spec sanitization lives in PluginCheck so this controller stays a thin + * adapter from the request shape to the evaluator. + * + * @param mixed $request + */ + public function verifyPluginActivation($request) + { + $this->guard(); + + $spec = ['logic' => $request->logic ?? null]; + + if (isset($request->groups)) { + $spec['groups'] = $request->groups; + } else { + $spec['checks'] = $request->checks ?? null; + } + + $result = PluginCheck::evaluate($spec); + + if (empty($result['available'])) { + wp_send_json_error( + $result['message'] ?? __('Plugin check failed', 'bit-integrations'), + 400 + ); + } + + wp_send_json_success(['available' => true]); + } + + /** + * Server-side OAuth2 token exchange (auth_code, pkce, client_credentials, refresh_token). + * Browsers cannot reach token endpoints (no CORS) and must not hold client_secret. + * + * @param mixed $request + */ + public function oauth2Exchange($request) + { + $this->guard(); + + $url = esc_url_raw((string) ($request->url ?? '')); + $method = strtoupper($this->sanitizeScalar($request->method ?? 'POST')); + $bodyParams = $this->normalizeArray($request->body_params ?? []); + $headers = $this->normalizeHeaders($request->headers ?? []); + $sslVerify = AuthDataCodec::normalizeSslVerify($request->ssl_verify ?? null); + + if ($url === '') { + wp_send_json_error(__('Token URL is required', 'bit-integrations')); + } + + if (!$this->isPublicHttpsUrl($url)) { + wp_send_json_error(__('Token URL must be a public https endpoint', 'bit-integrations'), 400); + } + + if (!isset($headers['Content-Type']) && !isset($headers['content-type'])) { + $headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + + $options = []; + + if ($sslVerify !== null) { + $options['sslverify'] = $sslVerify; + $options['verify'] = $sslVerify; + } + + $contentType = strtolower($headers['Content-Type'] ?? ($headers['content-type'] ?? '')); + $isJson = strpos($contentType, 'application/json') !== false; + // form-urlencoded: pass array, WP will http_build_query. JSON: encode. Default array. + $payload = $isJson ? wp_json_encode($bodyParams) : $bodyParams; + + $response = $method === 'GET' + ? HttpHelper::get($url, $bodyParams, $headers, $options) + : HttpHelper::request($url, $method === '' ? 'POST' : $method, $payload, $headers, $options); + + if (is_wp_error($response)) { + wp_send_json_error($response->get_error_message(), 400); + } + + $responseCode = isset(HttpHelper::$responseCode) ? (int) HttpHelper::$responseCode : 0; + $decoded = \is_object($response) ? Helper::jsonEncodeDecode($response) : $response; + + if ($responseCode < 200 || $responseCode >= 300 || (\is_array($decoded) && isset($decoded['error']))) { + if (\is_array($decoded) && isset($decoded['error_description'])) { + $errorMessage = $decoded['error_description']; + } elseif (\is_array($decoded) && isset($decoded['error']) && \is_string($decoded['error'])) { + $errorMessage = $decoded['error']; + } else { + $errorMessage = 'Token exchange failed'; + } + + wp_send_json_error( + [ + 'message' => $errorMessage, + 'response' => $decoded, + 'status' => $responseCode, + ], + 400 + ); + } + + wp_send_json_success(['data' => $decoded]); + } + + private function buildPayload($request, bool $isUpdate) + { + $appSlug = $this->sanitizeScalar($request->app_slug ?? ''); + $authType = $this->sanitizeScalar($request->auth_type ?? ''); + + if (!$isUpdate && $appSlug === '') { + return new WP_Error('missing_app_slug', __('App slug is required', 'bit-integrations')); + } + + if ($authType === AuthorizationType::WP_PLUGIN_CHECK) { + return new WP_Error( + 'invalid_auth_type', + __('WP Plugin Check does not require saving a reusable credential connection', 'bit-integrations') + ); + } + + if ($authType !== '' && !\in_array($authType, self::ALLOWED_AUTH_TYPES, true)) { + return new WP_Error('invalid_auth_type', __('Invalid auth type', 'bit-integrations')); + } + + if (empty($request->auth_details)) { + return new WP_Error('missing_auth_details', __('Authorization details are required', 'bit-integrations')); + } + + $authDetails = $this->normalizeArray($request->auth_details); + + if (empty($authDetails)) { + return new WP_Error('missing_auth_details', __('Authorization details are required', 'bit-integrations')); + } + + $accountName = $this->sanitizeScalar($request->account_name ?? ''); + $connectionName = $this->sanitizeScalar($request->connection_name ?? ''); + + if ($connectionName === '') { + $connectionName = $accountName !== '' ? $accountName : $appSlug; + } + + // Backfill account_name so findExistingIdForAccount upsert key is never empty — + // otherwise re-authorize creates duplicate rows for the same logical account. + if ($accountName === '') { + $accountName = $connectionName; + } + + return [ + 'app_slug' => $appSlug, + 'auth_type' => $authType !== '' ? $authType : AuthorizationType::OAUTH2, + 'connection_name' => $connectionName, + 'account_name' => $accountName, + 'auth_details' => $authDetails, + 'encrypt_keys' => $this->resolveEncryptKeys($request), + 'status' => isset($request->status) ? absint($request->status) : ConnectionModel::STATUS_VERIFIED, + ]; + } + + private function persist(array $payload, int $existingId) + { + $authDetails = $payload['auth_details']; + $encryptKeys = $payload['encrypt_keys']; + + if (!isset($authDetails['generated_at'])) { + $authDetails['generated_at'] = time(); + } + + $authDetails = AuthDataCodec::encryptValues($authDetails, $encryptKeys); + + $now = current_time('mysql'); + + $row = [ + 'app_slug' => $payload['app_slug'], + 'auth_type' => $payload['auth_type'], + 'connection_name' => $payload['connection_name'], + 'account_name' => $payload['account_name'], + 'encrypt_keys' => implode(',', $encryptKeys), + 'auth_details' => wp_json_encode($authDetails), + 'status' => $payload['status'], + 'updated_at' => $now, + ]; + + $connectionModel = new ConnectionModel(); + + if ($existingId > 0) { + $update = $connectionModel->update($row, ['id' => $existingId]); + + if (is_wp_error($update) && $update->get_error_code() !== 'result_empty') { + return $update; + } + + return $this->findById($existingId); + } + + $row['user_id'] = get_current_user_id(); + $row['created_at'] = $now; + + $insertId = $connectionModel->insert($row); + + if (is_wp_error($insertId)) { + return $insertId; + } + + return $this->findById((int) $insertId); + } + + /** + * Reject non-https URLs and hosts that resolve to private / loopback / reserved ranges. + * Token endpoints are always public https in practice; this closes SSRF surface for + * the server-side token exchange. + * + * Known-partial: literal IPs are filtered, but hostnames are NOT DNS-resolved here — + * a public hostname A-recording to a private IP would pass. Acceptable because the + * caller is an authenticated admin and the URL is supplied per-flow (not user-input + * from the public web). Add gethostbyname() screening if that threat model changes. + */ + private function isPublicHttpsUrl(string $url): bool + { + $parts = wp_parse_url($url); + + if (!$parts || empty($parts['scheme']) || empty($parts['host'])) { + return false; + } + + if (strtolower($parts['scheme']) !== 'https') { + return false; + } + + $host = strtolower($parts['host']); + + if (\in_array($host, ['localhost', 'localhost.localdomain', 'ip6-localhost', 'ip6-loopback'], true)) { + return false; + } + + if (filter_var($host, FILTER_VALIDATE_IP)) { + return (bool) filter_var( + $host, + FILTER_VALIDATE_IP, + FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE + ); + } + + return true; + } + + private function findById(int $id) + { + $rows = (new ConnectionModel())->get(self::COLUMNS, ['id' => $id], 1); + + if (is_wp_error($rows) || empty($rows[0])) { + return new WP_Error('connection_not_found', __('Connection not found', 'bit-integrations')); + } + + return $rows[0]; + } + + private function findExistingIdForAccount(string $appSlug, string $accountName): int + { + if ($appSlug === '' || $accountName === '') { + return 0; + } + + $rows = (new ConnectionModel())->get( + ['id'], + [ + 'app_slug' => $appSlug, + 'account_name' => $accountName, + 'status' => ConnectionModel::STATUS_VERIFIED, + ], + 1, + null, + 'id', + 'DESC' + ); + + if (is_wp_error($rows) || empty($rows[0])) { + return 0; + } + + return (int) $rows[0]->id; + } + + private function formatRow($row): array + { + $encryptKeys = AuthDataCodec::parseEncryptKeys($row->encrypt_keys ?? ''); + $authDetails = $this->normalizeArray($row->auth_details ?? null); + $authDetails = AuthDataCodec::decryptValues($authDetails, $encryptKeys); + + return [ + 'id' => (int) $row->id, + 'app_slug' => $row->app_slug, + 'auth_type' => (string) ($row->auth_type ?? ''), + 'connection_name' => $row->connection_name, + 'account_name' => $row->account_name, + 'encrypt_keys' => $encryptKeys, + 'auth_details' => $authDetails, + 'status' => isset($row->status) ? (int) $row->status : ConnectionModel::STATUS_VERIFIED, + 'user_id' => isset($row->user_id) ? (int) $row->user_id : 0, + 'created_at' => $row->created_at ?? null, + 'updated_at' => $row->updated_at ?? null, + ]; + } + + /** + * List responses should not expose decrypted credential payloads. + */ + private function formatListRow($row, ?array $linkedIntegrations = null): array + { + $payload = [ + 'id' => (int) $row->id, + 'app_slug' => $row->app_slug, + 'auth_type' => (string) ($row->auth_type ?? ''), + 'connection_name' => $row->connection_name, + 'account_name' => $row->account_name, + 'status' => isset($row->status) ? (int) $row->status : ConnectionModel::STATUS_VERIFIED, + 'user_id' => isset($row->user_id) ? (int) $row->user_id : 0, + 'created_at' => $row->created_at ?? null, + 'updated_at' => $row->updated_at ?? null, + ]; + + if (\is_array($linkedIntegrations)) { + $payload['linked_integrations'] = $linkedIntegrations; + $payload['linked_count'] = \count($linkedIntegrations); + } + + return $payload; + } + + private function resolveEncryptKeys($request): array + { + if (!isset($request->encrypt_keys)) { + return []; + } + + if (\is_string($request->encrypt_keys)) { + return AuthDataCodec::parseEncryptKeys($request->encrypt_keys); + } + + if (\is_array($request->encrypt_keys)) { + $keys = []; + foreach ($request->encrypt_keys as $key) { + $key = $this->sanitizeScalar($key); + + if ($key !== '') { + $keys[] = $key; + } + } + + return array_values(array_unique($keys)); + } + + return []; + } + + private function normalizeArray($value): array + { + if (\is_array($value)) { + return $value; + } + + if (\is_object($value)) { + return Helper::jsonEncodeDecode($value) ?: []; + } + + if (\is_string($value) && $value !== '') { + $decoded = json_decode($value, true); + + return \is_array($decoded) ? $decoded : []; + } + + return []; + } + + private function normalizePayload($value) + { + if (\is_array($value)) { + return $value; + } + + if (\is_object($value)) { + return Helper::jsonEncodeDecode($value) ?: []; + } + + return $value; + } + + private function normalizeHeaders($value): array + { + if (\is_object($value)) { + $value = Helper::jsonEncodeDecode($value) ?: []; + } + + if (!\is_array($value)) { + return []; + } + + $headers = []; + + foreach ($value as $key => $headerValue) { + if (!\is_scalar($key)) { + continue; + } + + $headerName = sanitize_text_field((string) $key); + + if ($headerName === '' || !\is_scalar($headerValue)) { + continue; + } + + $headers[$headerName] = sanitize_text_field((string) $headerValue); + } + + return $headers; + } + + private function getLinkedIntegrations(int $connectionId) + { + $linkedIntegrationMap = $this->buildLinkedIntegrationMap(); + + if (is_wp_error($linkedIntegrationMap)) { + return $linkedIntegrationMap; + } + + return $linkedIntegrationMap[$connectionId] ?? []; + } + + private function extractConnectionIdFromFlowDetails(array $flowDetails): int + { + foreach (['connection_id', 'connectionId'] as $key) { + if (!isset($flowDetails[$key]) || $flowDetails[$key] === '') { + continue; + } + + return absint($flowDetails[$key]); + } + + return 0; + } + + private function buildLinkedIntegrationMap() + { + $flows = (new FlowModel())->get(['id', 'name', 'flow_details']); + + if (is_wp_error($flows)) { + return $flows; + } + + if (empty($flows)) { + return []; + } + + $linkedIntegrationMap = []; + + foreach ($flows as $flow) { + $flowDetails = json_decode((string) ($flow->flow_details ?? ''), true); + + if (!\is_array($flowDetails)) { + continue; + } + + $connectionId = $this->extractConnectionIdFromFlowDetails($flowDetails); + + if ($connectionId < 1) { + continue; + } + + if (!isset($linkedIntegrationMap[$connectionId])) { + $linkedIntegrationMap[$connectionId] = []; + } + + $linkedIntegrationMap[$connectionId][] = [ + 'id' => absint($flow->id ?? 0), + 'name' => sanitize_text_field((string) ($flow->name ?? '')), + ]; + } + + return $linkedIntegrationMap; + } + + private function normalizeId($request): int + { + if (is_numeric($request)) { + return absint($request); + } + + if (\is_object($request)) { + foreach (['id', 'connection_id', 'connectionId'] as $key) { + if (!empty($request->{$key})) { + return absint($request->{$key}); + } + } + } + + if (\is_array($request)) { + foreach (['id', 'connection_id', 'connectionId'] as $key) { + if (!empty($request[$key])) { + return absint($request[$key]); + } + } + } + + return 0; + } + + private function sanitizeScalar($value): string + { + if (!\is_scalar($value)) { + return ''; + } + + return sanitize_text_field((string) $value); + } + + private function isTruthy($value): bool + { + if (\is_bool($value)) { + return $value; + } + + if (is_numeric($value)) { + return absint($value) === 1; + } + + if (!\is_scalar($value)) { + return false; + } + + $value = strtolower(trim((string) $value)); + + return \in_array($value, ['1', 'true', 'yes', 'on'], true); + } + + private function guard(): void + { + if ( + !Capabilities::Check('manage_options') + && !Capabilities::Check('bit_integrations_manage_integrations') + && !Capabilities::Check('bit_integrations_create_integrations') + && !Capabilities::Check('bit_integrations_edit_integrations') + ) { + wp_send_json_error(__('You do not have permission to manage connections', 'bit-integrations')); + } + } +} diff --git a/bitwpfi.php b/bitwpfi.php index 008213e07..1a7324a37 100644 --- a/bitwpfi.php +++ b/bitwpfi.php @@ -25,7 +25,7 @@ * @deprecated since version 2.7.8. use Config::DB_VERSION instead. */ global $btcbi_db_version; -$btcbi_db_version = '1.1'; +$btcbi_db_version = '1.2'; // Define most essential constants. /** diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 3f2e9d3fb..ff4917f53 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -27,6 +27,7 @@ const Integrations = lazy(() => import('./components/Integrations')) const Settings = lazy(() => import('./pages/Settings')) const DocSupport = lazy(() => import('./pages/DocSupport')) const FlowBuilder = lazy(() => import('./pages/FlowBuilder')) +const Connections = lazy(() => import('./pages/Connections')) const Error404 = lazy(() => import('./pages/Error404')) const integrationsElement = getIntegrationsElement(Integrations) @@ -112,6 +113,15 @@ function App() { } /> + + + + } + /> + { + const query = { app_slug: appSlug } + + if (options?.includeLinkedIntegrations) { + query.include_linked_integrations = 1 + } + + return bitsFetch(null, 'connections/list', query, 'GET') +} + +export const getConnection = id => bitsFetch(null, 'connections/get', { id }, 'GET') + +export const authorizeConnection = payload => bitsFetch(payload, 'connections/authorize') + +export const oauthConnectionExchange = payload => bitsFetch(payload, 'connections/oauth2/exchange') + +export const verifyPluginActivation = payload => + bitsFetch(payload, 'connections/verify-plugin-activation') + +export const saveConnection = payload => bitsFetch(payload, 'connections/save') + +export const updateConnection = payload => bitsFetch(payload, 'connections/update') + +export const reauthorizeConnection = payload => bitsFetch(payload, 'connections/reauthorize') + +export const deleteConnection = id => bitsFetch({ id }, 'connections/delete') diff --git a/frontend/src/Utils/connectionAuth.js b/frontend/src/Utils/connectionAuth.js new file mode 100644 index 000000000..f537de4d8 --- /dev/null +++ b/frontend/src/Utils/connectionAuth.js @@ -0,0 +1,21 @@ +import { reauthorizeConnection, saveConnection } from './connectionApi' + +export const AUTH_TYPES = Object.freeze({ + WP_PLUGIN_CHECK: 'wp_plugin_check', + OAUTH2: 'oauth2', + OAUTH1: 'oauth1', + API_KEY: 'api_key', + BEARER_TOKEN: 'bearer_token', + BASIC_AUTH: 'basic_auth', + CUSTOM: 'custom' +}) + +export const defaultEncryptKeys = { + [AUTH_TYPES.API_KEY]: ['value'], + [AUTH_TYPES.BASIC_AUTH]: ['password'], + [AUTH_TYPES.BEARER_TOKEN]: ['token'], + [AUTH_TYPES.OAUTH2]: ['client_secret', 'access_token', 'refresh_token'], + [AUTH_TYPES.OAUTH1]: ['consumer_secret', 'access_token', 'access_token_secret'] +} + +export const isWpPluginCheckType = authType => authType === AUTH_TYPES.WP_PLUGIN_CHECK diff --git a/frontend/src/Utils/oauthHelper.js b/frontend/src/Utils/oauthHelper.js new file mode 100644 index 000000000..89db9d5f5 --- /dev/null +++ b/frontend/src/Utils/oauthHelper.js @@ -0,0 +1,219 @@ +import { APP_CONFIG } from '../config/app' +import { oauthConnectionExchange } from './connectionApi' + +const OAUTH_CHANNEL = 'bit_integrations_oauth_share' +const PKCE_CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~' + +const base64UrlEncode = bytes => { + let str = '' + for (let i = 0; i < bytes.length; i++) str += String.fromCharCode(bytes[i]) + return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') +} + +export const generateCodeVerifier = (length = 64) => { + const charsetLen = PKCE_CHARSET.length + // Reject bytes >= max to avoid modulo bias (RFC 7636 wants uniform). + const max = 256 - (256 % charsetLen) + const buf = new Uint8Array(1) + let result = '' + while (result.length < length) { + window.crypto.getRandomValues(buf) + if (buf[0] < max) result += PKCE_CHARSET.charAt(buf[0] % charsetLen) + } + return result +} + +export const generateCodeChallengeS256 = async codeVerifier => { + const data = new TextEncoder().encode(codeVerifier) + const digest = await window.crypto.subtle.digest('SHA-256', data) + return base64UrlEncode(new Uint8Array(digest)) +} + +export const getRedirectUri = () => { + const api = (APP_CONFIG?.api || '').replace(/\/+$/, '') + return `${api}/redirect` +} + +export const getCallbackState = () => { + const baseURL = APP_CONFIG?.baseURL || '' + return `${baseURL}/auth-response/` +} + +const randomToken = (bytes = 16) => { + const arr = new Uint8Array(bytes) + window.crypto.getRandomValues(arr) + return base64UrlEncode(arr) +} + +const getChannelName = channelKey => (channelKey ? `${OAUTH_CHANNEL}:${channelKey}` : OAUTH_CHANNEL) + +export const createOauthChannelKey = () => randomToken(18) + +export const buildCallbackState = channelKey => { + const baseState = getCallbackState() + return channelKey ? `${baseState}&oauth_channel=${encodeURIComponent(channelKey)}` : baseState +} + +const appendQueryParam = (url, key, value) => { + url.searchParams.append(key, String(value)) +} + +export const buildAuthUrl = (authCodeEndpoint, { state, redirectUri, extraParams = {} }) => { + const url = new URL(authCodeEndpoint.url) + const queryParams = authCodeEndpoint.queryParams || {} + + Object.entries(queryParams).forEach(([key, value]) => appendQueryParam(url, key, value)) + Object.entries(extraParams).forEach(([key, value]) => appendQueryParam(url, key, value)) + url.searchParams.append('redirect_uri', redirectUri) + url.searchParams.append('state', state) + + return url.toString() +} + +const extractChannelFromState = stateValue => { + if (!stateValue || typeof stateValue !== 'string') return '' + + try { + const decoded = decodeURIComponent(stateValue) + const match = decoded.match(/(?:\?|&)oauth_channel=([^&]+)/) + return match?.[1] ? decodeURIComponent(match[1]) : '' + } catch { + return '' + } +} + +export const openOauthPopup = ( + authUrl, + label = 'OAuth', + { channelKey = '', includeLegacyFallback = false } = {} +) => + new Promise(resolve => { + const popup = window.open(authUrl, label, 'width=500,height=650,toolbar=off') + + if (!popup) { + resolve({ error: 'popup_blocked' }) + return + } + + let resolved = false + const channel = new BroadcastChannel(getChannelName(channelKey)) + const fallbackChannel = + includeLegacyFallback && channelKey ? new BroadcastChannel(OAUTH_CHANNEL) : null + + const cleanup = () => { + channel.close() + fallbackChannel?.close() + clearInterval(closeTimer) + } + + const resolveMessage = event => { + if (resolved) return + resolved = true + cleanup() + try { + popup.close() + } catch (_) {} // eslint-disable-line no-unused-vars, no-empty + resolve(event.data || {}) + } + channel.onmessage = resolveMessage + if (fallbackChannel) fallbackChannel.onmessage = resolveMessage + + const closeTimer = setInterval(() => { + if (popup.closed && !resolved) { + resolved = true + cleanup() + resolve({ error: 'popup_closed' }) + } + }, 800) + }) + +export const broadcastAuthCodeResponse = response => { + const channelKey = response?.oauth_channel || extractChannelFromState(response?.state) + const channel = new BroadcastChannel(getChannelName(channelKey)) + channel.postMessage(response) + setTimeout(() => channel.close(), 200) +} + +export const readAuthResponseFromUrl = () => { + const response = {} + const search = new URLSearchParams(window.location.search) + for (const [key, value] of search) if (value) response[key] = value + + // backend redirect appends "&code=...&state=..." after the hash route + const hashParams = new URLSearchParams(window.location.hash.replace(/^#/, '')) + for (const [key, value] of hashParams) if (value) response[key] = value + + return response +} + +const buildClientAuthHeaders = ({ clientId, clientSecret, clientAuthentication }) => { + if (clientAuthentication === 'header') { + return { Authorization: `Basic ${btoa(`${clientId}:${clientSecret}`)}` } + } + return {} +} + +const buildClientAuthBody = ({ clientId, clientSecret, clientAuthentication }) => { + if (clientAuthentication === 'header') return {} + return { client_id: clientId, client_secret: clientSecret } +} + +export const exchangeAuthCodeForToken = ({ + tokenEndpoint, + clientId, + clientSecret, + clientAuthentication = 'body', + code, + codeVerifier, + redirectUri, + sslVerify = true +}) => { + const bodyParams = { + grant_type: 'authorization_code', + code: decodeURIComponent(code), + redirect_uri: redirectUri, + ...(tokenEndpoint?.bodyParams || {}), + ...buildClientAuthBody({ clientId, clientSecret, clientAuthentication }) + } + + if (codeVerifier) bodyParams.code_verifier = codeVerifier + + return oauthConnectionExchange({ + url: tokenEndpoint.url, + method: tokenEndpoint.method || 'POST', + body_params: bodyParams, + headers: { + ...(tokenEndpoint?.headers || {}), + ...buildClientAuthHeaders({ clientId, clientSecret, clientAuthentication }) + }, + ssl_verify: sslVerify + }) +} + +export const exchangeClientCredentialsForToken = ({ + tokenEndpoint, + clientId, + clientSecret, + clientAuthentication = 'body', + scope, + sslVerify = true +}) => { + const bodyParams = { + grant_type: 'client_credentials', + ...(tokenEndpoint?.bodyParams || {}), + ...buildClientAuthBody({ clientId, clientSecret, clientAuthentication }) + } + + if (scope) bodyParams.scope = scope + + return oauthConnectionExchange({ + url: tokenEndpoint.url, + method: tokenEndpoint.method || 'POST', + body_params: bodyParams, + headers: { + ...(tokenEndpoint?.headers || {}), + ...buildClientAuthHeaders({ clientId, clientSecret, clientAuthentication }) + }, + ssl_verify: sslVerify + }) +} diff --git a/frontend/src/components/AllIntegrations/ACPT/ACPTAuthorization.jsx b/frontend/src/components/AllIntegrations/ACPT/ACPTAuthorization.jsx index cff2402a9..ae85836f4 100644 --- a/frontend/src/components/AllIntegrations/ACPT/ACPTAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ACPT/ACPTAuthorization.jsx @@ -1,146 +1,48 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useCallback, useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' -import { acptAuthentication } from './ACPTCommonFunc' - -export default function ACPTAuthorization({ - acptConf, - setAcptConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '', api_secret: '' }) - const handleInput = useCallback(e => { - const { name, value } = e.target - - setAcptConf(prev => ({ - ...prev, - [name]: value - })) - - setError(prev => ({ - ...prev, - [name]: '' - })) - }, []) - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - if (isAuthorized) { - setStep(2) - } - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function ACPTAuthorization({ acptConf, setAcptConf, step, setStep, isInfo }) { + const note = ` + ${__('Please note', 'bit-integrations')} +

${__( + 'The secret key will no longer be displayed, so please take note of it. Eventually, you can regenerate your API keys.', + 'bit-integrations' + )}

+

${__('To get API key-secret', 'bit-integrations')}

+
    +
  • ${__('Go to the ACPT dashboard.', 'bit-integrations')}
  • +
  • ${__('Open Tools, then go to API dashboard.', 'bit-integrations')}
  • +
  • ${__('Open REST API and generate an API key if needed.', 'bit-integrations')}
  • +
  • ${__('Copy the generated key-secret pair.', 'bit-integrations')}
  • +
` return ( -
- - -
-
- {__('Integration Name:', 'bit-integrations')} -
- -
{error.name}
-
-
-
- {__('Homepage URL:', 'bit-integrations')} -
- -
{error.base_url}
-
-
-
- {__('Api Key-Secret:', 'bit-integrations')} -
- -
{error.api_key}
-
- - {!isInfo && ( -
- - -
- - -
- )} - - -
+ ) } - -const note = ` - ${__('Please note', 'bit-integrations')} -

${__( - 'The secret key will no longer be displayed, so please take note of it. Eventually, you can regenerate your API keys.', - 'bit-integrations' - )}

-

${__('To Get Api Key-secret', 'bit-integrations')}

-
    -
  • ${__('First go to "ACPT" dashboard', 'bit-integrations')}
  • -
  • ${__('Then go to "Tools" from menu', 'bit-integrations')}
  • -
  • ${__('Click on "Go to API dashboard" from tools', 'bit-integrations')}
  • -
  • ${__('Then click "REST API" from the top sub menu', 'bit-integrations')}
  • -
  • ${__( - 'Then If you don’t have one API key click on the "Generate API key" button.', - 'bit-integrations' - )}
  • -
  • ${__( - 'The API "key-secret" pair will be displayed in a popup.', - 'bit-integrations' - )}
  • -
` diff --git a/frontend/src/components/AllIntegrations/ACPT/ACPTCommonFunc.js b/frontend/src/components/AllIntegrations/ACPT/ACPTCommonFunc.js index 9a9af4865..3a7161102 100644 --- a/frontend/src/components/AllIntegrations/ACPT/ACPTCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ACPT/ACPTCommonFunc.js @@ -1,8 +1,5 @@ /* eslint-disable no-console */ /* eslint-disable no-else-return */ -import toast from 'react-hot-toast' -import bitsFetch from '../../../Utils/bitsFetch' -import { __ } from '../../../Utils/i18nwrap' export const handleInput = (e, salesmateConf, setSalesmateConf) => { const newConf = { ...salesmateConf } @@ -40,40 +37,3 @@ export const checkMappedFields = acptConf => { } return true } - -export const acptAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.api_key || !confTmp.base_url) { - setError({ - base_url: !confTmp.base_url ? __("Homepage URL can't be empty", 'bit-integrations') : '', - api_key: !confTmp.api_key ? __("Api Key-Secret can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - base_url: confTmp.base_url, - api_key: confTmp.api_key - } - - bitsFetch(requestParams, 'acpt_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - - toast.success(__('Authorized Successfully', 'bit-integrations')) - - return - } - - setLoading({ ...loading, auth: false }) - - toast.error( - result?.data && typeof result.data === 'string' - ? result.data - : __('Authorized failed, Please enter valid Api Key-Secret', 'bit-integrations') - ) - }) -} diff --git a/frontend/src/components/AllIntegrations/AcademyLms/AcademyLmsAuthorization.jsx b/frontend/src/components/AllIntegrations/AcademyLms/AcademyLmsAuthorization.jsx index 5852399b3..78f67e65b 100644 --- a/frontend/src/components/AllIntegrations/AcademyLms/AcademyLmsAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/AcademyLms/AcademyLmsAuthorization.jsx @@ -1,102 +1,35 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import { deepCopy } from '../../../Utils/Helpers' -import bitsFetch from '../../../Utils/bitsFetch' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function AcademyLmsAuthorization({ academyLmsConf, setAcademyLmsConf, step, setStep, - setSnackbar + isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'academy_lms_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Academy Lms Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(academyLmsConf) - newConf[e.target.name] = e.target.value - setAcademyLmsConf(newConf) - } - return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- - - {isLoading === 'auth' && ( -
- - {__('Checking if Academy Lms is active!!!', 'bit-integrations')} -
- )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
- - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'Academy Lms' - )} -
- )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
+ ) } diff --git a/frontend/src/components/AllIntegrations/ActiveCampaign/ActiveCampaignAuthorization.jsx b/frontend/src/components/AllIntegrations/ActiveCampaign/ActiveCampaignAuthorization.jsx index 4f18a3a0c..fc55a5a5d 100644 --- a/frontend/src/components/AllIntegrations/ActiveCampaign/ActiveCampaignAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ActiveCampaign/ActiveCampaignAuthorization.jsx @@ -1,160 +1,86 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { refreshActiveCampaingHeader, refreshActiveCampaingList } from './ActiveCampaignCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { + refreshActiveCampaingAccounts, + refreshActiveCampaingHeader, + refreshActiveCampaingList, + refreshActiveCampaingTags +} from './ActiveCampaignCommonFunc' + export default function ActiveCampaignAuthorization({ - formID, activeCampaingConf, setActiveCampaingConf, step, setstep, setSnackbar, isInfo, - isLoading, setIsLoading }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_key: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) - // const [isLoading, setIsLoading] = useState(false) + const loadMetadata = useCallback( + async connectionId => { + const nextConf = connectionId + ? { ...activeCampaingConf, connection_id: connectionId } + : activeCampaingConf + + refreshActiveCampaingList(nextConf, setActiveCampaingConf, setIsLoading, setSnackbar) + refreshActiveCampaingHeader(nextConf, setActiveCampaingConf, setIsLoading, setSnackbar) + refreshActiveCampaingAccounts(nextConf, setActiveCampaingConf, setIsLoading, setSnackbar) + refreshActiveCampaingTags(nextConf, setActiveCampaingConf, setIsLoading, setSnackbar) + }, + [activeCampaingConf, setActiveCampaingConf, setIsLoading, setSnackbar] + ) - const handleAuthorize = () => { - const newConf = { ...activeCampaingConf } - if (!newConf.name || !newConf.api_key || !newConf.api_url) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - api_key: !newConf.api_key ? __("Access Api Key can't be empty", 'bit-integrations') : '', - api_url: !newConf.api_url ? __("Access API URL can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading('auth') - const data = { - api_key: newConf.api_key, - api_url: newConf.api_url - } - bitsFetch(data, 'aCampaign_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) + const handleSetStep = useCallback( + value => { + if ( + value === 2 && + (!activeCampaingConf?.default?.activeCampaignLists || !activeCampaingConf?.default?.fields) + ) { + loadMetadata() } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...activeCampaingConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setActiveCampaingConf(newConf) - } - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - refreshActiveCampaingHeader(activeCampaingConf, setActiveCampaingConf, setIsLoading, setSnackbar) - refreshActiveCampaingList(activeCampaingConf, setActiveCampaingConf, setIsLoading, setSnackbar) - setstep(2) - } + setstep(value) + }, + [activeCampaingConf, loadMetadata, setstep] + ) - const ActiveInstructions = ` -

${__('Get api url and api key', 'bit-integrations')}

-
    -
  • ${__('First go to activeCampaign your dashboard.', 'bit-integrations')}
  • -
  • ${__('Click Settings, Then click Developer', 'bit-integrations')}"
  • -
` + const note = ` +

${__('Get API URL and API key', 'bit-integrations')}

+
    +
  • ${__('Go to your ActiveCampaign dashboard.', 'bit-integrations')}
  • +
  • ${__('Open Settings, then Developer.', 'bit-integrations')}
  • +
  • ${__('Copy API URL and API Key.', 'bit-integrations')}
  • +
` return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- -
{error.name}
- -
- {__('Access API URL:', 'bit-integrations')} -
- -
{error.api_url}
- -
- {__('Access API Key:', 'bit-integrations')} -
- -
{error.api_key}
- {isLoading === 'auth' && ( -
- - {__('Checking API Key!!!', 'bit-integrations')} -
- )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
- - × - - {__('Sorry, Api key is invalid', 'bit-integrations')} -
- )} - {!isInfo && ( - <> - -
- - - )} - -
+ ) } diff --git a/frontend/src/components/AllIntegrations/ActiveCampaign/ActiveCampaignCommonFunc.js b/frontend/src/components/AllIntegrations/ActiveCampaign/ActiveCampaignCommonFunc.js index b8afc4b3a..0d6379002 100644 --- a/frontend/src/components/AllIntegrations/ActiveCampaign/ActiveCampaignCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ActiveCampaign/ActiveCampaignCommonFunc.js @@ -89,16 +89,21 @@ export const handleInput = (e, activeCampaingConf, setActiveCampaingConf) => { setActiveCampaingConf({ ...newConf }) } +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + api_key: confTmp.api_key, + api_url: confTmp.api_url + } + export const refreshActiveCampaingList = ( activeCampaingConf, setActiveCampaingConf, setIsLoading, setSnackbar ) => { - const refreshListsRequestParams = { - api_key: activeCampaingConf.api_key, - api_url: activeCampaingConf.api_url - } + const refreshListsRequestParams = buildAuthRequestParams(activeCampaingConf) setIsLoading(true) bitsFetch(refreshListsRequestParams, 'aCampaign_lists') .then(result => { @@ -142,10 +147,7 @@ export const refreshActiveCampaingAccounts = ( setIsLoading, setSnackbar ) => { - const refreshListsRequestParams = { - api_key: activeCampaingConf.api_key, - api_url: activeCampaingConf.api_url - } + const refreshListsRequestParams = buildAuthRequestParams(activeCampaingConf) setIsLoading(true) bitsFetch(refreshListsRequestParams, 'aCampaign_accounts') .then(result => { @@ -186,10 +188,7 @@ export const refreshActiveCampaingTags = ( setIsLoading, setSnackbar ) => { - const refreshListsRequestParams = { - api_key: activeCampaingConf.api_key, - api_url: activeCampaingConf.api_url - } + const refreshListsRequestParams = buildAuthRequestParams(activeCampaingConf) bitsFetch(refreshListsRequestParams, 'aCampaign_tags') .then(result => { if (result && result.success) { @@ -231,10 +230,7 @@ export const refreshActiveCampaingHeader = ( setIsLoading, setSnackbar ) => { - const refreshListsRequestParams = { - api_key: activeCampaingConf.api_key, - api_url: activeCampaingConf.api_url - } + const refreshListsRequestParams = buildAuthRequestParams(activeCampaingConf) setIsLoading(true) bitsFetch(refreshListsRequestParams, 'aCampaign_headers') .then(result => { diff --git a/frontend/src/components/AllIntegrations/Acumbamail/AcumbamailAuthorization.jsx b/frontend/src/components/AllIntegrations/Acumbamail/AcumbamailAuthorization.jsx index 19e9de90a..b1fab293b 100644 --- a/frontend/src/components/AllIntegrations/Acumbamail/AcumbamailAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Acumbamail/AcumbamailAuthorization.jsx @@ -1,118 +1,63 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { fetchAllList, handleAuthorize } from './AcumbamailCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { fetchAllList } from './AcumbamailCommonFunc' export default function AcumbamailAuthorization({ - formID, acumbamailConf, setAcumbamailConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const loadLists = useCallback( + connectionId => { + const nextConf = connectionId ? { ...acumbamailConf, connection_id: connectionId } : acumbamailConf + fetchAllList(nextConf, setAcumbamailConf, setIsLoading, setSnackbar) + }, + [acumbamailConf, setAcumbamailConf, setIsLoading, setSnackbar] + ) - setstep(2) - fetchAllList(acumbamailConf, setAcumbamailConf, setIsLoading, setSnackbar) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !acumbamailConf?.default?.allLists) { + loadLists() + } + setstep(value) + }, + [acumbamailConf?.default?.allLists, loadLists, setstep] + ) - const handleInput = e => { - const newConf = { ...acumbamailConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setAcumbamailConf(newConf) - } + const note = ` + + ${__('To get your auth token, please visit', 'bit-integrations')} + + ${__(' Acumbamail API docs', 'bit-integrations')} + + ` return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- - - - {__('To Get Client Auth token, Please Visit', 'bit-integrations')} -   - - {__('Acumbamail doc', 'bit-integrations')} - - - -
- {__('Auth Token:', 'bit-integrations')} -
- -
{error.auth_token}
- - {!isInfo && ( - <> - -
- - - )} -
+ ) } diff --git a/frontend/src/components/AllIntegrations/Acumbamail/AcumbamailCommonFunc.js b/frontend/src/components/AllIntegrations/Acumbamail/AcumbamailCommonFunc.js index 3fd047efd..c809f6acf 100644 --- a/frontend/src/components/AllIntegrations/Acumbamail/AcumbamailCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Acumbamail/AcumbamailCommonFunc.js @@ -1,7 +1,15 @@ import toast from 'react-hot-toast' import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { sprintf, __ } from '../../../Utils/i18nwrap' +import { __ } from '../../../Utils/i18nwrap' + +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + auth_token: conf.auth_token + } + +const hasAuthParams = conf => Boolean(conf?.connection_id || conf?.auth_token) export const handleInput = (e, acumbamailConf, setAcumbamailConf, setIsLoading, setSnackbar, formID) => { let newConf = { ...acumbamailConf } @@ -27,11 +35,14 @@ export const handleInput = (e, acumbamailConf, setAcumbamailConf, setIsLoading, export const refreshFields = (formID, acumbamailConf, setAcumbamailConf, setIsLoading, setSnackbar) => { const { listId } = acumbamailConf - if (!listId) { + if (!listId || !hasAuthParams(acumbamailConf)) { return } setIsLoading(true) - const refreshFieldsRequestParams = { auth_token: acumbamailConf.auth_token, list_id: listId } + const refreshFieldsRequestParams = { + ...buildAuthRequestParams(acumbamailConf), + list_id: listId + } bitsFetch(refreshFieldsRequestParams, 'acumbamail_refresh_fields') .then(result => { if (result && result.success) { @@ -60,8 +71,16 @@ export const refreshFields = (formID, acumbamailConf, setAcumbamailConf, setIsLo } export const fetchAllList = (acumbamailConf, setAcumbamailConf, setIsLoading, setSnackbar) => { + if (!hasAuthParams(acumbamailConf)) { + setSnackbar({ + show: true, + msg: __('Authorization info is missing. please authorize again', 'bit-integrations') + }) + return + } + setIsLoading(true) - const requestParams = { auth_token: acumbamailConf.auth_token } + const requestParams = buildAuthRequestParams(acumbamailConf) bitsFetch(requestParams, 'acumbamail_fetch_all_list') .then(result => { @@ -85,39 +104,6 @@ export const fetchAllList = (acumbamailConf, setAcumbamailConf, setIsLoading, se .catch(() => setIsLoading(false)) } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.auth_token) { - setError({ - auth_token: !confTmp.auth_token ? __("Api Key can't be empty", 'bit-integrations') : '' - }) - return - } - setError({}) - setIsLoading(true) - - const requestParams = { auth_token: confTmp.auth_token } - - bitsFetch(requestParams, 'acumbamail_authorization_and_fetch_subscriber_list').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - setConf(newConf) - setisAuthorized(true) - setIsLoading(false) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setIsLoading(false) - toast.error(__(result.data, 'bit-integrations')) - }) -} - export const checkMappedFields = acumbamailConf => { const mappedFields = acumbamailConf?.field_map ? acumbamailConf.field_map.filter( diff --git a/frontend/src/components/AllIntegrations/Affiliate/AffiliateAuthorization.jsx b/frontend/src/components/AllIntegrations/Affiliate/AffiliateAuthorization.jsx index 5696b1436..f7d403f20 100644 --- a/frontend/src/components/AllIntegrations/Affiliate/AffiliateAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Affiliate/AffiliateAuthorization.jsx @@ -1,103 +1,38 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function AffiliateAuthorization({ - formID, affiliateConf, setAffiliateConf, step, setStep, - isLoading, - setIsLoading, - setSnackbar + isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'affiliate_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Affiliate Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(affiliateConf) - newConf[e.target.name] = e.target.value - setAffiliateConf(newConf) - } - return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- - - {isLoading === 'auth' && ( -
- - Checking if LearnDash is active!!! -
- )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
- - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'Affiliate plugin' - )} -
- )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
+ ) } diff --git a/frontend/src/components/AllIntegrations/AgiledCRM/Agiled.jsx b/frontend/src/components/AllIntegrations/AgiledCRM/Agiled.jsx index 1d6af93b8..a4d81c939 100644 --- a/frontend/src/components/AllIntegrations/AgiledCRM/Agiled.jsx +++ b/frontend/src/components/AllIntegrations/AgiledCRM/Agiled.jsx @@ -120,9 +120,6 @@ function Agiled({ formFields, setFlow, flow, allIntegURL }) { setAgiledConf={setAgiledConf} step={step} setStep={setStep} - loading={loading} - setLoading={setLoading} - setSnackbar={setSnackbar} /> {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/AgiledCRM/AgiledActions.jsx b/frontend/src/components/AllIntegrations/AgiledCRM/AgiledActions.jsx index 623597d05..241e5bf1f 100644 --- a/frontend/src/components/AllIntegrations/AgiledCRM/AgiledActions.jsx +++ b/frontend/src/components/AllIntegrations/AgiledCRM/AgiledActions.jsx @@ -33,7 +33,7 @@ export default function AgiledActions({ agiledConf, setAgiledConf, loading, setL if (type === 'owner') { if (e.target?.checked) { - getAllOwners(agiledConf, setAgiledConf, setLoading) + getAllOwners(agiledConf, setAgiledConf, loading, setLoading) newConf.actions.owner = true } else { setActionMdl({ show: false }) @@ -41,7 +41,7 @@ export default function AgiledActions({ agiledConf, setAgiledConf, loading, setL } } else if (type === 'account') { if (e.target?.checked) { - getAllAccounts(agiledConf, setAgiledConf, setLoading) + getAllAccounts(agiledConf, setAgiledConf, loading, setLoading) newConf.actions.account = true } else { setActionMdl({ show: false }) @@ -49,7 +49,7 @@ export default function AgiledActions({ agiledConf, setAgiledConf, loading, setL } } else if (type === 'source') { if (e.target?.checked) { - getAllSources(agiledConf, setAgiledConf, setLoading) + getAllSources(agiledConf, setAgiledConf, loading, setLoading) newConf.actions.source = true } else { setActionMdl({ show: false }) @@ -57,7 +57,7 @@ export default function AgiledActions({ agiledConf, setAgiledConf, loading, setL } } else if (type === 'status') { if (e.target?.checked) { - getAllStatuses(agiledConf, setAgiledConf, setLoading) + getAllStatuses(agiledConf, setAgiledConf, loading, setLoading) newConf.actions.status = true } else { setActionMdl({ show: false }) @@ -65,7 +65,7 @@ export default function AgiledActions({ agiledConf, setAgiledConf, loading, setL } } else if (type === 'lifeCycleStage') { if (e.target?.checked) { - getAllLifeCycleStage(agiledConf, setAgiledConf, setLoading) + getAllLifeCycleStage(agiledConf, setAgiledConf, loading, setLoading) newConf.actions.lifeCycleStage = true } else { setActionMdl({ show: false }) @@ -169,7 +169,7 @@ export default function AgiledActions({ agiledConf, setAgiledConf, loading, setL singleSelect /> -
- - - )} - + ) } + +const note = `${__('Example: name.agiled.app', 'bit-integrations')}` diff --git a/frontend/src/components/AllIntegrations/AgiledCRM/AgiledCommonFunc.js b/frontend/src/components/AllIntegrations/AgiledCRM/AgiledCommonFunc.js index 37fd5ba08..828999dd4 100644 --- a/frontend/src/components/AllIntegrations/AgiledCRM/AgiledCommonFunc.js +++ b/frontend/src/components/AllIntegrations/AgiledCRM/AgiledCommonFunc.js @@ -46,43 +46,12 @@ export const checkMappedFields = agiledConf => { return true } -export const agiledAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.brand || !confTmp.auth_token) { - setError({ - brand: !confTmp.brand ? __("Brand Name (Account URL) can't be empty", 'bit-integrations') : '', - auth_token: !confTmp.auth_token ? __("Api Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) +export const getAllOwners = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, owners: true }) - const requestParams = { auth_token: confTmp.auth_token, brand: confTmp.brand } - - bitsFetch(requestParams, 'agiled_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid brand name & API key', 'bit-integrations')) - }) -} - -export const getAllOwners = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, owners: true }) - - const requestParams = { auth_token: confTmp.auth_token, brand: confTmp.brand } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { auth_token: confTmp.auth_token, brand: confTmp.brand } bitsFetch(requestParams, 'agiled_fetch_all_owners').then(result => { if (result && result.success) { @@ -91,20 +60,22 @@ export const getAllOwners = (confTmp, setConf, setLoading) => { newConf.owners = result.data } setConf(newConf) - setLoading({ ...setLoading, owners: false }) + setLoading({ ...loading, owners: false }) toast.success(__('Owners fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, owners: false }) + setLoading({ ...loading, owners: false }) toast.error(__('Owners fetching failed', 'bit-integrations')) }) } -export const getAllAccounts = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, accounts: true }) +export const getAllAccounts = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, accounts: true }) - const requestParams = { auth_token: confTmp.auth_token, brand: confTmp.brand } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { auth_token: confTmp.auth_token, brand: confTmp.brand } bitsFetch(requestParams, 'agiled_fetch_all_accounts').then(result => { if (result && result.success) { @@ -113,20 +84,22 @@ export const getAllAccounts = (confTmp, setConf, setLoading) => { newConf.accounts = result.data } setConf(newConf) - setLoading({ ...setLoading, accounts: false }) + setLoading({ ...loading, accounts: false }) toast.success(__('Accounts fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, accounts: false }) + setLoading({ ...loading, accounts: false }) toast.error(__('Accounts fetching failed', 'bit-integrations')) }) } -export const getAllSources = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, sources: true }) +export const getAllSources = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, sources: true }) - const requestParams = { auth_token: confTmp.auth_token, brand: confTmp.brand } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { auth_token: confTmp.auth_token, brand: confTmp.brand } bitsFetch(requestParams, 'agiled_fetch_all_sources').then(result => { if (result && result.success) { @@ -135,20 +108,22 @@ export const getAllSources = (confTmp, setConf, setLoading) => { newConf.sources = result.data } setConf(newConf) - setLoading({ ...setLoading, sources: false }) + setLoading({ ...loading, sources: false }) toast.success(__('Sources fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, sources: false }) + setLoading({ ...loading, sources: false }) toast.error(__('Sources fetching failed', 'bit-integrations')) }) } -export const getAllStatuses = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, statuses: true }) +export const getAllStatuses = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, statuses: true }) - const requestParams = { auth_token: confTmp.auth_token, brand: confTmp.brand } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { auth_token: confTmp.auth_token, brand: confTmp.brand } bitsFetch(requestParams, 'agiled_fetch_all_statuses').then(result => { if (result && result.success) { @@ -157,20 +132,22 @@ export const getAllStatuses = (confTmp, setConf, setLoading) => { newConf.statuses = result.data } setConf(newConf) - setLoading({ ...setLoading, statuses: false }) + setLoading({ ...loading, statuses: false }) toast.success(__('Status fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, statuses: false }) + setLoading({ ...loading, statuses: false }) toast.error(__('Status fetching failed', 'bit-integrations')) }) } -export const getAllLifeCycleStage = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, lifeCycleStages: true }) +export const getAllLifeCycleStage = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, lifeCycleStages: true }) - const requestParams = { auth_token: confTmp.auth_token, brand: confTmp.brand } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { auth_token: confTmp.auth_token, brand: confTmp.brand } bitsFetch(requestParams, 'agiled_fetch_all_lifeCycleStages').then(result => { if (result && result.success) { @@ -179,20 +156,22 @@ export const getAllLifeCycleStage = (confTmp, setConf, setLoading) => { newConf.lifeCycleStages = result.data } setConf(newConf) - setLoading({ ...setLoading, lifeCycleStages: false }) + setLoading({ ...loading, lifeCycleStages: false }) toast.success(__('Life cycle stages fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, lifeCycleStages: false }) + setLoading({ ...loading, lifeCycleStages: false }) toast.error(__('Life cycle stages fetching failed', 'bit-integrations')) }) } -export const getAllCRMPipelines = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, CRMPipelines: true }) +export const getAllCRMPipelines = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, CRMPipelines: true }) - const requestParams = { auth_token: confTmp.auth_token, brand: confTmp.brand } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { auth_token: confTmp.auth_token, brand: confTmp.brand } bitsFetch(requestParams, 'agiled_fetch_all_CRMPipelines').then(result => { if (result && result.success) { @@ -201,24 +180,29 @@ export const getAllCRMPipelines = (confTmp, setConf, setLoading) => { newConf.CRMPipelines = result.data } setConf(newConf) - setLoading({ ...setLoading, CRMPipelines: false }) + setLoading({ ...loading, CRMPipelines: false }) toast.success(__('Pipelines fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, CRMPipelines: false }) + setLoading({ ...loading, CRMPipelines: false }) toast.error(__('Pipelines fetching failed', 'bit-integrations')) }) } -export const getAllCRMPipelineStages = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, CRMPipelineStages: true }) +export const getAllCRMPipelineStages = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, CRMPipelineStages: true }) - const requestParams = { - auth_token: confTmp.auth_token, - brand: confTmp.brand, - selectedCRMPipeline: confTmp.selectedCRMPipeline - } + const requestParams = confTmp.connection_id + ? { + connection_id: confTmp.connection_id, + selectedCRMPipeline: confTmp.selectedCRMPipeline + } + : { + auth_token: confTmp.auth_token, + brand: confTmp.brand, + selectedCRMPipeline: confTmp.selectedCRMPipeline + } bitsFetch(requestParams, 'agiled_fetch_all_CRMPipelineStages').then(result => { if (result && result.success) { @@ -227,12 +211,12 @@ export const getAllCRMPipelineStages = (confTmp, setConf, setLoading) => { newConf.CRMPipelineStages = result.data } setConf(newConf) - setLoading({ ...setLoading, CRMPipelineStages: false }) + setLoading({ ...loading, CRMPipelineStages: false }) toast.success(__('Pipeline stages fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, CRMPipelineStages: false }) + setLoading({ ...loading, CRMPipelineStages: false }) toast.error(__('Pipeline stages fetching failed', 'bit-integrations')) }) } diff --git a/frontend/src/components/AllIntegrations/AgiledCRM/AgiledIntegLayout.jsx b/frontend/src/components/AllIntegrations/AgiledCRM/AgiledIntegLayout.jsx index a04a46837..90e82e223 100644 --- a/frontend/src/components/AllIntegrations/AgiledCRM/AgiledIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/AgiledCRM/AgiledIntegLayout.jsx @@ -24,7 +24,7 @@ export default function AgiledIntegLayout({ if (e.target.value !== '') { newConf[name] = e.target.value if (e.target.value === 'deal') { - getAllCRMPipelines(newConf, setAgiledConf, setLoading) + getAllCRMPipelines(newConf, setAgiledConf, loading, setLoading) } } else { delete newConf[name] @@ -37,7 +37,7 @@ export default function AgiledIntegLayout({ newConf[name] = val if (name === 'selectedCRMPipeline' && val !== '') { newConf.selectedCRMPipelineStages = '' - getAllCRMPipelineStages(newConf, setAgiledConf, setLoading) + getAllCRMPipelineStages(newConf, setAgiledConf, loading, setLoading) } setAgiledConf({ ...newConf }) } @@ -103,7 +103,7 @@ export default function AgiledIntegLayout({ singleSelect /> -
- - - )} - + ) } diff --git a/frontend/src/components/AllIntegrations/Airtable/AirtableCommonFunc.js b/frontend/src/components/AllIntegrations/Airtable/AirtableCommonFunc.js index 166c32515..dbb0a3769 100644 --- a/frontend/src/components/AllIntegrations/Airtable/AirtableCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Airtable/AirtableCommonFunc.js @@ -30,62 +30,43 @@ export const checkMappedFields = airtableConf => { return true } -export const airtableAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading, - type -) => { - if (!confTmp.auth_token) { - setError({ - auth_token: !confTmp.auth_token - ? __("Personal access token can't be empty", 'bit-integrations') - : '' - }) +export const fetchAllBases = (confTmp, setConf, loading, setLoading, type = 'fetch') => { + if (!confTmp.connection_id && !confTmp.auth_token) { + toast.error(__("Personal access token can't be empty", 'bit-integrations')) return } - setError({}) + setLoading({ ...loading, bases: true }) - if (type === 'authentication') { - setLoading({ ...loading, auth: true }) - } - if (type === 'refreshBases') { - setLoading({ ...loading, bases: true }) - } - const requestParams = { auth_token: confTmp.auth_token } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { auth_token: confTmp.auth_token } - bitsFetch(requestParams, 'airtable_authentication').then(result => { + bitsFetch(requestParams, 'airtable_fetch_all_bases').then(result => { if (result && result.success) { const newConf = { ...confTmp } - setIsAuthorized(true) - if (type === 'authentication') { - if (result.data) { - newConf.bases = result.data - } - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if (type === 'refreshBases') { - if (result.data) { - newConf.bases = result.data - } - setLoading({ ...loading, bases: false }) - toast.success(__('All bases fectched successfully', 'bit-integrations')) + if (result.data) { + newConf.bases = result.data } setConf(newConf) + setLoading({ ...loading, bases: false }) + toast.success( + type === 'refresh' + ? __('All bases fetched successfully', 'bit-integrations') + : __('Bases fetched successfully', 'bit-integrations') + ) return } - setLoading({ ...loading, auth: false, bases: false }) - toast.error(__('Authorized failed!', 'bit-integrations')) + setLoading({ ...loading, bases: false }) + toast.error(__('Bases fetching failed', 'bit-integrations')) }) } export const getAllTables = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, tables: true }) - const requestParams = { auth_token: confTmp.auth_token, baseId: confTmp.selectedBase } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id, baseId: confTmp.selectedBase } + : { auth_token: confTmp.auth_token, baseId: confTmp.selectedBase } bitsFetch(requestParams, 'airtable_fetch_all_tables').then(result => { if (result && result.success) { @@ -109,11 +90,17 @@ export const getAllFields = (confTmp, setConf, loading, setLoading, type) => { } else if (type === 'refresh') { setLoading({ ...loading, customFields: true }) } - const requestParams = { - auth_token: confTmp.auth_token, - baseId: confTmp.selectedBase, - tableId: confTmp.selectedTable - } + const requestParams = confTmp.connection_id + ? { + connection_id: confTmp.connection_id, + baseId: confTmp.selectedBase, + tableId: confTmp.selectedTable + } + : { + auth_token: confTmp.auth_token, + baseId: confTmp.selectedBase, + tableId: confTmp.selectedTable + } bitsFetch(requestParams, 'airtable_fetch_all_fields').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/Airtable/AirtableIntegLayout.jsx b/frontend/src/components/AllIntegrations/Airtable/AirtableIntegLayout.jsx index 972f51dce..95ad42b8e 100644 --- a/frontend/src/components/AllIntegrations/Airtable/AirtableIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/Airtable/AirtableIntegLayout.jsx @@ -1,12 +1,10 @@ /* eslint-disable no-console */ -/* eslint-disable no-unused-vars */ -import { useState } from 'react' import MultiSelect from 'react-multiple-select-dropdown-lite' import { __ } from '../../../Utils/i18nwrap' import Loader from '../../Loaders/Loader' import 'react-multiple-select-dropdown-lite/dist/index.css' import AirtableActions from './AirtableActions' -import { airtableAuthentication, getAllFields, getAllTables } from './AirtableCommonFunc' +import { fetchAllBases, getAllFields, getAllTables } from './AirtableCommonFunc' import AirtableFieldMap from './AirtableFieldMap' import { addFieldMap } from './IntegrationHelpers' @@ -19,9 +17,6 @@ export default function AirtableIntegLayout({ setLoading, setSnackbar }) { - const [error, setError] = useState({ name: '', auth_token: '' }) - const [isAuthorized, setIsAuthorized] = useState(false) - const setChanges = (val, name) => { const newConf = { ...airtableConf } newConf[name] = val @@ -49,17 +44,7 @@ export default function AirtableIntegLayout({ onChange={val => setChanges(val, 'selectedBase')} /> -
- - - )} - + ) } diff --git a/frontend/src/components/AllIntegrations/Asana/AsanaCommonFunc.js b/frontend/src/components/AllIntegrations/Asana/AsanaCommonFunc.js index 908b25447..b1ad6b10d 100644 --- a/frontend/src/components/AllIntegrations/Asana/AsanaCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Asana/AsanaCommonFunc.js @@ -45,43 +45,14 @@ export const checkMappedFields = asanaConf => { return true } -export const asanaAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { api_key: confTmp.api_key } - - bitsFetch(requestParams, 'asana_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid API key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id ? { connection_id: confTmp.connection_id } : { api_key: confTmp.api_key } export const getCustomFields = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, customFields: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action: confTmp.actionName, project_id: confTmp.selectedProject } @@ -120,7 +91,7 @@ export const getAllProjects = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, Projects: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -149,7 +120,7 @@ export const getAllSections = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, Sections: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), selected_project: confTmp.selectedProject } diff --git a/frontend/src/components/AllIntegrations/AsgarosForum/AsgarosForumAuthorization.jsx b/frontend/src/components/AllIntegrations/AsgarosForum/AsgarosForumAuthorization.jsx index 5423524c9..599e346ca 100644 --- a/frontend/src/components/AllIntegrations/AsgarosForum/AsgarosForumAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/AsgarosForum/AsgarosForumAuthorization.jsx @@ -1,81 +1,33 @@ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { asgarosForumAuthentication } from './AsgarosForumCommonFunc' +import Authorization from '../../Connections/Authorization' export default function AsgarosForumAuthorization({ asgarosForumConf, setAsgarosForumConf, step, nextPage, - isLoading, - setIsLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - - const handleInput = e => { - const newConf = { ...asgarosForumConf } - newConf[e.target.name] = e.target.value - setAsgarosForumConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
-
- {__('Integration Name:', 'bit-integrations')} -
- -
{error.name}
- - - - {!isInfo && ( - <> - -
- - - )} -
+ ) + }} + /> ) } diff --git a/frontend/src/components/AllIntegrations/AsgarosForum/AsgarosForumCommonFunc.js b/frontend/src/components/AllIntegrations/AsgarosForum/AsgarosForumCommonFunc.js index 41ec3ba59..62d4d814a 100644 --- a/frontend/src/components/AllIntegrations/AsgarosForum/AsgarosForumCommonFunc.js +++ b/frontend/src/components/AllIntegrations/AsgarosForum/AsgarosForumCommonFunc.js @@ -1,6 +1,4 @@ import { create } from 'mutative' -import bitsFetch from '../../../Utils/bitsFetch' -import { __ } from '../../../Utils/i18nwrap' import { asgarosForumActionFields } from './staticData' export const handleInput = (e, asgarosForumConf, setAsgarosForumConf) => { @@ -45,45 +43,3 @@ export const checkMappedFields = asgarosForumConf => { ) ) } - -export const asgarosForumAuthentication = ( - confTmp, - setAsgarosForumConf, - setError, - setIsAuthorized, - setIsLoading -) => { - if (!confTmp?.name) { - setError({ - name: __("Integration name can't be empty", 'bit-integrations') - }) - return - } - - setError({}) - setIsLoading(true) - - bitsFetch({ name: confTmp.name }, 'asgaros_forum_authorize') - .then(result => { - if (result?.success) { - setIsAuthorized(true) - setAsgarosForumConf(prevConf => - create(prevConf, draftConf => { - draftConf.name = confTmp.name - }) - ) - } else { - setError({ - name: result?.data || __('Authorization failed', 'bit-integrations') - }) - } - }) - .catch(() => { - setError({ - name: __('Authorization failed', 'bit-integrations') - }) - }) - .finally(() => { - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/Autonami/AutonamiAuthorization.jsx b/frontend/src/components/AllIntegrations/Autonami/AutonamiAuthorization.jsx index e6c76d707..05649eb81 100644 --- a/frontend/src/components/AllIntegrations/Autonami/AutonamiAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Autonami/AutonamiAuthorization.jsx @@ -1,108 +1,36 @@ -/* eslint-disable react/jsx-no-useless-fragment */ -import { useEffect, useState } from 'react' -import { __, sprintf } from '../../../Utils/i18nwrap' -import bitsFetch from '../../../Utils/bitsFetch' -import LoaderSm from '../../Loaders/LoaderSm' -import BackIcn from '../../../Icons/BackIcn' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + export default function AutonamiAuthorization({ - formID, autonamiConf, setAutonamiConf, step, nextPage, - setSnackbar, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ integrationName: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const [isMounted, setIsMounted] = useState(true) - useEffect( - () => () => { - setIsMounted(false) - }, - [] - ) - - const handleAuthorize = () => { - setIsLoading('auth') - bitsFetch({}, 'autonami_authorize').then(result => { - if (isMounted) { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Connect Successfully', 'bit-integrations') }) - } - setShowAuthMsg(true) - setIsLoading(false) - } - }) - } - const handleInput = e => { - const newConf = { ...autonamiConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setAutonamiConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( - <> -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- - - {showAuthMsg && !isAuthorized && !isLoading && ( -
- - × - - {sprintf( - __('Please! First Install or Active %s Plugin', 'bit-integrations'), - 'Autonami Pro' - )} -
- )} - {!isInfo && ( - <> - -
- - - )} -
- + ) } diff --git a/frontend/src/components/AllIntegrations/BenchMark/BenchMarkAuthorization.jsx b/frontend/src/components/AllIntegrations/BenchMark/BenchMarkAuthorization.jsx index e1d77e455..1aea2814e 100644 --- a/frontend/src/components/AllIntegrations/BenchMark/BenchMarkAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/BenchMark/BenchMarkAuthorization.jsx @@ -1,70 +1,39 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' import { refreshBenchMarkList, refreshBenchMarkHeader } from './BenchMarkCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' export default function BenchMarkAuthorization({ - formID, benchMarkConf, setBenchMarkConf, step, setstep, setSnackbar, isInfo, - isLoading, setIsLoading }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_secret: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) + const loadLists = useCallback( + connectionId => { + const nextConf = connectionId ? { ...benchMarkConf, connection_id: connectionId } : benchMarkConf - const handleAuthorize = () => { - const newConf = { ...benchMarkConf } - if (!newConf.name || !newConf.api_secret) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - api_secret: !newConf.api_secret - ? __("Access API Secret Key can't be empty", 'bit-integrations') - : '' - }) - return - } - setIsLoading('auth') - const data = { - api_secret: newConf.api_secret - } - bitsFetch(data, 'benchMark_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...benchMarkConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setBenchMarkConf(newConf) - } - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + refreshBenchMarkList(nextConf, setBenchMarkConf, setIsLoading, setSnackbar) + }, + [benchMarkConf, setBenchMarkConf, setIsLoading, setSnackbar] + ) - refreshBenchMarkList(benchMarkConf, setBenchMarkConf, setIsLoading, setSnackbar) - setstep(2) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !benchMarkConf?.default?.benchMarkLists) { + loadLists() + } + setstep(value) + }, + [benchMarkConf, loadLists, setstep] + ) - const ActiveInstructions = ` + const note = `

${__('Get api secret key', 'bit-integrations')}

  • ${__('First go to your BenchMark dashboard.', 'bit-integrations')}
  • @@ -72,90 +41,23 @@ export default function BenchMarkAuthorization({
` return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- -
{error.name}
- -
- {__('Access API Secret Key:', 'bit-integrations')} -
- -
{error.api_secret}
- - - {__('To Get API Secret Key, Please Visit', 'bit-integrations')} -   - - {__('BenchMark API Token', 'bit-integrations')} - - -
-
- - {isLoading === 'auth' && ( -
- - Checking API Secret Key!!! -
- )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
- - × - - {__('Sorry, API Secret key is invalid', 'bit-integrations')} -
- )} - {!isInfo && ( - <> - -
- - - )} - -
+ ) } diff --git a/frontend/src/components/AllIntegrations/BenchMark/BenchMarkCommonFunc.js b/frontend/src/components/AllIntegrations/BenchMark/BenchMarkCommonFunc.js index 4419445ec..2065ee065 100644 --- a/frontend/src/components/AllIntegrations/BenchMark/BenchMarkCommonFunc.js +++ b/frontend/src/components/AllIntegrations/BenchMark/BenchMarkCommonFunc.js @@ -7,10 +7,14 @@ export const handleInput = (e, benchMarkConf, setBenchMarkConf) => { newConf.name = e.target.value setBenchMarkConf({ ...newConf }) } + +const buildAuthRequestParams = conf => + conf.connection_id ? { connection_id: conf.connection_id } : { api_secret: conf.api_secret } + // refreshMappedLists export const refreshBenchMarkList = (benchMarkConf, setBenchMarkConf, setIsLoading, setSnackbar) => { const refreshListsRequestParams = { - api_secret: benchMarkConf.api_secret + ...buildAuthRequestParams(benchMarkConf) } bitsFetch(refreshListsRequestParams, 'benchMark_lists') .then(result => { @@ -49,7 +53,7 @@ export const refreshBenchMarkList = (benchMarkConf, setBenchMarkConf, setIsLoadi // refreshMappedFields export const refreshBenchMarkHeader = (benchMarkConf, setBenchMarkConf, setIsLoading, setSnackbar) => { const refreshListsRequestParams = { - api_secret: benchMarkConf.api_secret, + ...buildAuthRequestParams(benchMarkConf), list_id: benchMarkConf.listId } bitsFetch(refreshListsRequestParams, 'benchMark_headers') diff --git a/frontend/src/components/AllIntegrations/Bento/BentoAuthorization.jsx b/frontend/src/components/AllIntegrations/Bento/BentoAuthorization.jsx index 2a843a9db..57c24db18 100644 --- a/frontend/src/components/AllIntegrations/Bento/BentoAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Bento/BentoAuthorization.jsx @@ -1,147 +1,41 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' -import { bentoAuthentication } from './BentoCommonFunc' - -export default function BentoAuthorization({ - bentoConf, - setBentoConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ publishable_key: '', secret_key: '' }) - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !bentoConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...bentoConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setBentoConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function BentoAuthorization({ bentoConf, setBentoConf, step, setStep, isInfo }) { + const note = ` +

${__('To get Publishable Key, Secret Key and Site UUID', 'bit-integrations')}

+
    +
  • ${__('Open the Bento team dashboard.', 'bit-integrations')}
  • +
  • ${__('Go to Settings, then API Keys.', 'bit-integrations')}
  • +
  • ${__('Copy Publishable Key, Secret Key and Site UUID.', 'bit-integrations')}
  • +
  • ${__('Use Publishable Key as Username and Secret Key as Password.', 'bit-integrations')}
  • +
` return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- - -
- {__('Publishable Key:', 'bit-integrations')} -
- -
{error.publishable_key}
- -
- {__('Secret Key:', 'bit-integrations')} -
- -
{error.secret_key}
- -
- {__('Site UUID:', 'bit-integrations')} -
- -
{error.site_uuid}
- - - {__('To Get Publishable Key, Secret Key & Site UUID, Please Visit', 'bit-integrations')} -   - - {__('Bento team dashboard', 'bit-integrations')} - - -
- - {!isInfo && ( -
- -
- -
- )} - -
+ ) } - -const note = ` -

${__('To Get Publishable Key, Secret Key & Site UUID', 'bit-integrations')}

-
    -
  • ${__('Navigate to the Bento team dashboard,.', 'bit-integrations')}
  • -
  • ${__('go to "Settings" and then "API Keys"', 'bit-integrations')}
  • -
  • ${__( - "where you'll find your Publishable Key, Secret Key & Site UUID", - 'bit-integrations' - )}
  • -
` diff --git a/frontend/src/components/AllIntegrations/Bento/BentoCommonFunc.js b/frontend/src/components/AllIntegrations/Bento/BentoCommonFunc.js index fa3d1a7bd..c611de1b2 100644 --- a/frontend/src/components/AllIntegrations/Bento/BentoCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Bento/BentoCommonFunc.js @@ -44,6 +44,13 @@ export const checkMappedFields = bentoConf => { } const setRequestParams = (config, customs = {}) => { + if (config.connection_id) { + return { + ...customs, + connection_id: config.connection_id + } + } + return { ...customs, publishable_key: config.publishable_key, @@ -52,42 +59,6 @@ const setRequestParams = (config, customs = {}) => { } } -export const bentoAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.publishable_key || !confTmp.secret_key || !confTmp.site_uuid) { - setError({ - publishable_key: !confTmp.publishable_key - ? __("Publishable Key can't be empty", 'bit-integrations') - : '', - secret_key: !confTmp.secret_key ? __("Secret Key can't be empty", 'bit-integrations') : '', - site_uuid: !confTmp.site_uuid ? __("Site UUID can't be empty", 'bit-integrations') : '' - }) - - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - bitsFetch(setRequestParams(confTmp), 'bento_authentication').then(result => { - setLoading({ ...loading, auth: false }) - - if (result && result.success) { - setIsAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - - toast.error( - result?.data - ? result?.data - : __( - 'Authorized failed, Please enter valid Publishable Key, Secret Key & Site UUID', - 'bit-integrations' - ) - ) - }) -} - export const getFields = (confTmp, setConf, action, setIsLoading) => { setIsLoading(true) diff --git a/frontend/src/components/AllIntegrations/BitForm/BitFormAuthorization.jsx b/frontend/src/components/AllIntegrations/BitForm/BitFormAuthorization.jsx index 5e3a50cfb..99791c749 100644 --- a/frontend/src/components/AllIntegrations/BitForm/BitFormAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/BitForm/BitFormAuthorization.jsx @@ -1,120 +1,72 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { fetchAllForm, handleAuthorize } from './BitFormCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { fetchAllForm } from './BitFormCommonFunc' export default function BitFormAuthorization({ - formID, bitFormConf, setBitFormConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const loadForms = useCallback( + connectionId => { + const nextConf = connectionId ? { ...bitFormConf, connection_id: connectionId } : bitFormConf - setstep(2) - fetchAllForm(bitFormConf, setBitFormConf, setIsLoading, setSnackbar) - } - - const handleInput = e => { - const newConf = { ...bitFormConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setBitFormConf(newConf) - } - - return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- + fetchAllForm(nextConf, setBitFormConf, setIsLoading, setSnackbar) + }, + [bitFormConf, setBitFormConf, setIsLoading, setSnackbar] + ) -
- {__('Your Domain Name:', 'bit-integrations')} -
- + const handleSetStep = useCallback( + value => { + if (value === 2 && !bitFormConf?.default?.forms) { + loadForms() + } + setstep(value) + }, + [bitFormConf, loadForms, setstep] + ) -
- {__('Client id:', 'bit-integrations')} -
- -
{error.api_key}
+ const note = ` +

${__('To get your Bit Form API key', 'bit-integrations')}

+
    +
  • ${__('Open your Bit Form WordPress dashboard.', 'bit-integrations')}
  • +
  • ${__('Go to Integrations and copy your Client ID (API key).', 'bit-integrations')}
  • +
` -
{error.clientSecret}
- {!isInfo && ( - <> - -
- - - )} -
+ return ( + ) } diff --git a/frontend/src/components/AllIntegrations/BitForm/BitFormCommonFunc.js b/frontend/src/components/AllIntegrations/BitForm/BitFormCommonFunc.js index 0019b7bf3..49655b6b4 100644 --- a/frontend/src/components/AllIntegrations/BitForm/BitFormCommonFunc.js +++ b/frontend/src/components/AllIntegrations/BitForm/BitFormCommonFunc.js @@ -44,40 +44,14 @@ export const checkAddressFieldMapRequired = sheetConf => { return true } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.api_key) { - setError({ api_key: !confTmp.api_key ? __("Api Key can't be empty", 'bit-integrations') : '' }) - return - } - setError({}) - setIsLoading(true) - - const requestParams = { app_domain: confTmp.domainName, api_key: confTmp.api_key } - - bitsFetch(requestParams, 'bitForm_authorization_and_fetch_form_list').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - setConf(newConf) - setisAuthorized(true) - setIsLoading(false) - toast.success(__('Authorization Successful', 'bit-integrations')) - return - } - setIsLoading(false) - toast.error(__('Authorization Failed', 'bit-integrations')) - }) -} +const buildAuthRequestParams = conf => + conf.connection_id + ? { connection_id: conf.connection_id } + : { app_domain: conf.domainName, api_key: conf.api_key } export const fetchAllForm = (bitFormConf, setBitFormConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { app_domain: bitFormConf.domainName, api_key: bitFormConf.api_key } + const requestParams = buildAuthRequestParams(bitFormConf) bitsFetch(requestParams, 'bitForm_all_form_list') .then(result => { @@ -103,8 +77,7 @@ export const fetchAllForm = (bitFormConf, setBitFormConf, setIsLoading, setSnack const fetchSingleFormFeilds = (formID, bitFormConf, setBitFormConf, setIsLoading, setSnackbar) => { setIsLoading(true) const requestParams = { - app_domain: bitFormConf.domainName, - api_key: bitFormConf.api_key, + ...buildAuthRequestParams(bitFormConf), id: bitFormConf.id } diff --git a/frontend/src/components/AllIntegrations/BuddyBoss/BuddyBossAuthorization.jsx b/frontend/src/components/AllIntegrations/BuddyBoss/BuddyBossAuthorization.jsx index 3668d08ab..2afd577a8 100644 --- a/frontend/src/components/AllIntegrations/BuddyBoss/BuddyBossAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/BuddyBoss/BuddyBossAuthorization.jsx @@ -1,103 +1,35 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + export default function BuddyBossAuthorization({ - formID, buddyBossConf, setBuddyBossConf, step, setStep, - isLoading, - setIsLoading, - setSnackbar + isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - // const [isLoading, setIsLoading] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'buddyBoss_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with BuddyBoss Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(buddyBossConf) - newConf[e.target.name] = e.target.value - setBuddyBossConf(newConf) - } - return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- - - {isLoading === 'auth' && ( -
- - Checking if BuddyBoss is active!!! -
- )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
- - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'BuddyBoss' - )} -
- )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
+ ) } diff --git a/frontend/src/components/AllIntegrations/CampaignMonitor/CampaignMonitorAuthorization.jsx b/frontend/src/components/AllIntegrations/CampaignMonitor/CampaignMonitorAuthorization.jsx index 98210e918..2ab941122 100644 --- a/frontend/src/components/AllIntegrations/CampaignMonitor/CampaignMonitorAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/CampaignMonitor/CampaignMonitorAuthorization.jsx @@ -1,12 +1,9 @@ -import { useState } from 'react' -import { toast } from 'react-hot-toast' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' +import Authorization from '../../Connections/Authorization' import { refreshCampaignMonitorLists } from './CampaignMonitorCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' export default function CampaignMonitorAuthorization({ campaignMonitorConf, @@ -14,179 +11,73 @@ export default function CampaignMonitorAuthorization({ step, setstep, setSnackbar, - isInfo, - isLoading, - setIsLoading + isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_key: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const handleAuthorize = () => { - const newConf = { ...campaignMonitorConf } - if (!newConf.name || !newConf.client_id || !newConf.api_key) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - client_id: !newConf.client_id ? __("Client Id can't be empty", 'bit-integrations') : '', - api_key: !newConf.api_key ? __("Access Api Key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading('auth') + const loadLists = useCallback( + connectionId => { + const nextConf = connectionId + ? { ...campaignMonitorConf, connection_id: connectionId } + : campaignMonitorConf - const data = { - api_key: newConf.api_key, - client_id: newConf.client_id - } + refreshCampaignMonitorLists(nextConf, setCampaignMonitorConf, () => {}, setSnackbar) + }, + [campaignMonitorConf, setCampaignMonitorConf, setSnackbar] + ) - bitsFetch(data, 'campaign_monitor_authorize').then(result => { - if (result && result.success) { - const newConf = { ...campaignMonitorConf } - newConf.tokenDetails = result.data - setCampaignMonitorConf(newConf) - setisAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if ( - (result && result.data) || - (!result.success && typeof result.data.Message === 'string') - ) { - toast.error( - `${__('Authorization failed Cause:', 'bit-integrations')}${result.data.Message}. ${__( - 'please try again', - 'bit-integrations' - )}` - ) - } else { - toast.error(__('Authorization failed. please try again', 'bit-integrations')) + const handleSetStep = useCallback( + value => { + if (value === 2 && !campaignMonitorConf?.default?.campaignMonitorLists) { + loadLists() } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...campaignMonitorConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setCampaignMonitorConf(newConf) - } - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - refreshCampaignMonitorLists(campaignMonitorConf, setCampaignMonitorConf, setIsLoading, setSnackbar) - setstep(2) - } - - const ActiveInstructions = ` -

${__('Get Client Id & Api key', 'bit-integrations')}

-
    -
  • ${__('First go to your CampaignMonitor dashboard.', 'bit-integrations')}
  • -
  • ${__('Click on Your "Profile Image" at the top right', 'bit-integrations')}
  • -
  • ${__('Click on the "Account Settings"', 'bit-integrations')}
  • -
  • ${__('Then Click "API keys"', 'bit-integrations')}
  • -
` - - return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- -
{error.name}
- -
- {__('Client id:', 'bit-integrations')} -
- -
{error.client_id}
- -
- {__('Access API Key:', 'bit-integrations')} -
- -
{error.api_key}
+ setstep(value) + }, + [campaignMonitorConf, loadLists, setstep] + ) - - {__('To Get Client Id & Api Key, Please Visit', 'bit-integrations')} -   + const note = ` +

${__('Get Client Id & Api key', 'bit-integrations')}

+
    +
  • ${__('First go to your CampaignMonitor dashboard.', 'bit-integrations')}
  • +
  • ${__('Click on your profile image at the top right.', 'bit-integrations')}
  • +
  • ${__('Click on Account Settings, then API keys.', 'bit-integrations')}
  • +
  • ${__('Use your API key in the Username field.', 'bit-integrations')}
  • +
+ + ${__('To get Client Id & API key, please visit', 'bit-integrations')} - {__('Campaign Monitor API Key', 'bit-integrations')} + ${__(' Campaign Monitor API Key', 'bit-integrations')} - -
-
- {isLoading === 'auth' && ( -
- - {__('Checking API Key!!!', 'bit-integrations')} -
- )} +
` - {showAuthMsg && !isAuthorized && !isLoading && ( -
- - × - - {__('Sorry, Api key is invalid', 'bit-integrations')} -
- )} - {!isInfo && ( - <> - -
- - - )} - -
+ return ( + ) } diff --git a/frontend/src/components/AllIntegrations/CampaignMonitor/CampaignMonitorCommonFunc.js b/frontend/src/components/AllIntegrations/CampaignMonitor/CampaignMonitorCommonFunc.js index d34d5307e..88c88b34f 100644 --- a/frontend/src/components/AllIntegrations/CampaignMonitor/CampaignMonitorCommonFunc.js +++ b/frontend/src/components/AllIntegrations/CampaignMonitor/CampaignMonitorCommonFunc.js @@ -8,6 +8,11 @@ export const handleInput = (e, campaignMonitorConf, setCampaignMonitorConf) => { setCampaignMonitorConf({ ...newConf }) } +const buildAuthRequestParams = conf => + conf.connection_id + ? { connection_id: conf.connection_id } + : { client_id: conf.client_id, api_key: conf.api_key } + // refreshMappedLists export const refreshCampaignMonitorLists = ( campaignMonitorConf, @@ -15,11 +20,8 @@ export const refreshCampaignMonitorLists = ( setIsLoading, setSnackbar ) => { - setIsLoading(true) - const refreshListsRequestParams = { - client_id: campaignMonitorConf.client_id, - api_key: campaignMonitorConf.api_key - } + if (typeof setIsLoading === 'function') setIsLoading(true) + const refreshListsRequestParams = buildAuthRequestParams(campaignMonitorConf) bitsFetch(refreshListsRequestParams, 'campaign_monitor_lists') .then(result => { @@ -51,9 +53,11 @@ export const refreshCampaignMonitorLists = ( msg: __('CampaignMonitor Lists refresh failed. please try again', 'bit-integrations') }) } - setIsLoading(false) + if (typeof setIsLoading === 'function') setIsLoading(false) + }) + .catch(() => { + if (typeof setIsLoading === 'function') setIsLoading(false) }) - .catch(() => setIsLoading(false)) } // refreshMappedFields @@ -63,10 +67,9 @@ export const refreshCampaignMonitorFields = ( setIsLoading, setSnackbar ) => { - setIsLoading(true) + if (typeof setIsLoading === 'function') setIsLoading(true) const refreshListsRequestParams = { - client_id: campaignMonitorConf.client_id, - api_key: campaignMonitorConf.api_key, + ...buildAuthRequestParams(campaignMonitorConf), listId: campaignMonitorConf.listId } @@ -87,9 +90,11 @@ export const refreshCampaignMonitorFields = ( msg: __('CampaignMonitor Custom fields refresh failed. please try again', 'bit-integrations') }) } - setIsLoading(false) + if (typeof setIsLoading === 'function') setIsLoading(false) + }) + .catch(() => { + if (typeof setIsLoading === 'function') setIsLoading(false) }) - .catch(() => setIsLoading(false)) } export const generateMappedField = campaignMonitorConf => { diff --git a/frontend/src/components/AllIntegrations/CapsuleCRM/CapsuleCRMAuthorization.jsx b/frontend/src/components/AllIntegrations/CapsuleCRM/CapsuleCRMAuthorization.jsx index f597bd90c..6173d334f 100644 --- a/frontend/src/components/AllIntegrations/CapsuleCRM/CapsuleCRMAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/CapsuleCRM/CapsuleCRMAuthorization.jsx @@ -1,131 +1,47 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { capsulecrmAuthentication } from './CapsuleCRMCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + export default function CapsuleCRMAuthorization({ capsulecrmConf, setCapsuleCRMConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '', api_url: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !capsulecrmConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...capsulecrmConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setCapsuleCRMConf(newConf) - } + const note = ` +

${__('Get API Token', 'bit-integrations')}

+
    +
  • ${__('Sign in to your CapsuleCRM account.', 'bit-integrations')}
  • +
  • ${__('Open My Preferences, then API Authentication Tokens.', 'bit-integrations')}
  • +
  • ${__('Create and copy your API token.', 'bit-integrations')}
  • +
  • ${__('For reference, your account domain looks like {name}.capsulecrm.com.', 'bit-integrations')}
  • +
` return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- - -
- {__('Your API URL:', 'bit-integrations')} -
- -
{error.api_url}
- {__('Example: {name}.capsulecrm.com', 'bit-integrations')} -
- {__('API Key:', 'bit-integrations')} -
- -
{error.api_key}
- {capsulecrmConf.api_url && ( - - {__('To Get API Token, Please Visit', 'bit-integrations')} -   - - {__('CapsuleCRM API Token', 'bit-integrations')} - - - )} -
-
- - {!isInfo && ( -
- -
- -
- )} -
+ ) } diff --git a/frontend/src/components/AllIntegrations/CapsuleCRM/CapsuleCRMCommonFunc.js b/frontend/src/components/AllIntegrations/CapsuleCRM/CapsuleCRMCommonFunc.js index fb6bdb7a5..7ddc2408f 100644 --- a/frontend/src/components/AllIntegrations/CapsuleCRM/CapsuleCRMCommonFunc.js +++ b/frontend/src/components/AllIntegrations/CapsuleCRM/CapsuleCRMCommonFunc.js @@ -51,45 +51,19 @@ export const checkMappedFields = capsulecrmConf => { return true } -export const capsulecrmAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_url || !confTmp.api_key) { - setError({ - api_url: !confTmp.api_url ? __("API URL can't be empty", 'bit-integrations') : '', - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { api_key: confTmp.api_key, api_url: confTmp.api_url } - - bitsFetch(requestParams, 'capsulecrm_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid api_url name & API key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + api_key: confTmp.api_key, + api_url: confTmp.api_url + } export const getCustomFields = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, customFields: true }) const requestParams = { - api_key: confTmp.api_key, - api_url: confTmp.api_url, + ...buildAuthRequestParams(confTmp), action: confTmp.actionName } @@ -116,7 +90,7 @@ export const getCustomFields = (confTmp, setConf, setLoading) => { export const getAllOpportunities = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, opportunities: true }) - const requestParams = { api_key: confTmp.api_key, api_url: confTmp.api_url } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'capsulecrm_fetch_all_opportunities').then(result => { if (result && result.success) { @@ -138,7 +112,7 @@ export const getAllOpportunities = (confTmp, setConf, setLoading) => { export const getAllOwners = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, owners: true }) - const requestParams = { api_key: confTmp.api_key, api_url: confTmp.api_url } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'capsulecrm_fetch_all_owners').then(result => { if (result && result.success) { @@ -160,7 +134,7 @@ export const getAllOwners = (confTmp, setConf, setLoading) => { export const getAllTeams = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, teams: true }) - const requestParams = { api_key: confTmp.api_key, api_url: confTmp.api_url } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'capsulecrm_fetch_all_teams').then(result => { if (result && result.success) { @@ -183,8 +157,7 @@ export const getAllCurrencies = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, currencies: true }) const requestParams = { - api_key: confTmp.api_key, - api_url: confTmp.api_url, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -209,8 +182,7 @@ export const getAllCRMParties = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMParties: true }) const requestParams = { - api_key: confTmp.api_key, - api_url: confTmp.api_url, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -234,10 +206,7 @@ export const getAllCRMParties = (confTmp, setConf, setLoading) => { export const getAllCRMMilestones = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMMilestones: true }) - const requestParams = { - api_key: confTmp.api_key, - api_url: confTmp.api_url - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'capsulecrm_fetch_all_CRMMilestones').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/Clickup/ClickupAuthorization.jsx b/frontend/src/components/AllIntegrations/Clickup/ClickupAuthorization.jsx index fb73b55c4..a50ab7aa6 100644 --- a/frontend/src/components/AllIntegrations/Clickup/ClickupAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Clickup/ClickupAuthorization.jsx @@ -1,129 +1,34 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { clickupAuthentication } from './ClickupCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' -import Note from '../../Utilities/Note' - -export default function ClickupAuthorization({ - clickupConf, - setClickupConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !clickupConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...clickupConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setClickupConf(newConf) - } - - const ActiveInstructions = ` -

${__('To get the ClickUp API Key', 'bit-integrations')}

-
    -
  • ${__('Navigate to your personal Settings.', 'bit-integrations')}
  • -
  • ${__('Click Apps in the left sidebar.', 'bit-integrations')}
  • -
  • ${__('Click Generate to create your API token.', 'bit-integrations')}
  • -
  • ${__('Click Copy to copy the key to your clipboard.', 'bit-integrations')}
  • -
  • ${__('Paste your API Key into the “API Key” field.', 'bit-integrations')}
  • -
` +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function ClickupAuthorization({ clickupConf, setClickupConf, step, setStep, isInfo }) { + const note = ` +

${__('To get the ClickUp API key', 'bit-integrations')}

+
    +
  • ${__('Open your personal Settings in ClickUp.', 'bit-integrations')}
  • +
  • ${__('Go to Apps in the left sidebar.', 'bit-integrations')}
  • +
  • ${__('Generate your API token and copy it.', 'bit-integrations')}
  • +
` return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- - -
- {__('API Key:', 'bit-integrations')} -
- -
{error.api_key}
- - - {__('To Get API Token, Please Visit', 'bit-integrations')} -   - - {__('Clickup API Token', 'bit-integrations')} - - -
-
- - {!isInfo && ( -
- -
- -
- )} - -
+ ) } diff --git a/frontend/src/components/AllIntegrations/Clickup/ClickupCommonFunc.js b/frontend/src/components/AllIntegrations/Clickup/ClickupCommonFunc.js index a5f60cbb5..ca35171f7 100644 --- a/frontend/src/components/AllIntegrations/Clickup/ClickupCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Clickup/ClickupCommonFunc.js @@ -45,43 +45,14 @@ export const checkMappedFields = clickupConf => { return true } -export const clickupAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { api_key: confTmp.api_key } - - bitsFetch(requestParams, 'clickup_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid API key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id ? { connection_id: confTmp.connection_id } : { api_key: confTmp.api_key } export const getCustomFields = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, customFields: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action: confTmp.actionName, list_id: confTmp.selectedList } @@ -115,7 +86,7 @@ export const getAllTeams = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, Teams: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -144,7 +115,7 @@ export const getAllSpaces = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, Spaces: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName, team_id: confTmp.selectedTeam } @@ -174,7 +145,7 @@ export const getAllFolders = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, Folders: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName, space_id: confTmp.selectedSpace } @@ -204,7 +175,7 @@ export const getAllLists = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, Lists: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName, folder_id: confTmp.selectedFolder } diff --git a/frontend/src/components/AllIntegrations/ClinchPad/ClinchPadAuthorization.jsx b/frontend/src/components/AllIntegrations/ClinchPad/ClinchPadAuthorization.jsx index 8717b5744..911c18cca 100644 --- a/frontend/src/components/AllIntegrations/ClinchPad/ClinchPadAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ClinchPad/ClinchPadAuthorization.jsx @@ -1,117 +1,50 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { clinchPadAuthentication } from './ClinchPadCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function ClinchPadAuthorization({ clinchPadConf, setClinchPadConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !clinchPadConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...clinchPadConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setClinchPadConf(newConf) - } + const note = ` +

${__('Get API token', 'bit-integrations')}

+
    +
  • ${__('Go to your ClinchPad account settings.', 'bit-integrations')}
  • +
  • ${__('Open API token section and copy the token.', 'bit-integrations')}
  • +
  • ${__('Paste the token and authorize.', 'bit-integrations')}
  • +
+ + ${__('To get API token, visit', 'bit-integrations')} + + ${__('ClinchPad Settings', 'bit-integrations')} + + + ` return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- - -
- {__('API Key:', 'bit-integrations')} -
- -
{error.api_key}
- - - {__('To Get API Token, Please Visit', 'bit-integrations')} -   - - {__('ClinchPad API Token', 'bit-integrations')} - - -
-
- - {!isInfo && ( -
- -
- -
- )} -
+ ({ + Authorization: `Basic ${btoa(`api-key:${authData.api_key || ''}`)}` + }) + }} + noteDetails={{ note }} + /> ) } diff --git a/frontend/src/components/AllIntegrations/ClinchPad/ClinchPadCommonFunc.js b/frontend/src/components/AllIntegrations/ClinchPad/ClinchPadCommonFunc.js index 061861e89..a585864e4 100644 --- a/frontend/src/components/AllIntegrations/ClinchPad/ClinchPadCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ClinchPad/ClinchPadCommonFunc.js @@ -4,6 +4,9 @@ import toast from 'react-hot-toast' import bitsFetch from '../../../Utils/bitsFetch' import { __ } from '../../../Utils/i18nwrap' +const buildAuthRequestParams = confTmp => + confTmp.connection_id ? { connection_id: confTmp.connection_id } : { api_key: confTmp.api_key } + export const handleInput = (e, clinchPadConf, setClinchPadConf) => { const newConf = { ...clinchPadConf } const { name } = e.target @@ -49,42 +52,10 @@ export const checkMappedFields = clinchPadConf => { return true } -export const clinchPadAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { api_key: confTmp.api_key } - - bitsFetch(requestParams, 'clinchPad_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid API key', 'bit-integrations')) - }) -} - export const getAllParentOrganizations = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, parentOrganizations: true }) - const requestParams = { api_key: confTmp.api_key } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'clinchPad_fetch_all_parentOrganizations').then(result => { if (result && result.success) { @@ -107,7 +78,7 @@ export const getAllCRMPipelines = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMPipelines: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -132,7 +103,7 @@ export const getAllCRMContacts = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMContacts: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } diff --git a/frontend/src/components/AllIntegrations/CompanyHub/CompanyHubAuthorization.jsx b/frontend/src/components/AllIntegrations/CompanyHub/CompanyHubAuthorization.jsx index 3cca45675..4fc328a68 100644 --- a/frontend/src/components/AllIntegrations/CompanyHub/CompanyHubAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/CompanyHub/CompanyHubAuthorization.jsx @@ -1,139 +1,83 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ /* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { companyHubAuthentication } from './CompanyHubCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import Authorization from '../../Connections/Authorization' +import { getAllCompanies, getAllContacts } from './CompanyHubCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' export default function CompanyHubAuthorization({ companyHubConf, setCompanyHubConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ sub_domain: '', api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const loadMetadata = useCallback( + connectionId => { + const nextConf = connectionId ? { ...companyHubConf, connection_id: connectionId } : companyHubConf - !companyHubConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...companyHubConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setCompanyHubConf(newConf) - } - - const ActiveInstructions = ` -

${__('To Get Public Id & Secret Key', 'bit-integrations')}

-
    -
  • ${__('First go to your CompanyHub dashboard.', 'bit-integrations')}
  • -
  • ${__('Click go to "Settings" from Left Bottom corner', 'bit-integrations')}
  • -
  • ${__('Then Click "Integrations"', 'bit-integrations')}
  • -
  • ${__('Then Click "Generate Api key"', 'bit-integrations')}
  • -
  • ${__('Then copy "API Authorization Credentials"', 'bit-integrations')}
  • -
` - - return ( -
- - -
- {__('Integration Name:', 'bit-integrations')} -
- - -
- {__('Sub Domain:', 'bit-integrations')} -
- -
{error.sub_domain}
+ getAllCompanies(nextConf, setCompanyHubConf, () => {}) + getAllContacts(nextConf, setCompanyHubConf, () => {}) + }, + [companyHubConf, setCompanyHubConf] + ) -
- {__('API Key:', 'bit-integrations')} -
- -
{error.api_key}
+ const handleSetStep = useCallback( + value => { + if (value === 2 && (!companyHubConf?.companies || !companyHubConf?.contacts)) { + loadMetadata() + } + setStep(value) + }, + [companyHubConf, loadMetadata, setStep] + ) - - {__('To Get Sub Domain & API Key, Please Visit', 'bit-integrations')} -   - - {__('CompanyHub Sub Domain & API Key', 'bit-integrations')} + const note = ` +

${__('To get Sub Domain & API Key', 'bit-integrations')}

+
    +
  • ${__('First go to your CompanyHub dashboard.', 'bit-integrations')}
  • +
  • ${__('Click Settings from the left-bottom corner.', 'bit-integrations')}
  • +
  • ${__('Then click Integrations and generate API key.', 'bit-integrations')}
  • +
+ + ${__('To get Sub Domain & API Key, please visit', 'bit-integrations')} +
+ ${__(' CompanyHub Sub Domain & API Key', 'bit-integrations')} - -
-
+
` - {!isInfo && ( -
- -
- -
- )} - -
+ return ( + ) } diff --git a/frontend/src/components/AllIntegrations/CompanyHub/CompanyHubCommonFunc.js b/frontend/src/components/AllIntegrations/CompanyHub/CompanyHubCommonFunc.js index 26463b262..33eaa5525 100644 --- a/frontend/src/components/AllIntegrations/CompanyHub/CompanyHubCommonFunc.js +++ b/frontend/src/components/AllIntegrations/CompanyHub/CompanyHubCommonFunc.js @@ -1,8 +1,6 @@ /* eslint-disable no-console */ /* eslint-disable no-else-return */ -import toast from 'react-hot-toast' import bitsFetch from '../../../Utils/bitsFetch' -import { __ } from '../../../Utils/i18nwrap' export const handleInput = (e, salesmateConf, setSalesmateConf) => { const newConf = { ...salesmateConf } @@ -41,96 +39,66 @@ export const checkMappedFields = companyHubConf => { return true } -export const companyHubAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.sub_domain || !confTmp.api_key) { - setError({ - sub_domain: !confTmp.sub_domain ? __("Sub Domain can't be empty", 'bit-integrations') : '', - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - sub_domain: confTmp.sub_domain, - api_key: confTmp.api_key - } - - bitsFetch(requestParams, 'company_hub_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid Sub Domain & API Key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + sub_domain: confTmp.sub_domain, + api_key: confTmp.api_key + } export const getAllCompanies = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, companies: true }) - - const requestParams = { - sub_domain: confTmp.sub_domain, - api_key: confTmp.api_key + if (typeof setLoading === 'function') { + setLoading(prev => ({ ...prev, companies: true })) } + const requestParams = buildAuthRequestParams(confTmp) + bitsFetch(requestParams, 'company_hub_fetch_all_companies').then(result => { if (result && result.success) { if (result.data) { - setConf(prevConf => { - prevConf.companies = result.data - return prevConf - }) + setConf(prevConf => ({ ...prevConf, companies: result.data })) - setLoading({ ...setLoading, companies: false }) - toast.success(__('Companies fetched successfully', 'bit-integrations')) + if (typeof setLoading === 'function') { + setLoading(prev => ({ ...prev, companies: false })) + } return } - setLoading({ ...setLoading, companies: false }) - toast.error(__('Companies Not Found!', 'bit-integrations')) + if (typeof setLoading === 'function') { + setLoading(prev => ({ ...prev, companies: false })) + } return } - setLoading({ ...setLoading, companies: false }) - toast.error(__('Companies fetching failed', 'bit-integrations')) + if (typeof setLoading === 'function') { + setLoading(prev => ({ ...prev, companies: false })) + } }) } export const getAllContacts = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, contact: true }) - - const requestParams = { - sub_domain: confTmp.sub_domain, - api_key: confTmp.api_key + if (typeof setLoading === 'function') { + setLoading(prev => ({ ...prev, contact: true })) } + const requestParams = buildAuthRequestParams(confTmp) + bitsFetch(requestParams, 'company_hub_fetch_all_contacts').then(result => { if (result && result.success) { if (result.data) { - setConf(prevConf => { - prevConf.contacts = result.data - return prevConf - }) + setConf(prevConf => ({ ...prevConf, contacts: result.data })) - setLoading({ ...setLoading, contact: false }) - toast.success(__('Contacts fetched successfully', 'bit-integrations')) + if (typeof setLoading === 'function') { + setLoading(prev => ({ ...prev, contact: false })) + } return } - setLoading({ ...setLoading, contact: false }) - toast.error(__('Contacts not found!', 'bit-integrations')) + if (typeof setLoading === 'function') { + setLoading(prev => ({ ...prev, contact: false })) + } return } - setLoading({ ...setLoading, contact: false }) - toast.error(__('Contacts fetching failed', 'bit-integrations')) + if (typeof setLoading === 'function') { + setLoading(prev => ({ ...prev, contact: false })) + } }) } diff --git a/frontend/src/components/AllIntegrations/ConstantContact/ConstantContact.jsx b/frontend/src/components/AllIntegrations/ConstantContact/ConstantContact.jsx index 258b4a095..2c45caab3 100644 --- a/frontend/src/components/AllIntegrations/ConstantContact/ConstantContact.jsx +++ b/frontend/src/components/AllIntegrations/ConstantContact/ConstantContact.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' import BackIcn from '../../../Icons/BackIcn' @@ -11,7 +11,6 @@ import ConstantContactAuthorization from './ConstantContactAuthorization' import { checkAddressFieldMapRequired, handleInput, - setGrantTokenResponse, checkMappedFields, generateMappedField } from './ConstantContactCommonFunc' @@ -95,10 +94,6 @@ function ConstantContact({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - // eslint-disable-next-line no-unused-expressions - window.opener && setGrantTokenResponse('constantContact') - }, []) const nextPage = () => { setTimeout(() => { document.getElementById('btcd-settings-wrp').scrollTop = 0 @@ -130,9 +125,6 @@ function ConstantContact({ formFields, setFlow, flow, allIntegURL }) { setConstantContactConf={setConstantContactConf} step={step} setstep={setstep} - isLoading={isLoading} - setIsLoading={setIsLoading} - setSnackbar={setSnackbar} /> {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/ConstantContact/ConstantContactAuthorization.jsx b/frontend/src/components/AllIntegrations/ConstantContact/ConstantContactAuthorization.jsx index dbfb25d1c..89453167d 100644 --- a/frontend/src/components/AllIntegrations/ConstantContact/ConstantContactAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ConstantContact/ConstantContactAuthorization.jsx @@ -1,194 +1,54 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import CopyText from '../../Utilities/CopyText' -import { handleConstantContactAuthorize } from './ConstantContactCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' -import { $appConfigState } from '../../../GlobalStates' -import { useRecoilValue } from 'recoil' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function ConstantContactAuthorization({ constantContactConf, setConstantContactConf, step, setstep, - isLoading, - setIsLoading, - setSnackbar, - redirectLocation, isInfo }) { - const btcbi = useRecoilValue($appConfigState) - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ - dataCenter: '', - clientId: '', - clientSecret: '' - }) - const scopes = 'account_read account_update contact_data offline_access campaign_data' - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - } - - const handleInput = e => { - const newConf = { ...constantContactConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setConstantContactConf(newConf) - } - const note = ` -

${__('Step of get API Key(Client Id) And Client Secret:', 'bit-integrations')}

+

${__('Steps to get Client ID and Client Secret', 'bit-integrations')}

    +
  • ${__('Go to Constant Contact developer portal and create app.', 'bit-integrations')}
  • +
  • ${__('Enable Authorization Code flow and refresh token support.', 'bit-integrations')}
  • ${__( - 'Goto', - 'bit-integrations' - )} ${__( - 'Constant Contact Application', - 'bit-integrations' - )}
  • -
  • ${__('Then create a new application.', 'bit-integrations')}
  • -
  • ${__( - 'Select (Authorization Code Flow and Implicit Flow) and (Rotating Refresh Tokens or Long Lived Refresh Tokens).', - 'bit-integrations' - )}
  • -
  • ${__( - 'Copy the Authorized Redirect URIs from here and paste it into the Constant Contact application form.', - 'bit-integrations' - )}
  • -
  • ${__( - 'Then generate Client Secret from the Constant Contact application', + 'Copy redirect URI from this form and add it to app configuration.', 'bit-integrations' )}
  • -
  • ${__( - 'Copy the Client Id and Client Secret from Constant Contact application and paste into this authorization form.', - 'bit-integrations' - )}
  • -
  • ${__('Finally, click Authorize button.', 'bit-integrations')}
  • -
+
  • ${__('Copy client ID and client secret, then click Authorize.', 'bit-integrations')}
  • + ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - - - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Constant Contact Application', 'bit-integrations')} - - - -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    - {!isInfo && ( - <> - -
    - - - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/ConstantContact/ConstantContactCommonFunc.js b/frontend/src/components/AllIntegrations/ConstantContact/ConstantContactCommonFunc.js index d9863c057..ac7da5dc0 100644 --- a/frontend/src/components/AllIntegrations/ConstantContact/ConstantContactCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ConstantContact/ConstantContactCommonFunc.js @@ -1,7 +1,7 @@ import toast from 'react-hot-toast' import bitsFetch from '../../../Utils/bitsFetch' import { deepCopy } from '../../../Utils/Helpers' -import { sprintf, __ } from '../../../Utils/i18nwrap' +import { __ } from '../../../Utils/i18nwrap' export const handleInput = ( e, @@ -43,6 +43,15 @@ export const checkAddressFieldMapRequired = constantContactConf => { return true } +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + const listChange = (constantContactConf, setConstantContactConf) => { const newConf = deepCopy(constantContactConf) newConf.field_map = [{ formField: '', constantContactFormField: '' }] @@ -55,9 +64,7 @@ export const getAllContactLists = (id, confTmp, setConf, isLoading, setIsLoading const requestParams = { integId: id, - clientId: confTmp.clientId, - clientSecret: confTmp.clientSecret, - tokenDetails: confTmp.tokenDetails + ...buildAuthRequestParams(confTmp) } bitsFetch(requestParams, 'cContact_refresh_list').then(result => { @@ -65,6 +72,9 @@ export const getAllContactLists = (id, confTmp, setConf, isLoading, setIsLoading const newConf = { ...confTmp } if (result.data) { newConf.lists = result.data.contactList + if (result.data.tokenDetails) { + newConf.tokenDetails = result.data.tokenDetails + } } setConf(newConf) setIsLoading({ ...isLoading, list: false }) @@ -79,18 +89,19 @@ export const getAllContactLists = (id, confTmp, setConf, isLoading, setIsLoading const getAllCustomFields = (confTmp, setConf) => { const requestParams = { - clientId: confTmp.clientId, - clientSecret: confTmp.clientSecret, - tokenDetails: confTmp.tokenDetails + ...buildAuthRequestParams(confTmp) } bitsFetch(requestParams, 'cContact_custom_fields').then(result => { if (result && result.success) { const newConf = { ...confTmp } - if (result.data) { + if (result.data?.customFields) { const mergedFields = newConf.default.constantContactFields.concat(result.data.customFields) newConf.default.constantContactFields = mergedFields } + if (result.data?.tokenDetails) { + newConf.tokenDetails = result.data.tokenDetails + } setConf(newConf) } }) @@ -101,9 +112,7 @@ export const getContactTags = (id, confTmp, setConf, isLoading, setIsLoading) => const requestParams = { integId: id, - clientId: confTmp.clientId, - clientSecret: confTmp.clientSecret, - tokenDetails: confTmp.tokenDetails + ...buildAuthRequestParams(confTmp) } bitsFetch(requestParams, 'cContact_refresh_tags').then(result => { @@ -111,6 +120,9 @@ export const getContactTags = (id, confTmp, setConf, isLoading, setIsLoading) => const newConf = { ...confTmp } if (result.data) { newConf.tags = result.data.contactTag + if (result.data.tokenDetails) { + newConf.tokenDetails = result.data.tokenDetails + } } setConf(newConf) setIsLoading({ ...isLoading, tag: false }) @@ -123,147 +135,6 @@ export const getContactTags = (id, confTmp, setConf, isLoading, setIsLoading) => }) } -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation.replace(`${window.opener.location.href}`, '').split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] - } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} - -export const handleConstantContactAuthorize = ( - integ, - ajaxInteg, - scopes, - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - if (!confTmp.clientId) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - - const apiEndpoint = `https://authz.constantcontact.com/oauth2/default/v1/authorize?scope=${scopes}&response_type=code&client_id=${ - confTmp.clientId - }&state=${encodeURIComponent(window.location.href)}/redirect&redirect_uri=${encodeURIComponent( - `${btcbi.api}` - )}/redirect` - - const authWindow = window.open(apiEndpoint, integ, 'width=400,height=609,toolbar=off') - - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitsConstantContact = localStorage.getItem(`__${integ}`) - if (bitsConstantContact) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsConstantContact) - localStorage.removeItem(`__${integ}`) - if (grantTokenResponse.code.search('#')) { - const [code] = grantTokenResponse.code.split('#') - grantTokenResponse.code = code - } - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper( - ajaxInteg, - grantTokenResponse, - newConf, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi - ) - } - } - }, 500) -} - -const tokenHelper = ( - ajaxInteg, - grantToken, - confTmp, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - - bitsFetch(tokenRequestParams, `${ajaxInteg}_generate_token`) - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Authorized Successfully', 'bit-integrations') - }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} - export const checkMappedFields = sheetconf => { const mappedFleld = sheetconf.field_map ? sheetconf.field_map.filter(mapped => !mapped.formField && !mapped.constantContactFormField) diff --git a/frontend/src/components/AllIntegrations/ConvertKit/ConvertKitAuthorization.jsx b/frontend/src/components/AllIntegrations/ConvertKit/ConvertKitAuthorization.jsx index 681273045..0422388f7 100644 --- a/frontend/src/components/AllIntegrations/ConvertKit/ConvertKitAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ConvertKit/ConvertKitAuthorization.jsx @@ -1,70 +1,39 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' import { refreshConvertKitForm } from './ConvertKitCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' export default function ConvertKitAuthorization({ - formID, convertKitConf, setConvertKitConf, step, setstep, setSnackbar, isInfo, - isLoading, setIsLoading }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_secret: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) + const loadForms = useCallback( + connectionId => { + const nextConf = connectionId ? { ...convertKitConf, connection_id: connectionId } : convertKitConf - const handleAuthorize = () => { - const newConf = { ...convertKitConf } - if (!newConf.name || !newConf.api_secret) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - api_secret: !newConf.api_secret - ? __("Access API Secret Key can't be empty", 'bit-integrations') - : '' - }) - return - } - setIsLoading('auth') - const data = { - api_secret: newConf.api_secret - } - bitsFetch(data, 'convertKit_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...convertKitConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setConvertKitConf(newConf) - } - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + refreshConvertKitForm(nextConf, setConvertKitConf, setIsLoading, setSnackbar) + }, + [convertKitConf, setConvertKitConf, setIsLoading, setSnackbar] + ) - refreshConvertKitForm(convertKitConf, setConvertKitConf, setIsLoading, setSnackbar) - setstep(2) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !convertKitConf?.default?.convertKitForms) { + loadForms() + } + setstep(value) + }, + [convertKitConf, loadForms, setstep] + ) - const ActiveInstructions = ` + const note = `

    ${__('Get api secret key', 'bit-integrations')}

    • ${sprintf( @@ -75,90 +44,23 @@ export default function ConvertKitAuthorization({
    ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - -
    {error.name}
    - -
    - {__('Access API Secret Key:', 'bit-integrations')} -
    - -
    {error.api_secret}
    - - - {__('To Get API Secret Key, Please Visit', 'bit-integrations')} -   - - {sprintf(__('%s API Token', 'bit-integrations'), 'Kit(ConvertKit)')} - - -
    -
    - - {isLoading === 'auth' && ( -
    - - {__('Checking API Secret Key', 'bit-integrations')}!!! -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {__('Sorry, API Secret key is invalid', 'bit-integrations')} -
    - )} - {!isInfo && ( - <> - -
    - - - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/ConvertKit/ConvertKitCommonFunc.js b/frontend/src/components/AllIntegrations/ConvertKit/ConvertKitCommonFunc.js index 75a2af68a..c2fa94eba 100644 --- a/frontend/src/components/AllIntegrations/ConvertKit/ConvertKitCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ConvertKit/ConvertKitCommonFunc.js @@ -9,9 +9,12 @@ export const handleInput = (e, convertKitConf, setConvertKitConf) => { setConvertKitConf({ ...newConf }) } +const buildAuthRequestParams = conf => + conf.connection_id ? { connection_id: conf.connection_id } : { api_secret: conf.api_secret } + export const refreshConvertKitForm = (convertKitConf, setConvertKitConf, setIsLoading, setSnackbar) => { const refreshFormsRequestParams = { - api_secret: convertKitConf.api_secret + ...buildAuthRequestParams(convertKitConf) } bitsFetch(refreshFormsRequestParams, 'convertKit_forms') .then(result => { @@ -57,7 +60,7 @@ export const refreshConvertKitForm = (convertKitConf, setConvertKitConf, setIsLo // refreshConvertKitTags export const refreshConvertKitTags = (convertKitConf, setConvertKitConf, setIsLoading, setSnackbar) => { const refreshFormsRequestParams = { - api_secret: convertKitConf.api_secret + ...buildAuthRequestParams(convertKitConf) } bitsFetch(refreshFormsRequestParams, 'convertKit_tags') .then(result => { @@ -136,7 +139,7 @@ export const refreshConvertKitHeader = ( }) } else { const refreshFormsRequestParams = { - api_secret: convertKitConf.api_secret + ...buildAuthRequestParams(convertKitConf) } bitsFetch(refreshFormsRequestParams, 'convertKit_headers') diff --git a/frontend/src/components/AllIntegrations/CopperCRM/CopperCRMAuthorization.jsx b/frontend/src/components/AllIntegrations/CopperCRM/CopperCRMAuthorization.jsx index 51a5caca0..ecc17a211 100644 --- a/frontend/src/components/AllIntegrations/CopperCRM/CopperCRMAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/CopperCRM/CopperCRMAuthorization.jsx @@ -1,131 +1,55 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { coppercrmAuthentication } from './CopperCRMCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function CopperCRMAuthorization({ copperCRMConf, setCopperCRMConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '', api_email: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !copperCRMConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...copperCRMConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setCopperCRMConf(newConf) - } - const ActiveInstructions = ` -

    ${__('Get api secret key', 'bit-integrations')}

    -
      -
    • ${__('First go to your Copper dashboard.', 'bit-integrations')}
    • -
    • ${__('Then click Settings from Navbar.', 'bit-integrations')}
    • -
    • ${__('Click "Integrations", Then click "API Keys"', 'bit-integrations')}
    • -
    ` + const note = ` +

    ${__('Get API credentials', 'bit-integrations')}

    +
      +
    • ${__('Go to your Copper dashboard.', 'bit-integrations')}
    • +
    • ${__('Open Settings > Integrations > API Keys.', 'bit-integrations')}
    • +
    • ${__('Copy your API key and account email, then authorize.', 'bit-integrations')}
    • +
    + ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Your API Email:', 'bit-integrations')} -
    - -
    {error.api_email}
    - {/* - {__('Example: {name}.coppercrm.com', 'bit-integrations')} - */} -
    - {__('API Key:', 'bit-integrations')} -
    - -
    {error.api_key}
    - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/CopperCRM/CopperCRMCommonFunc.js b/frontend/src/components/AllIntegrations/CopperCRM/CopperCRMCommonFunc.js index 88f32d758..6896c1f90 100644 --- a/frontend/src/components/AllIntegrations/CopperCRM/CopperCRMCommonFunc.js +++ b/frontend/src/components/AllIntegrations/CopperCRM/CopperCRMCommonFunc.js @@ -4,6 +4,14 @@ import toast from 'react-hot-toast' import bitsFetch from '../../../Utils/bitsFetch' import { __ } from '../../../Utils/i18nwrap' +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + api_key: confTmp.api_key, + api_email: confTmp.api_email + } + export const handleInput = (e, copperCRMConf, setCopperCRMConf) => { const newConf = { ...copperCRMConf } const { name } = e.target @@ -51,48 +59,11 @@ export const checkMappedFields = copperCRMConf => { return true } -export const coppercrmAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_email || !confTmp.api_key) { - setError({ - api_email: !confTmp.api_email ? __("API Email can't be empty", 'bit-integrations') : '', - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - api_key: confTmp.api_key, - api_email: confTmp.api_email - } - - bitsFetch(requestParams, 'coppercrm_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid api_email name & API key', 'bit-integrations')) - }) -} - export const getCustomFields = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, customFields: true }) const requestParams = { - api_key: confTmp.api_key, - api_email: confTmp.api_email, + ...buildAuthRequestParams(confTmp), action: confTmp.actionName } @@ -119,10 +90,7 @@ export const getCustomFields = (confTmp, setConf, setLoading) => { export const getAllOpportunities = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, opportunities: true }) - const requestParams = { - api_key: confTmp.api_key, - api_email: confTmp.api_email - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'coppercrm_fetch_all_opportunities').then(result => { if (result && result.success) { @@ -144,10 +112,7 @@ export const getAllOpportunities = (confTmp, setConf, setLoading) => { export const getAllOwners = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, owners: true }) - const requestParams = { - api_key: confTmp.api_key, - api_email: confTmp.api_email - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'coppercrm_fetch_all_owners').then(result => { if (result && result.success) { @@ -169,10 +134,7 @@ export const getAllOwners = (confTmp, setConf, setLoading) => { export const getAllCompanies = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, companies: true }) - const requestParams = { - api_key: confTmp.api_key, - api_email: confTmp.api_email - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'coppercrm_fetch_all_companies').then(result => { if (result && result.success) { @@ -194,10 +156,7 @@ export const getAllCompanies = (confTmp, setConf, setLoading) => { export const getAllTags = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, tags: true }) - const requestParams = { - api_key: confTmp.api_key, - api_email: confTmp.api_email - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'coppercrm_fetch_all_tags').then(result => { if (result && result.success) { @@ -220,8 +179,7 @@ export const getAllPipelineStages = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, pipelineStages: true }) const requestParams = { - api_key: confTmp.api_key, - api_email: confTmp.api_email, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -246,8 +204,7 @@ export const getAllCRMPeoples = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMPeoples: true }) const requestParams = { - api_key: confTmp.api_key, - api_email: confTmp.api_email, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -271,10 +228,7 @@ export const getAllCRMPeoples = (confTmp, setConf, setLoading) => { export const getAllCRMPipelines = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMPipelines: true }) - const requestParams = { - api_key: confTmp.api_key, - api_email: confTmp.api_email - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'coppercrm_fetch_all_CRMPipelines').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/CreatorLms/CreatorLmsAuthorization.jsx b/frontend/src/components/AllIntegrations/CreatorLms/CreatorLmsAuthorization.jsx index fd7cd0535..e8db792b6 100644 --- a/frontend/src/components/AllIntegrations/CreatorLms/CreatorLmsAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/CreatorLms/CreatorLmsAuthorization.jsx @@ -1,115 +1,42 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function CreatorLmsAuthorization({ - formID, creatorLmsConf, setCreatorLmsConf, step, nextPage, - isLoading, - setIsLoading, - setSnackbar, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'creator_lms_authorize').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Creator LMS Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = { ...creatorLmsConf } - newConf[e.target.name] = e.target.value - setCreatorLmsConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - {isLoading === 'auth' && ( -
    - - {__('Checking if Creator LMS is authorized!!!', 'bit-integrations')} -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    -
    -
    - -
    -
    - {__('Creator LMS is not activated or not installed', 'bit-integrations')} -
    -
    -
    - )} - - {showAuthMsg && isAuthorized && !isLoading && ( -
    -
    - -
    -
    {__('Creator LMS is activated', 'bit-integrations')}
    -
    - )} - - -
    - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Demio/DemioAuthorization.jsx b/frontend/src/components/AllIntegrations/Demio/DemioAuthorization.jsx index 3844926c1..462fd865d 100644 --- a/frontend/src/components/AllIntegrations/Demio/DemioAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Demio/DemioAuthorization.jsx @@ -1,141 +1,73 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { demioAuthentication, getAllEvents } from './DemioCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllEvents } from './DemioCommonFunc' export default function DemioAuthorization({ demioConf, setDemioConf, step, setStep, - loading, setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '', api_secret: '' }) - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !demioConf?.default - setStep(2) - getAllEvents(demioConf, setDemioConf, setLoading) - } + const loadEvents = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...demioConf, connection_id: connectionId } : demioConf + getAllEvents(nextConf, setDemioConf, setLoading) + }, + [demioConf, setDemioConf, setLoading] + ) - const handleInput = e => { - const newConf = { ...demioConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setDemioConf(newConf) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !(demioConf?.events || []).length) { + loadEvents() + } + setStep(value) + }, + [demioConf, loadEvents, setStep] + ) - const ActiveInstructions = ` -

    ${__('To Get API Key & API Secret', 'bit-integrations')}

    -
      -
    • ${__('First go to your Demio dashboard.', 'bit-integrations')}
    • -
    • ${__('Click go to "Settings" from Right Top corner', 'bit-integrations')}
    • -
    • ${__('Then Click "API" from the "Settings Menu"', 'bit-integrations')}
    • -
    • ${__('Then Click "Generate Api Secret"', 'bit-integrations')}
    • -
    • ${__('Then copy "API Authorization Credentials"', 'bit-integrations')}
    • -
    ` + const note = ` +

    ${__('To get API Key and API Secret', 'bit-integrations')}

    +
      +
    • ${__('Open your Demio dashboard.', 'bit-integrations')}
    • +
    • ${__('Go to Settings, then API.', 'bit-integrations')}
    • +
    • ${__('Generate API secret and copy credentials.', 'bit-integrations')}
    • +
    ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('API Key:', 'bit-integrations')} -
    - -
    {error.api_key}
    - -
    - {__('API Secret:', 'bit-integrations')} -
    - -
    {error.api_secret}
    - - - {__('To Get API Key & API Secret, Please Visit', 'bit-integrations')} -   - - {__('Demio API Key & Secret', 'bit-integrations')} - - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Demio/DemioCommonFunc.js b/frontend/src/components/AllIntegrations/Demio/DemioCommonFunc.js index caeb92369..2f7c551dc 100644 --- a/frontend/src/components/AllIntegrations/Demio/DemioCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Demio/DemioCommonFunc.js @@ -41,49 +41,15 @@ export const checkMappedFields = demioConf => { return true } -export const demioAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key || !confTmp.api_secret) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '', - api_secret: !confTmp.api_secret ? __("API Secret can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - api_key: confTmp.api_key, - api_secret: confTmp.api_secret - } - - bitsFetch(requestParams, 'demio_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid Sub Domain & API Key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { api_key: confTmp.api_key, api_secret: confTmp.api_secret } export const getAllEvents = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, event: true }) - const requestParams = { - api_key: confTmp.api_key, - api_secret: confTmp.api_secret - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'demio_fetch_all_events').then(result => { if (result && result.success) { @@ -110,8 +76,7 @@ export const getAllSessions = (confTmp, setConf, event_id, setLoading) => { setLoading({ ...setLoading, session: true }) const requestParams = { - api_key: confTmp.api_key, - api_secret: confTmp.api_secret, + ...buildAuthRequestParams(confTmp), event_id: event_id } diff --git a/frontend/src/components/AllIntegrations/DirectIq/DirectIqAuthorization.jsx b/frontend/src/components/AllIntegrations/DirectIq/DirectIqAuthorization.jsx index b21635d6e..b2c7528b1 100644 --- a/frontend/src/components/AllIntegrations/DirectIq/DirectIqAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/DirectIq/DirectIqAuthorization.jsx @@ -1,176 +1,62 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +/* eslint-disable jsx-a11y/anchor-is-valid */ +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' import { refreshDirectIqList } from './DirectIqCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' export default function DirectIqAuthorization({ - formID, directIqConf, setDirectIqConf, step, setstep, setSnackbar, isInfo, - isLoading, setIsLoading }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', client_secret: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) + const loadLists = useCallback( + connectionId => { + const nextConf = connectionId ? { ...directIqConf, connection_id: connectionId } : directIqConf - const handleAuthorize = () => { - const newConf = { ...directIqConf } - if (!newConf.name || !newConf.client_secret) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - client_id: !newConf.client_id ? __("Access Client Id can't be empty", 'bit-integrations') : '', - client_secret: !newConf.client_secret - ? __("Access Client Secret Key can't be empty", 'bit-integrations') - : '' - }) - return - } - setIsLoading('auth') - const data = { - client_id: newConf.client_id, - client_secret: newConf.client_secret - } - bitsFetch(data, 'directIq_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...directIqConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setDirectIqConf(newConf) - } - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + refreshDirectIqList(nextConf, setDirectIqConf, setIsLoading, setSnackbar) + }, + [directIqConf, setDirectIqConf, setIsLoading, setSnackbar] + ) - refreshDirectIqList(directIqConf, setDirectIqConf, setIsLoading, setSnackbar) - setstep(2) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !directIqConf?.default?.directIqLists) { + loadLists() + } + setstep(value) + }, + [directIqConf, loadLists, setstep] + ) - const ActiveInstructions = ` -

    ${__('Get client id and client secret key', 'bit-integrations')}

    -
      -
    • ${__('First go to your DirectIq dashboard.', 'bit-integrations')}
    • -
    • ${__('Click "Integrations", Then click "API Keys"', 'bit-integrations')}
    • -
    ` + const note = ` +

    ${__('Get client id and client secret key', 'bit-integrations')}

    +
      +
    • ${__('First go to your DirectIq dashboard.', 'bit-integrations')}
    • +
    • ${__('Click "Integrations", Then click "API Keys"', 'bit-integrations')}
    • +
    ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - -
    {error.name}
    - -
    - {__('Access Client id:', 'bit-integrations')} -
    - -
    {error.client_id}
    - -
    - {__('Access Client Secret Key:', 'bit-integrations')} -
    - -
    {error.client_secret}
    - - - {__('To Get Client Id and Client Secret Key, Please Visit', 'bit-integrations')} -   - - {__('DirectIQ API Token', 'bit-integrations')} - - -
    -
    - {isLoading === 'auth' && ( -
    - - Checking Client Secret Key!!! -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - Sorry, Client Secret key is invalid -
    - )} - {!isInfo && ( - <> - -
    - - - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/DirectIq/DirectIqCommonFunc.js b/frontend/src/components/AllIntegrations/DirectIq/DirectIqCommonFunc.js index a9fbf4f34..2b43bc62d 100644 --- a/frontend/src/components/AllIntegrations/DirectIq/DirectIqCommonFunc.js +++ b/frontend/src/components/AllIntegrations/DirectIq/DirectIqCommonFunc.js @@ -8,12 +8,17 @@ export const handleInput = (e, directIqConf, setDirectIqConf) => { setDirectIqConf({ ...newConf }) } +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + client_id: confTmp.client_id, + client_secret: confTmp.client_secret + } + // refreshMappedLists export const refreshDirectIqList = (directIqConf, setDirectIqConf, setIsLoading, setSnackbar) => { - const refreshListsRequestParams = { - client_id: directIqConf.client_id, - client_secret: directIqConf.client_secret - } + const refreshListsRequestParams = buildAuthRequestParams(directIqConf) bitsFetch(refreshListsRequestParams, 'directIq_lists') .then(result => { if (result && result.success) { @@ -52,8 +57,7 @@ export const refreshDirectIqList = (directIqConf, setDirectIqConf, setIsLoading, // refreshMappedFields export const refreshDirectIqHeader = (directIqConf, setDirectIqConf, setIsLoading, setSnackbar) => { const refreshListsRequestParams = { - client_id: directIqConf.client_id, - client_secret: directIqConf.client_secret, + ...buildAuthRequestParams(directIqConf), list_id: directIqConf.listId } diff --git a/frontend/src/components/AllIntegrations/Discord/Discord.jsx b/frontend/src/components/AllIntegrations/Discord/Discord.jsx index a20579d2c..933b380f4 100644 --- a/frontend/src/components/AllIntegrations/Discord/Discord.jsx +++ b/frontend/src/components/AllIntegrations/Discord/Discord.jsx @@ -1,7 +1,7 @@ /* eslint-disable no-unused-expressions */ import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' -import { useNavigate, useParams } from 'react-router' +import { useNavigate } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' @@ -14,7 +14,6 @@ import BackIcn from '../../../Icons/BackIcn' function Discord({ formFields, setFlow, flow, allIntegURL }) { const navigate = useNavigate() - const { formID } = useParams() const [isLoading, setIsLoading] = useState(false) const [step, setstep] = useState(1) const [snack, setSnackbar] = useState({ show: false }) @@ -51,14 +50,11 @@ function Discord({ formFields, setFlow, flow, allIntegURL }) { {/* STEP 1 */} {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/Discord/DiscordAuthorization.jsx b/frontend/src/components/AllIntegrations/Discord/DiscordAuthorization.jsx index 8eaf6027d..f1a687ba4 100644 --- a/frontend/src/components/AllIntegrations/Discord/DiscordAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Discord/DiscordAuthorization.jsx @@ -1,43 +1,47 @@ -import { useState } from 'react' +/* eslint-disable jsx-a11y/anchor-is-valid */ +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { getAllServers, handleAuthorize } from './DiscordCommonFunc' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllServers } from './DiscordCommonFunc' export default function DiscordAuthorization({ - formID, discordConf, setDiscordConf, step, setstep, - isLoading, setIsLoading, - setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ accessToken: '' }) - const nextPage = () => { - getAllServers(discordConf, setDiscordConf, setIsLoading) - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const loadServers = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...discordConf, connection_id: connectionId } : discordConf - setstep(2) - } - const handleInput = e => { - const newConf = { ...discordConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setDiscordConf(newConf) - } + await getAllServers(nextConf, setDiscordConf, setIsLoading) + }, + [discordConf, setDiscordConf, setIsLoading] + ) + + const handleConnectionSelected = useCallback( + async connectionId => { + await loadServers(connectionId) + }, + [loadServers] + ) + + const handleSetStep = useCallback( + value => { + if (value === 2 && !discordConf?.default) { + loadServers() + } + + setstep(value) + }, + [discordConf, loadServers, setstep] + ) - const discordInstructions = ` -

    ${__('Get Access Token few step', 'bit-integrations')}

    + const discordInstructions = `

    ${__('Get Access Token few step', 'bit-integrations')}

    • ${__('First create app.', 'bit-integrations')}
    • ${__('Click on OAuth2.', 'bit-integrations')}
    • @@ -48,90 +52,32 @@ export default function DiscordAuthorization({ 'bit-integrations' )}
    • ${__( - 'Then click on Bot from left navbar and copy the Access token.', + 'Then click on Bot from left navbar and copy the Access token.', 'bit-integrations' )}
    ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - - {__('To get access Token , Please Visit', 'bit-integrations')}{' '} - - {__('Discord Console', 'bit-integrations')} - - - -
    - {__('Access Token:', 'bit-integrations')} -
    - -
    {error.accessToken}
    - - {!isInfo && ( - <> - -
    - - - )} - - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Discord/DiscordCommonFunc.js b/frontend/src/components/AllIntegrations/Discord/DiscordCommonFunc.js index bd115e8b1..ed5f6dbe4 100644 --- a/frontend/src/components/AllIntegrations/Discord/DiscordCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Discord/DiscordCommonFunc.js @@ -14,19 +14,18 @@ export const handleInput = (e, discordConf, setDiscordConf) => { setDiscordConf({ ...newConf }) } +const buildAuthRequestParams = confTmp => + confTmp.connection_id ? { connection_id: confTmp.connection_id } : { accessToken: confTmp.accessToken } + export const getAllServers = (confTmp, setConf, setIsLoading) => { - if (!confTmp.accessToken) { - setError({ - accessToken: !confTmp.accessToken ? __("Access Token can't be empty", 'bit-integrations') : '' - }) + if (!confTmp.connection_id && !confTmp.accessToken) { + toast.error(__("Access Token can't be empty", 'bit-integrations')) return } setIsLoading(true) - const tokenRequestParams = { accessToken: confTmp.accessToken } - - bitsFetch(tokenRequestParams, 'discord_fetch_servers').then(result => { + bitsFetch(buildAuthRequestParams(confTmp), 'discord_fetch_servers').then(result => { if (result && result.success) { setConf(oldConf => { const newConf = { ...oldConf } @@ -40,27 +39,34 @@ export const getAllServers = (confTmp, setConf, setIsLoading) => { }) setIsLoading(false) - toast.success(__('Servers fetched successfully', 'bit-integrations')) return } + setIsLoading(false) toast.error(__('Servers fetching failed', 'bit-integrations')) }) } export const getAllChannels = (confTmp, setConf, setIsLoading) => { - if (!confTmp.accessToken) { - setError({ - accessToken: !confTmp.accessToken ? __("Access Token can't be empty", 'bit-integrations') : '' - }) + if (!confTmp.connection_id && !confTmp.accessToken) { + toast.error(__("Access Token can't be empty", 'bit-integrations')) + return + } + + if (!confTmp.selectedServer) { + toast.error(__('Server is required', 'bit-integrations')) return } + setIsLoading(true) - const tokenRequestParams = { accessToken: confTmp.accessToken, serverId: confTmp.selectedServer } + const requestParams = { + ...buildAuthRequestParams(confTmp), + serverId: confTmp.selectedServer + } - bitsFetch(tokenRequestParams, 'discord_fetch_channels').then(result => { + bitsFetch(requestParams, 'discord_fetch_channels').then(result => { if (result && result.success) { setConf(oldConf => { const newConf = { ...oldConf } @@ -74,88 +80,11 @@ export const getAllChannels = (confTmp, setConf, setIsLoading) => { }) setIsLoading(false) - toast.success(__('Channels fetched successfully', 'bit-integrations')) return } + setIsLoading(false) toast.error(__('Channels fetching failed', 'bit-integrations')) }) } - -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.accessToken) { - setError({ - accessToken: !confTmp.accessToken ? __("Access Token can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setIsLoading(true) - - const tokenRequestParams = { accessToken: confTmp.accessToken } - - bitsFetch(tokenRequestParams, 'handle_authorize') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} -// export const handleAuthorize = (confTmp, setConf, setError, setisAuthorized, setIsLoading, setSnackbar) => { -// if (!confTmp.accessToken) { -// setError({ accessToken: !confTmp.accessToken ? __('Access Token can\'t be empty', 'bit-integrations') : '' }) -// return -// } - -// setError({}) -// setIsLoading(true) - -// const tokenRequestParams = { accessToken: confTmp.accessToken } - -// bitsFetch(tokenRequestParams, 'discord_authorization_and_fetch_servers') -// .then(result => result) -// .then(result => { -// if (result && result.success) { -// const newConf = { ...confTmp } -// newConf.tokenDetails = result.data -// setConf(newConf) -// setisAuthorized(true) -// setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) -// } else if ((result && result.data && result.data.data) || (!result.success && typeof result.data === 'string')) { -// setSnackbar({ show: true, msg: `${__('Authorization failed Cause:', 'bit-integrations')}${result.data.data || result.data}. ${__('please try again', 'bit-integrations')}` }) -// } else { -// setSnackbar({ show: true, msg: __('Authorization failed. please try again', 'bit-integrations') }) -// } -// setIsLoading(false) -// }) -// } diff --git a/frontend/src/components/AllIntegrations/Dokan/DokanAuthorization.jsx b/frontend/src/components/AllIntegrations/Dokan/DokanAuthorization.jsx index cc4c7f2b4..100d305fc 100644 --- a/frontend/src/components/AllIntegrations/Dokan/DokanAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Dokan/DokanAuthorization.jsx @@ -1,86 +1,29 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { dokanAuthentication } from './dokanCommonFunctions' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function DokanAuthorization({ - dokanConf, - setDokanConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !dokanConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...dokanConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setDokanConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function DokanAuthorization({ dokanConf, setDokanConf, step, setStep, isInfo }) { return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - {error.name &&
    {error.name}
    } -
    - - {!isInfo && ( -
    - -
    - -
    - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Dokan/dokanCommonFunctions.js b/frontend/src/components/AllIntegrations/Dokan/dokanCommonFunctions.js index 9bcdc5a5d..9ccd76b4e 100644 --- a/frontend/src/components/AllIntegrations/Dokan/dokanCommonFunctions.js +++ b/frontend/src/components/AllIntegrations/Dokan/dokanCommonFunctions.js @@ -34,25 +34,6 @@ export const checkMappedFields = dokanConf => { return true } -export const dokanAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.name) { - setError({ name: !confTmp.name ? __("Name can't be empty", 'bit-integrations') : '' }) - return - } - - setLoading({ ...loading, auth: true }) - bitsFetch({}, 'dokan_authentication').then(result => { - if (result.success) { - setIsAuthorized(true) - toast.success(__('Connected Successfully', 'bit-integrations')) - setLoading({ ...loading, auth: false }) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Connection failed: install and active Dokan plugin first!', 'bit-integrations')) - }) -} - export const getAllVendors = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, vendors: true }) diff --git a/frontend/src/components/AllIntegrations/Drip/DripAuthorization.jsx b/frontend/src/components/AllIntegrations/Drip/DripAuthorization.jsx index 5ae2a4383..8e607cebe 100644 --- a/frontend/src/components/AllIntegrations/Drip/DripAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Drip/DripAuthorization.jsx @@ -1,134 +1,64 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +/* eslint-disable jsx-a11y/anchor-is-valid */ +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { dripAuthentication } from './DripCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' -import toast from 'react-hot-toast' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { fetchDripAccounts } from './DripCommonFunc' -export default function DripAuthorization({ - formID, - dripConf, - setDripConf, - step, - setstep, - isInfo, - loading, - setLoading -}) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_token: '' }) - - const handleInput = e => { - const newConf = { ...dripConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setDripConf(newConf) - } - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) +export default function DripAuthorization({ dripConf, setDripConf, step, setstep, isInfo, setLoading }) { + const loadAccounts = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...dripConf, connection_id: connectionId } : dripConf + await fetchDripAccounts(nextConf, setDripConf, setLoading, 'fetch') + }, + [dripConf, setDripConf, setLoading] + ) - setstep(2) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !(dripConf?.accounts || []).length) { + loadAccounts() + } + setstep(value) + }, + [dripConf, loadAccounts, setstep] + ) - const ActiveInstructions = ` -

    ${__('Get Drip Api Token', 'bit-integrations')}

    - ` + const note = ` +

    ${__('Get Drip Api Token', 'bit-integrations')}

    +
      +
    • ${__( + 'First go to your', + 'bit-integrations' + )} ${__( + 'Drip user settings', + 'bit-integrations' + )}.
    • +
    • ${__('Copy the API Token from "User Info".', 'bit-integrations')}
    • +
    • ${__( + 'Use that token as Username in the authorization form and keep Password empty.', + 'bit-integrations' + )}
    • +
    ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - -
    {error.name}
    - -
    - {__('Drip Api Token:', 'bit-integrations')} -
    - -
    {error.api_token}
    - - - {__('To Get Drip Api Token, Please Visit', 'bit-integrations')} -   - - {__('Drip User Settings', 'bit-integrations')} - - -
    -
    - {loading?.auth && ( -
    - - Checking Api Token Key!!! -
    - )} - - {!isInfo && ( - <> - -
    - - - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Drip/DripCommonFunc.js b/frontend/src/components/AllIntegrations/Drip/DripCommonFunc.js index 1e67babf4..f7db81166 100644 --- a/frontend/src/components/AllIntegrations/Drip/DripCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Drip/DripCommonFunc.js @@ -9,55 +9,33 @@ export const handleInput = (e, dripConf, setDripConf) => { setDripConf({ ...newConf }) } -export const dripAuthentication = ( - dripConf, - setDripConf, - setError, - setisAuthorized, - loading, - setLoading, - type = 'authentication' -) => { - const newConf = { ...dripConf } +const buildAuthRequestParams = confTmp => + confTmp.connection_id ? { connection_id: confTmp.connection_id } : { api_token: confTmp.api_token } - if (!newConf.name || !newConf.api_token) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - api_token: !newConf.api_token ? __("Access Api Token Key can't be empty", 'bit-integrations') : '' - }) +export const fetchDripAccounts = async (confTmp, setConf, setLoading, type = 'fetch') => { + if (!confTmp.connection_id && !confTmp.api_token) { + toast.error(__("Access Api Token can't be empty", 'bit-integrations')) return } - let responseErrorMsg + setLoading(prev => ({ ...prev, accounts: true })) - if (type === 'authentication') { - setLoading({ ...loading, auth: true }) - responseErrorMsg = 'Authorization Failed' - } else if (type === 'accounts') { - setLoading({ ...loading, accounts: true }) - responseErrorMsg = 'Accounts fetching failed' - } + const result = await bitsFetch(buildAuthRequestParams(confTmp), 'drip_fetch_all_accounts') - const data = { - api_token: newConf.api_token + if (result?.success) { + const newConf = { ...confTmp, accounts: result.data || [] } + setConf(newConf) + setLoading(prev => ({ ...prev, accounts: false })) + toast.success( + type === 'refresh' + ? __('Accounts fetched Successfully', 'bit-integrations') + : __('Authorized Successfully', 'bit-integrations') + ) + return } - bitsFetch(data, 'drip_authorize').then(result => { - if (result?.success) { - newConf.accounts = result.data - if (type === 'authentication') { - setisAuthorized(true) - toast.success('Authorized Successfully') - } else if (type === 'accounts') { - toast.success('Accounts fetched Successfully') - } - } else { - toast.error(responseErrorMsg) - } - - setDripConf({ ...newConf }) - setLoading({ ...loading, auth: false, accounts: false }) - }) + setLoading(prev => ({ ...prev, accounts: false })) + toast.error(__('Accounts fetching failed', 'bit-integrations')) } export const checkMappedFields = dripConf => { @@ -83,10 +61,10 @@ export const generateMappedField = dripConf => { } export const getCustomFields = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, customFields: true }) + setLoading(prev => ({ ...prev, customFields: true })) const requestParams = { - apiToken: confTmp.api_token, + ...buildAuthRequestParams(confTmp), selectedAccountId: confTmp.selectedAccountId } @@ -97,19 +75,20 @@ export const getCustomFields = (confTmp, setConf, setLoading) => { newConf.dripFormFields = [...staticFields, ...result.data] } setConf(newConf) - setLoading({ ...setLoading, customFields: false }) + setLoading(prev => ({ ...prev, customFields: false })) toast.success(__('Custom fields fetch successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, customFields: false }) + setLoading(prev => ({ ...prev, customFields: false })) toast.error(__('Custom fields fetch failed', 'bit-integrations')) }) } + export const getAllTags = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, tags: true }) + setLoading(prev => ({ ...prev, tags: true })) const requestParams = { - apiToken: confTmp.api_token, + ...buildAuthRequestParams(confTmp), selectedAccountId: confTmp.selectedAccountId } @@ -120,11 +99,11 @@ export const getAllTags = (confTmp, setConf, setLoading) => { newConf.tags = result.data } setConf(newConf) - setLoading({ ...setLoading, tags: false }) + setLoading(prev => ({ ...prev, tags: false })) toast.success(__('Tags fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, customFields: false }) + setLoading(prev => ({ ...prev, tags: false })) toast.error(__('Tags fetching failed', 'bit-integrations')) }) } diff --git a/frontend/src/components/AllIntegrations/Drip/DripIntegLayout.jsx b/frontend/src/components/AllIntegrations/Drip/DripIntegLayout.jsx index b83aa5a09..5d3737125 100644 --- a/frontend/src/components/AllIntegrations/Drip/DripIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/Drip/DripIntegLayout.jsx @@ -1,15 +1,14 @@ // eslint-disable-next-line import/no-extraneous-dependencies +import { useState } from 'react' import { __ } from '../../../Utils/i18nwrap' import Loader from '../../Loaders/Loader' import { addFieldMap } from '../IntegrationHelpers/IntegrationHelpers' -import { dripAuthentication, getCustomFields, staticFields } from './DripCommonFunc' -import DripFieldMap from './DripFieldMap' -import { useState } from 'react' import DripActions from './DripActions' +import { fetchDripAccounts, getCustomFields, staticFields } from './DripCommonFunc' +import DripFieldMap from './DripFieldMap' export default function DripIntegLayout({ formFields, dripConf, setDripConf, loading, setLoading }) { const [error, setError] = useState({ name: '', api_token: '' }) - const [isAuthorized, setisAuthorized] = useState(false) const handleInput = e => { const accountId = e.target.value @@ -44,17 +43,7 @@ export default function DripIntegLayout({ formFields, dripConf, setDripConf, loa ))} -
    - - - )} - + ) } diff --git a/frontend/src/components/AllIntegrations/Dropbox/DropboxCommonFunc.js b/frontend/src/components/AllIntegrations/Dropbox/DropboxCommonFunc.js index c3a6a9117..bbb85e66b 100644 --- a/frontend/src/components/AllIntegrations/Dropbox/DropboxCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Dropbox/DropboxCommonFunc.js @@ -14,12 +14,19 @@ export const handleInput = (e, dropboxConf, setDropboxConf) => { setDropboxConf({ ...newConf }) } +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const getAllDropboxFolders = (flowID, dropboxConf, setDropboxConf) => { const queryParams = { flowID: flowID ?? null, - clientId: dropboxConf.clientId, - clientSecret: dropboxConf.clientSecret, - tokenDetails: dropboxConf.tokenDetails + ...buildAuthRequestParams(dropboxConf) } const loadPostTypes = bitsFetch(queryParams, 'dropbox_get_all_folders').then(result => { if (result && result.success) { @@ -41,45 +48,3 @@ export const getAllDropboxFolders = (flowID, dropboxConf, setDropboxConf) => { loading: __('Loading Dropbox Folders List...', 'bit-integrations') }) } - -export const handleAuthorize = (confTmp, setConf, setIsAuthorized, setIsLoading, setError) => { - if (!confTmp.accessCode || !confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Client Secret can't be empty", 'bit-integrations') : '', - accessCode: !confTmp.accessCode ? __("Access Code can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - - const tokenRequestParams = { - clientId: confTmp.clientId, - clientSecret: confTmp.clientSecret, - accessCode: confTmp.accessCode - } - - bitsFetch(tokenRequestParams, 'dropbox_authorization') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setIsAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - toast.error( - `${__('Authorization failed Cause:', 'bit-integrations')} ${ - result.data.data || result.data - } ${__('please try again', 'bit-integrations')}` - ) - } else { - toast.error(__('Authorization failed. please try again', 'bit-integrations')) - } - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/ElasticEmail/ElasticEmailAuthorization.jsx b/frontend/src/components/AllIntegrations/ElasticEmail/ElasticEmailAuthorization.jsx index cd1eabc1f..13e58f875 100644 --- a/frontend/src/components/AllIntegrations/ElasticEmail/ElasticEmailAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ElasticEmail/ElasticEmailAuthorization.jsx @@ -1,12 +1,10 @@ -/* eslint-disable no-unused-expressions */ -/* eslint-disable no-undef */ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' +import Authorization from '../../Connections/Authorization' import { getAllList } from './ElasticEmailCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' + export default function ElasticEmailAuthorization({ elasticEmailConf, setElasticEmailConf, @@ -14,124 +12,57 @@ export default function ElasticEmailAuthorization({ setstep, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_key: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const handleAuthorize = () => { - const newConf = { ...elasticEmailConf } - if (!newConf.name || !newConf.api_key) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - api_key: !newConf.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading('auth') - const data = { api_key: newConf.api_key } - bitsFetch(data, 'elasticemail_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...elasticEmailConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setElasticEmailConf(newConf) - } + const loadLists = useCallback( + connectionId => { + const nextConf = connectionId + ? { ...elasticEmailConf, connection_id: connectionId } + : elasticEmailConf - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - !elasticEmailConf?.default && getAllList(elasticEmailConf, setElasticEmailConf, setIsLoading) - setstep(2) - } + getAllList(nextConf, setElasticEmailConf, () => {}) + }, + [elasticEmailConf, setElasticEmailConf] + ) - return ( -
    - + const handleSetStep = useCallback( + value => { + if (value === 2 && !elasticEmailConf?.default?.lists) { + loadLists() + } + setstep(value) + }, + [elasticEmailConf, loadLists, setstep] + ) -
    - {__('Integration Name:', 'bit-integrations')} -
    - -
    {error.name}
    -
    - {__('API Key:', 'bit-integrations')} -
    - -
    {error.api_key}
    - - {__('To get API , Please Visit', 'bit-integrations')}{' '} + const note = ` + + ${__('To get API, please visit', 'bit-integrations')} - {__('Elastic Email API Console', 'bit-integrations')} + ${__(' Elastic Email API Console', 'bit-integrations')} - - {isLoading === 'auth' && ( -
    - - {__('Checking API Key!!!', 'bit-integrations')} -
    - )} +
    ` - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {__('Sorry, Api key is invalid', 'bit-integrations')} -
    - )} - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/ElasticEmail/ElasticEmailCommonFunc.jsx b/frontend/src/components/AllIntegrations/ElasticEmail/ElasticEmailCommonFunc.jsx index 55cdd95b6..c3273e5fa 100644 --- a/frontend/src/components/AllIntegrations/ElasticEmail/ElasticEmailCommonFunc.jsx +++ b/frontend/src/components/AllIntegrations/ElasticEmail/ElasticEmailCommonFunc.jsx @@ -34,9 +34,13 @@ export const generateMappedField = elasticEmailConf => { ? requiredFlds.map(field => ({ formField: '', elasticEmailField: field.key })) : [{ formField: '', elasticEmailField: '' }] } + +const buildAuthRequestParams = conf => + conf.connection_id ? { connection_id: conf.connection_id } : { apiKey: conf.api_key } + export const getAllList = (elasticEmailConf, setElasticEmailConf, setIsLoading) => { - setIsLoading(true) - const queryParams = { apiKey: elasticEmailConf.api_key } + if (setIsLoading) setIsLoading(true) + const queryParams = buildAuthRequestParams(elasticEmailConf) const loadPostTypes = bitsFetch(null, 'get_all_lists', queryParams, 'GET').then(result => { if (result && result.success) { const newConf = { ...elasticEmailConf } @@ -45,10 +49,10 @@ export const getAllList = (elasticEmailConf, setElasticEmailConf, setIsLoading) newConf.default.lists = result.data.lists } setElasticEmailConf({ ...newConf }) - setIsLoading(false) + if (setIsLoading) setIsLoading(false) return __('List refreshed successfully', 'bit-integrations') } - setIsLoading(false) + if (setIsLoading) setIsLoading(false) return __('List refresh failed. please try again', 'bit-integrations') }) toast.promise(loadPostTypes, { diff --git a/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopus.jsx b/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopus.jsx index ddc223151..96bd722f7 100644 --- a/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopus.jsx +++ b/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopus.jsx @@ -89,7 +89,6 @@ function EmailOctopus({ formFields, setFlow, flow, allIntegURL }) { setStep={setStep} loading={loading} setLoading={setLoading} - setSnackbar={setSnackbar} /> {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusActions.jsx b/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusActions.jsx index d4eb7f54f..3ae459abf 100644 --- a/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusActions.jsx +++ b/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusActions.jsx @@ -20,7 +20,7 @@ export default function EmailOctopusActions({ const newConf = { ...emailOctopusConf } if (type === 'tag') { if (e.target?.checked) { - getAllTags(emailOctopusConf, setEmailOctopusConf, setLoading) + getAllTags(emailOctopusConf, setEmailOctopusConf, loading, setLoading) newConf.actions.tags = true } else { setActionMdl({ show: false }) @@ -111,7 +111,7 @@ export default function EmailOctopusActions({ onChange={val => setChanges(val)} /> -
    - - - )} - + ) } diff --git a/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusCommonFunc.js b/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusCommonFunc.js index 9aaabce9d..606f705e1 100644 --- a/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusCommonFunc.js +++ b/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusCommonFunc.js @@ -37,60 +37,43 @@ export const checkMappedFields = emailOctopusConf => { return true } -export const emailOctopusAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading, - type -) => { - if (!confTmp.auth_token) { - setError({ - auth_token: !confTmp.auth_token ? __("Api Key can't be empty", 'bit-integrations') : '' - }) +export const fetchAllLists = (confTmp, setConf, loading, setLoading, type = 'fetch') => { + if (!confTmp.connection_id && !confTmp.auth_token) { + toast.error(__("Api Key can't be empty", 'bit-integrations')) return } - setError({}) + setLoading({ ...loading, lists: true }) - if (type === 'authentication') { - setLoading({ ...loading, auth: true }) - } - if (type === 'refreshLists') { - setLoading({ ...loading, lists: true }) - } - const requestParams = { auth_token: confTmp.auth_token } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { auth_token: confTmp.auth_token } - bitsFetch(requestParams, 'emailOctopus_authentication').then(result => { + bitsFetch(requestParams, 'emailOctopus_fetch_all_lists').then(result => { if (result && result.success) { const newConf = { ...confTmp } - setIsAuthorized(true) - if (type === 'authentication') { - if (result.data) { - newConf.lists = result.data - } - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if (type === 'refreshLists') { - if (result.data) { - newConf.lists = result.data - } - setLoading({ ...loading, lists: false }) - toast.success(__('All lists fectched successfully', 'bit-integrations')) + if (result.data) { + newConf.lists = result.data } setConf(newConf) + setLoading({ ...loading, lists: false }) + toast.success( + type === 'refresh' + ? __('All lists fetched successfully', 'bit-integrations') + : __('Lists fetched successfully', 'bit-integrations') + ) return } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid domain name & API key', 'bit-integrations')) + setLoading({ ...loading, lists: false }) + toast.error(__('Lists fetching failed', 'bit-integrations')) }) } -export const getAllFields = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, customFields: true }) - const requestParams = { auth_token: confTmp.auth_token, listId: confTmp.selectedList } +export const getAllFields = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, customFields: true }) + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id, listId: confTmp.selectedList } + : { auth_token: confTmp.auth_token, listId: confTmp.selectedList } bitsFetch(requestParams, 'emailOctopus_fetch_all_fields').then(result => { if (result && result.success) { @@ -99,21 +82,22 @@ export const getAllFields = (confTmp, setConf, setLoading) => { newConf.emailOctopusFields = result.data } setConf(newConf) - setLoading({ ...setLoading, customFields: false }) - setLoading({ ...setLoading, emailOctopusFields: true }) + setLoading({ ...loading, customFields: false, emailOctopusFields: true }) toast.success(__('Fields fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, customFields: false }) + setLoading({ ...loading, customFields: false }) toast.error(__('Fields fetching failed', 'bit-integrations')) }) } -export const getAllTags = (confTmp, setConf, setLoading) => { - setLoading({ tags: true, emailOctopusFields: true }) +export const getAllTags = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, tags: true, emailOctopusFields: true }) - const requestParams = { auth_token: confTmp.auth_token, listId: confTmp.selectedList } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id, listId: confTmp.selectedList } + : { auth_token: confTmp.auth_token, listId: confTmp.selectedList } bitsFetch(requestParams, 'emailOctopus_fetch_all_tags').then(result => { if (result && result.success) { @@ -122,12 +106,12 @@ export const getAllTags = (confTmp, setConf, setLoading) => { newConf.tags = result.data } setConf(newConf) - setLoading({ tags: false, emailOctopusFields: true }) + setLoading({ ...loading, tags: false, emailOctopusFields: true }) toast.success(__('Tags fetched successfully', 'bit-integrations')) return } - setLoading({ tags: false, emailOctopusFields: true }) + setLoading({ ...loading, tags: false, emailOctopusFields: true }) toast.error(__('Tags fetching failed', 'bit-integrations')) }) } diff --git a/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusIntegLayout.jsx b/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusIntegLayout.jsx index 581500437..ca9f9afce 100644 --- a/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/EmailOctopus/EmailOctopusIntegLayout.jsx @@ -1,12 +1,10 @@ /* eslint-disable no-console */ -/* eslint-disable no-unused-vars */ -import { useState } from 'react' import MultiSelect from 'react-multiple-select-dropdown-lite' import { __ } from '../../../Utils/i18nwrap' import Loader from '../../Loaders/Loader' import 'react-multiple-select-dropdown-lite/dist/index.css' import EmailOctopusActions from './EmailOctopusActions' -import { emailOctopusAuthentication, getAllFields } from './EmailOctopusCommonFunc' +import { fetchAllLists, getAllFields } from './EmailOctopusCommonFunc' import EmailOctopusFieldMap from './EmailOctopusFieldMap' import { addFieldMap } from './IntegrationHelpers' @@ -19,14 +17,11 @@ export default function EmailOctopusIntegLayout({ setLoading, setSnackbar }) { - const [error, setError] = useState({ name: '', auth_token: '' }) - const [isAuthorized, setIsAuthorized] = useState(false) - const setChanges = val => { const newConf = { ...emailOctopusConf } newConf.selectedList = val setEmailOctopusConf({ ...newConf }) - getAllFields(newConf, setEmailOctopusConf, setLoading) + getAllFields(newConf, setEmailOctopusConf, loading, setLoading) } return ( @@ -43,15 +38,7 @@ export default function EmailOctopusIntegLayout({ /> -
    - - - )} - + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/Encharge/EnchargeCommonFunc.js b/frontend/src/components/AllIntegrations/Encharge/EnchargeCommonFunc.js index 303e39fa7..d8d135105 100644 --- a/frontend/src/components/AllIntegrations/Encharge/EnchargeCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Encharge/EnchargeCommonFunc.js @@ -8,8 +8,11 @@ export const handleInput = (e, enchargeConf, setEnchargeConf) => { setEnchargeConf({ ...newConf }) } +const buildAuthRequestParams = conf => + conf.connection_id ? { connection_id: conf.connection_id } : { api_key: conf.api_key } + export const refreshEnchargeHeader = (enchargeConf, setEnchargeConf, setIsLoading, setSnackbar) => { - const refreshEnchargeHeaderData = { api_key: enchargeConf.api_key } + const refreshEnchargeHeaderData = buildAuthRequestParams(enchargeConf) const newConf = { ...enchargeConf } bitsFetch(refreshEnchargeHeaderData, 'encharge_headers') .then(result => { diff --git a/frontend/src/components/AllIntegrations/Fabman/Fabman.jsx b/frontend/src/components/AllIntegrations/Fabman/Fabman.jsx index 7644e8587..d20fff8eb 100644 --- a/frontend/src/components/AllIntegrations/Fabman/Fabman.jsx +++ b/frontend/src/components/AllIntegrations/Fabman/Fabman.jsx @@ -151,6 +151,7 @@ export default function Fabman({ formFields, setFlow, flow, allIntegURL }) { const [fabmanConf, setFabmanConf] = useState({ name: 'Fabman', type: 'Fabman', + app_slug: 'fabman', field_map: [{ formField: '', fabmanFormField: '' }], customFields: [], actions: {}, diff --git a/frontend/src/components/AllIntegrations/Fabman/FabmanAuthorization.jsx b/frontend/src/components/AllIntegrations/Fabman/FabmanAuthorization.jsx index 366400616..6baedef38 100644 --- a/frontend/src/components/AllIntegrations/Fabman/FabmanAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Fabman/FabmanAuthorization.jsx @@ -1,11 +1,14 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ /* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback, useState } from 'react' import { __, sprintf } from '../../../Utils/i18nwrap' import LoaderSm from '../../Loaders/LoaderSm' -import { fabmanAuthentication, fetchFabmanWorkspaces } from './FabmanCommonFunc' +import { fabmanAuthentication, fetchFabmanAccountId, fetchFabmanWorkspaces } from './FabmanCommonFunc' import Note from '../../Utilities/Note' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' import TutorialLink from '../../Utilities/TutorialLink' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import Authorization from '../../Connections/Authorization' const STEP_ONE_STYLE = { width: 900, height: 'auto' } @@ -21,6 +24,13 @@ export default function FabmanAuthorization({ const [isAuthorized, setIsAuthorized] = useState(false) const [error, setError] = useState({ name: '', apiKey: '' }) + const handleConnectionSelected = useCallback( + async connectionId => { + await fetchFabmanAccountId(connectionId, setFabmanConf) + }, + [setFabmanConf] + ) + const nextPage = () => { fetchFabmanWorkspaces(fabmanConf, setFabmanConf, loading, setLoading, 'fetch') setTimeout(() => { @@ -39,68 +49,24 @@ export default function FabmanAuthorization({ } return ( -
    - -
    - {__('Integration Name:', 'bit-integrations')} -
    - -
    - {__('API Key:', 'bit-integrations')} -
    - -
    - {error.apiKey} -
    - - {!isInfo && ( -
    - - - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Fabman/FabmanCommonFunc.js b/frontend/src/components/AllIntegrations/Fabman/FabmanCommonFunc.js index cac6f45c2..297e5a8f6 100644 --- a/frontend/src/components/AllIntegrations/Fabman/FabmanCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Fabman/FabmanCommonFunc.js @@ -5,6 +5,13 @@ import bitsFetch from '../../../Utils/bitsFetch' import { __ } from '../../../Utils/i18nwrap' import { create } from 'mutative' +export const fetchFabmanAccountId = async (connectionId, setConf) => { + const result = await bitsFetch({ connection_id: connectionId }, 'fabman_fetch_account_id') + if (result?.success && result.data?.accountId) { + setConf(prev => ({ ...prev, accountId: result.data.accountId })) + } +} + export const handleInput = (e, fabmanConf, setFabmanConf) => { const newConf = { ...fabmanConf } const { name } = e.target @@ -86,14 +93,16 @@ export const fabmanAuthentication = ( } export const fetchFabmanWorkspaces = (confTmp, setConf, loading, setLoading, type = 'fetch') => { - if (!confTmp.apiKey) { + if (!confTmp.connection_id && !confTmp.apiKey) { toast.error(__("API key can't be empty", 'bit-integrations')) return } setLoading({ ...loading, workspaces: true }) - const requestParams = { apiKey: confTmp.apiKey } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { apiKey: confTmp.apiKey } bitsFetch(requestParams, 'fabman_fetch_workspaces') .then(result => { diff --git a/frontend/src/components/AllIntegrations/Flowlu/FlowluAuthorization.jsx b/frontend/src/components/AllIntegrations/Flowlu/FlowluAuthorization.jsx index 8e2c0fe9e..0ab68713d 100644 --- a/frontend/src/components/AllIntegrations/Flowlu/FlowluAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Flowlu/FlowluAuthorization.jsx @@ -1,157 +1,44 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' -import { toast } from 'react-hot-toast' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { flowluAuthentication } from './FlowluCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function FlowluAuthorization({ - flowluConf, - setFlowluConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '', company_name: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !flowluConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...flowluConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setFlowluConf(newConf) - } - - const handleSessionTokenLink = () => { - flowluConf.company_name - ? window.open( - `https://${flowluConf.company_name}.flowlu.com/cabinet/all_settings?section=api`, - '_blank', - 'noreferrer' - ) - : toast.error(__('Company Name is required!', 'bit-integrations')) - } - - const ActiveInstructions = ` -

    ${__('Get the API Key', 'bit-integrations')}

    -
      -
    • ${__('First go to your Flowlu dashboard.', 'bit-integrations')}
    • -
    • ${__('Click go to your "Profile" from Right top corner', 'bit-integrations')}
    • -
    • ${__('Then Click "Portal Settings"', 'bit-integrations')}
    • -
    • ${__('Click go to "API Settings" from "Main Settings"', 'bit-integrations')}
    • -
    • ${__('Then click "create", Then Copy', 'bit-integrations')}
    • -
    ` +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function FlowluAuthorization({ flowluConf, setFlowluConf, step, setStep, isInfo }) { + const note = ` +

    ${__('Get the API Key', 'bit-integrations')}

    +
      +
    • ${__('First go to your Flowlu dashboard.', 'bit-integrations')}
    • +
    • ${__('Open Profile from the top-right corner.', 'bit-integrations')}
    • +
    • ${__('Open Portal Settings, then API Settings.', 'bit-integrations')}
    • +
    • ${__('Create and copy your API key.', 'bit-integrations')}
    • +
    • ${__('Use your workspace subdomain as Company Name.', 'bit-integrations')}
    • +
    ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('API Key:', 'bit-integrations')} -
    - -
    {error.api_key}
    - -
    - {__('Company Name:', 'bit-integrations')} -
    - -
    -
    https://
    -
    - -
    -
    .flowlu.com
    -
    -
    {error.company_name}
    - - - {__('To get API key, please visit', 'bit-integrations')} -   - - {__('Flowlu API Key', 'bit-integrations')} - - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Flowlu/FlowluCommonFunc.js b/frontend/src/components/AllIntegrations/Flowlu/FlowluCommonFunc.js index fa7748dc6..35a9f6364 100644 --- a/frontend/src/components/AllIntegrations/Flowlu/FlowluCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Flowlu/FlowluCommonFunc.js @@ -16,11 +16,18 @@ export const handleInput = (e, flowluConf, setFlowluConf) => { setFlowluConf({ ...newConf }) } +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + api_key: confTmp.api_key, + company_name: confTmp.company_name + } + export const getAllFields = (flowluConf, setFlowluConf, setIsLoading, setSnackbar) => { setIsLoading(true) const requestParams = { - api_key: flowluConf.api_key, - company_name: flowluConf.company_name, + ...buildAuthRequestParams(flowluConf), action_name: flowluConf.actionName, selectedAccountType: flowluConf?.selectedAccountType } @@ -79,49 +86,10 @@ export const checkMappedFields = flowluConf => { return true } -export const flowluAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key || !confTmp.company_name) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '', - company_name: !confTmp.company_name ? __("Company Name can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - api_key: confTmp.api_key, - company_name: confTmp.company_name - } - - bitsFetch(requestParams, 'flowlu_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid API Key or Company Name', 'bit-integrations')) - }) -} - export const getAllAccountCategories = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, accountCategories: true }) - const requestParams = { - api_key: confTmp.api_key, - company_name: confTmp.company_name - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'flowlu_fetch_all_account_categories').then(result => { if (result && result.success) { @@ -143,10 +111,7 @@ export const getAllAccountCategories = (confTmp, setConf, setLoading) => { export const getAllIndustry = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, industry: true }) - const requestParams = { - api_key: confTmp.api_key, - company_name: confTmp.company_name - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'flowlu_fetch_all_industries').then(result => { if (result && result.success) { @@ -168,10 +133,7 @@ export const getAllIndustry = (confTmp, setConf, setLoading) => { export const getAllPipeline = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, pipeline: true }) - const requestParams = { - api_key: confTmp.api_key, - company_name: confTmp.company_name - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'flowlu_fetch_all_pipelines').then(result => { if (result && result.success) { @@ -194,8 +156,7 @@ export const getAllStage = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, stage: true }) const requestParams = { - api_key: confTmp.api_key, - company_name: confTmp.company_name, + ...buildAuthRequestParams(confTmp), pipeline_id: confTmp.selectedPipeline } @@ -219,10 +180,7 @@ export const getAllStage = (confTmp, setConf, setLoading) => { export const getAllSource = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, source: true }) - const requestParams = { - api_key: confTmp.api_key, - company_name: confTmp.company_name - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'flowlu_fetch_all_sources').then(result => { if (result && result.success) { @@ -244,10 +202,7 @@ export const getAllSource = (confTmp, setConf, setLoading) => { export const getAllCustomer = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, customer: true }) - const requestParams = { - api_key: confTmp.api_key, - company_name: confTmp.company_name - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'flowlu_fetch_all_customers').then(result => { if (result && result.success) { @@ -269,10 +224,7 @@ export const getAllCustomer = (confTmp, setConf, setLoading) => { export const getAllManagers = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, manager: true }) - const requestParams = { - api_key: confTmp.api_key, - company_name: confTmp.company_name - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'flowlu_fetch_all_managers').then(result => { if (result && result.success) { @@ -294,10 +246,7 @@ export const getAllManagers = (confTmp, setConf, setLoading) => { export const getAllProjectStage = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, projectStage: true }) - const requestParams = { - api_key: confTmp.api_key, - company_name: confTmp.company_name - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'flowlu_fetch_all_project_tages').then(result => { if (result && result.success) { @@ -319,10 +268,7 @@ export const getAllProjectStage = (confTmp, setConf, setLoading) => { export const getAllPortfolio = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, portfolio: true }) - const requestParams = { - api_key: confTmp.api_key, - company_name: confTmp.company_name - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'flowlu_fetch_all_portfolio').then(result => { if (result && result.success) { @@ -344,10 +290,7 @@ export const getAllPortfolio = (confTmp, setConf, setLoading) => { export const getAllProjectOpportunity = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, projectOpportunity: true }) - const requestParams = { - api_key: confTmp.api_key, - company_name: confTmp.company_name - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'flowlu_fetch_all_project_opportunity').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/FluentCRM/FluentCrmAuthorization.jsx b/frontend/src/components/AllIntegrations/FluentCRM/FluentCrmAuthorization.jsx index 7405c0516..2f78e568f 100644 --- a/frontend/src/components/AllIntegrations/FluentCRM/FluentCrmAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/FluentCRM/FluentCrmAuthorization.jsx @@ -1,108 +1,39 @@ -/* eslint-disable react/jsx-no-useless-fragment */ -import { useEffect, useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import bitsFetch from '../../../Utils/bitsFetch' -import LoaderSm from '../../Loaders/LoaderSm' -import BackIcn from '../../../Icons/BackIcn' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function FluentCrmAuthorization({ - formID, fluentCrmConf, setFluentCrmConf, step, nextPage, - setSnackbar, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ integrationName: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const [isMounted, setIsMounted] = useState(true) - useEffect( - () => () => { - setIsMounted(false) - }, - [] - ) - - const handleAuthorize = () => { - setIsLoading('auth') - bitsFetch({}, 'fluent_crm_authorize').then(result => { - if (isMounted) { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Connected Successfully', 'bit-integrations') }) - } - setShowAuthMsg(true) - setIsLoading(false) - } - }) - } - const handleInput = e => { - const newConf = { ...fluentCrmConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setFluentCrmConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( - <> -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - {isLoading === 'auth' && ( -
    - - {__('Checking if Fluent CRM is active!!!', 'bit-integrations')} -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {__('Please! First Install Fluent CRM Plugins', 'bit-integrations')} -
    - )} - -
    - -
    - + ) } diff --git a/frontend/src/components/AllIntegrations/FluentCart/FluentCartAuthorization.jsx b/frontend/src/components/AllIntegrations/FluentCart/FluentCartAuthorization.jsx index 9f40bc794..34ca18160 100644 --- a/frontend/src/components/AllIntegrations/FluentCart/FluentCartAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/FluentCart/FluentCartAuthorization.jsx @@ -1,112 +1,36 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function FluentCartAuthorization({ - formID, fluentCartConf, setFluentCartConf, step, nextPage, - isLoading, - setIsLoading, - setSnackbar + isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'fluent_cart_authorize').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with FluentCart Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = { ...fluentCartConf } - newConf[e.target.name] = e.target.value - setFluentCartConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - {isLoading === 'auth' && ( -
    - - {__('Checking if FluentCart is authorized!!!', 'bit-integrations')} -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    -
    -
    - -
    -
    - {__('FluentCart is not activated or not installed', 'bit-integrations')} -
    -
    -
    - )} - - {showAuthMsg && isAuthorized && !isLoading && ( -
    -
    - -
    -
    {__('FluentCart is activated', 'bit-integrations')}
    -
    - )} - - -
    - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/FluentSupport/FluentSupportAuthorization.jsx b/frontend/src/components/AllIntegrations/FluentSupport/FluentSupportAuthorization.jsx index 21b825bb0..5ae042a66 100644 --- a/frontend/src/components/AllIntegrations/FluentSupport/FluentSupportAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/FluentSupport/FluentSupportAuthorization.jsx @@ -1,94 +1,55 @@ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { getCustomFields, handleAuthorize } from './FluentSupportCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import { getCustomFields } from './FluentSupportCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function FluentSupportAuthorization({ - formID, fluentSupportConf, setFluentSupportConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - + const loadCustomFields = useCallback(() => { getCustomFields(fluentSupportConf, setFluentSupportConf, setIsLoading, setSnackbar) - setstep(2) - } - const handleInput = e => { - const newConf = { ...fluentSupportConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setFluentSupportConf(newConf) - } - - return ( -
    - + }, [fluentSupportConf, setFluentSupportConf, setIsLoading, setSnackbar]) -
    - {__('Integration Name:', 'bit-integrations')} -
    - + const handleSetStep = useCallback( + value => { + if (value === 2 && !fluentSupportConf?.fluentSupportFields?.length) { + loadCustomFields() + } + setstep(value) + }, + [fluentSupportConf?.fluentSupportFields?.length, loadCustomFields, setstep] + ) - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/FluentSupport/FluentSupportCommonFunc.js b/frontend/src/components/AllIntegrations/FluentSupport/FluentSupportCommonFunc.js index 44bf042fd..b925cac1d 100644 --- a/frontend/src/components/AllIntegrations/FluentSupport/FluentSupportCommonFunc.js +++ b/frontend/src/components/AllIntegrations/FluentSupport/FluentSupportCommonFunc.js @@ -1,6 +1,5 @@ import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { sprintf, __ } from '../../../Utils/i18nwrap' +import { __ } from '../../../Utils/i18nwrap' import { create } from 'mutative' export const handleInput = ( @@ -160,45 +159,3 @@ export const checkMappedFields = fluentSupportConf => { } return true } - -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - setError({}) - setIsLoading(true) - - bitsFetch(null, 'fluentSupport_authorization') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - setConf(newConf) - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected Successfully', 'bit-integrations') - }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Cunnection failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Cunnection failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/FreshSales/FreshSalesAuthorization.jsx b/frontend/src/components/AllIntegrations/FreshSales/FreshSalesAuthorization.jsx index 831b3212f..c9dbc6eda 100644 --- a/frontend/src/components/AllIntegrations/FreshSales/FreshSalesAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/FreshSales/FreshSalesAuthorization.jsx @@ -1,37 +1,15 @@ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' -import { handleAuthorize } from './FreshSalesCommonFunc' +import Authorization from '../../Connections/Authorization' export default function FreshSalesAuthorization({ freshSalesConf, setFreshSalesConf, step, setstep, - isLoading, - setIsLoading, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - } - - const handleInput = e => { - const newConf = { ...freshSalesConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setFreshSalesConf(newConf) - } const note = `

    ${__('Step of generate API token:', 'bit-integrations')}

      @@ -48,86 +26,39 @@ export default function FreshSalesAuthorization({ )}
    • ${__('Finally, click Authorize button.', 'bit-integrations')}
    + + ${__('Example: name.myfreshworks.com/crm/sales', 'bit-integrations')} + ` return ( -
    - - -
    - {__('Bundle Alias(Your Account URL):', 'bit-integrations')} -
    - -
    {error.bundle_alias}
    - - {__('Example: name.myfreshworks.com/crm/sales', 'bit-integrations')} - - -
    - {__('API Token:', 'bit-integrations')} -
    - -
    {error.api_key}
    - - {freshSalesConf.bundle_alias && ( - - {__('To Get API Token, Please Visit', 'bit-integrations')} -   - - {__('FreshSales API Token', 'bit-integrations')} - - - )} -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/FreshSales/FreshSalesCommonFunc.js b/frontend/src/components/AllIntegrations/FreshSales/FreshSalesCommonFunc.js index 3cd3d6ab4..514715a10 100644 --- a/frontend/src/components/AllIntegrations/FreshSales/FreshSalesCommonFunc.js +++ b/frontend/src/components/AllIntegrations/FreshSales/FreshSalesCommonFunc.js @@ -1,10 +1,17 @@ /* eslint-disable radix */ /* eslint-disable no-unused-expressions */ -import toast from 'react-hot-toast' import bitsFetch from '../../../Utils/bitsFetch' -import { __, sprintf } from '../../../Utils/i18nwrap' +import { __ } from '../../../Utils/i18nwrap' import { create } from 'mutative' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + api_key: conf.api_key, + bundle_alias: conf.bundle_alias + } + export const handleInput = ( e, freshSalesConf, @@ -113,8 +120,7 @@ const contactViewChange = ( const refreshFields = (module, freshSalesConf, setFreshSalesConf, setIsLoading, setSnackbar) => { const requestParams = { - api_key: freshSalesConf.api_key, - bundle_alias: freshSalesConf.bundle_alias, + ...buildAuthRequestParams(freshSalesConf), module } @@ -147,8 +153,7 @@ const refreshFields = (module, freshSalesConf, setFreshSalesConf, setIsLoading, export const accountRefreshViews = (freshSalesConf, setFreshSalesConf, setIsLoading, setSnackbar) => { const requestParams = { - api_key: freshSalesConf.api_key, - bundle_alias: freshSalesConf.bundle_alias, + ...buildAuthRequestParams(freshSalesConf), module: 'filters', type: 'sales_accounts' } @@ -177,8 +182,7 @@ export const accountRefreshViews = (freshSalesConf, setFreshSalesConf, setIsLoad export const contactRefreshViews = (freshSalesConf, setFreshSalesConf, setIsLoading, setSnackbar) => { const requestParams = { - api_key: freshSalesConf.api_key, - bundle_alias: freshSalesConf.bundle_alias, + ...buildAuthRequestParams(freshSalesConf), module: 'filters', type: 'contacts' } @@ -213,8 +217,7 @@ export const refreshAccounts = ( setSnackbar ) => { const requestParams = { - api_key: freshSalesConf.api_key, - bundle_alias: freshSalesConf.bundle_alias, + ...buildAuthRequestParams(freshSalesConf), account_view_id: accountViewId, contact_view_id: freshSalesConf.moduleData.contact_view_id, module: 'sales_accounts' @@ -250,8 +253,7 @@ export const refreshContacts = ( setSnackbar ) => { const requestParams = { - api_key: freshSalesConf.api_key, - bundle_alias: freshSalesConf.bundle_alias, + ...buildAuthRequestParams(freshSalesConf), contact_view_id: contactViewId, account_view_id: freshSalesConf.moduleData.account_view_id, module: 'contacts' @@ -333,33 +335,3 @@ export const checkRequired = freshSalesConf => { } return true } - -export const handleAuthorize = (confTmp, setError, setisAuthorized, setIsLoading) => { - if (!confTmp.bundle_alias || !confTmp.api_key) { - setError({ - bundle_alias: !confTmp.bundle_alias - ? __("Bundle Alias (Account URL) can't be empty", 'bit-integrations') - : '', - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - setError({}) - setIsLoading(true) - const requestParams = { - api_key: confTmp.api_key, - bundle_alias: confTmp.bundle_alias, - module: 'filters' - } - - bitsFetch(requestParams, 'FreshSales_authorization').then(result => { - if (result && result.success) { - setisAuthorized(true) - setIsLoading(false) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setIsLoading(false) - toast.error(__('Authorized failed', 'bit-integrations')) - }) -} diff --git a/frontend/src/components/AllIntegrations/Freshdesk/FreshdeskAuthorization.jsx b/frontend/src/components/AllIntegrations/Freshdesk/FreshdeskAuthorization.jsx index c6fbc10d8..d5435eb1c 100644 --- a/frontend/src/components/AllIntegrations/Freshdesk/FreshdeskAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Freshdesk/FreshdeskAuthorization.jsx @@ -1,40 +1,37 @@ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { getAllTicketFields, handleAuthorize } from './FreshdeskCommonFunc' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' +import { getAllTicketFields } from './FreshdeskCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function FreshdeskAuthorization({ - formID, freshdeskConf, setFreshdeskConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const loadTicketFields = useCallback( + connectionId => { + const nextConf = connectionId ? { ...freshdeskConf, connection_id: connectionId } : freshdeskConf + getAllTicketFields(nextConf, setFreshdeskConf, setIsLoading, setSnackbar) + }, + [freshdeskConf, setFreshdeskConf, setIsLoading, setSnackbar] + ) + + const handleSetStep = useCallback( + value => { + if (value === 2 && !freshdeskConf?.ticketFields?.length) { + loadTicketFields() + } - getAllTicketFields(freshdeskConf, setFreshdeskConf, setIsLoading, setSnackbar) - setstep(2) - } - const handleInput = e => { - const newConf = { ...freshdeskConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setFreshdeskConf(newConf) - } + setstep(value) + }, + [freshdeskConf?.ticketFields?.length, loadTicketFields, setstep] + ) const freshdeskInstructions = `

    ${__('Locate Your App Domain', 'bit-integrations')}

    @@ -68,106 +65,49 @@ export default function FreshdeskAuthorization({ 'Paste the copied App API key into the designated “App API key” field within the integrations you’re configuring.', 'bit-integrations' )} - ` + + + ${__('To get access Token , Please Visit', 'bit-integrations')}${' '} + + ${__('FreshDesk Console', 'bit-integrations')} + + +` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Your App Domain:', 'bit-integrations')} -
    - - {`${__( - 'App Domain Example', - 'bit-integrations' - )}: https://domain.freshdesk.com`} -
    {error.app_domain}
    - -
    - {__('App api key:', 'bit-integrations')} -
    - - - {__('To get access Token , Please Visit', 'bit-integrations')}{' '} - - {__('FreshDesk Console', 'bit-integrations')} - - -
    {error.api_key}
    - - {!isInfo && ( - <> - -
    - - - )} - - -
    + ({ + Authorization: btoa(`${authData.api_key}`), + 'Content-Type': 'application/json' + }), + extraFields: [ + { + name: 'app_domain', + label: __('Your App Domain', 'bit-integrations'), + required: true, + placeholder: __('https://domain.freshdesk.com', 'bit-integrations') + } + ] + }} + noteDetails={{ note: freshdeskInstructions }} + onConnectionSelected={loadTicketFields} + /> ) } diff --git a/frontend/src/components/AllIntegrations/Freshdesk/FreshdeskCommonFunc.js b/frontend/src/components/AllIntegrations/Freshdesk/FreshdeskCommonFunc.js index 9b4d8751d..89ef5a875 100644 --- a/frontend/src/components/AllIntegrations/Freshdesk/FreshdeskCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Freshdesk/FreshdeskCommonFunc.js @@ -2,85 +2,38 @@ import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' -export const handleInput = (e, slackConf, setSlackConf) => { - const newConf = { ...slackConf } +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + app_domain: conf.app_domain, + api_key: conf.api_key + } + +const hasAuthParams = conf => Boolean(conf?.connection_id || (conf?.app_domain && conf?.api_key)) + +export const handleInput = (e, freshdeskConf, setFreshdeskConf) => { + const newConf = { ...freshdeskConf } const { name } = e.target if (e.target.value !== '') { newConf[name] = e.target.value } else { delete newConf[name] } - setSlackConf({ ...newConf }) -} - -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setIsLoading(true) - - const tokenRequestParams = { - app_domain: confTmp.app_domain, - api_key: confTmp.api_key - } - - bitsFetch(tokenRequestParams, 'freshdesk_authorization_and_fetch_tickets') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Authorized Successfully', 'bit-integrations') - }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) + setFreshdeskConf({ ...newConf }) } export const getAllTicketFields = (confTmp, setConf, setIsLoading, setSnackbar) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' + if (!hasAuthParams(confTmp)) { + setSnackbar({ + show: true, + msg: __('Authorization info is missing. please authorize again', 'bit-integrations') }) return } setIsLoading(true) - const tokenRequestParams = { - app_domain: confTmp.app_domain, - api_key: confTmp.api_key - } + const tokenRequestParams = buildAuthRequestParams(confTmp) bitsFetch(tokenRequestParams, 'freshdesk_fetch_ticket_fields') .then(result => result) @@ -110,18 +63,16 @@ export const getAllTicketFields = (confTmp, setConf, setIsLoading, setSnackbar) } export const getAllContactFields = (confTmp, setConf, setIsLoading, setSnackbar) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' + if (!hasAuthParams(confTmp)) { + setSnackbar({ + show: true, + msg: __('Authorization info is missing. please authorize again', 'bit-integrations') }) return } setIsLoading(true) - const tokenRequestParams = { - app_domain: confTmp.app_domain, - api_key: confTmp.api_key - } + const tokenRequestParams = buildAuthRequestParams(confTmp) bitsFetch(tokenRequestParams, 'freshdesk_fetch_Contact_fields') .then(result => result) diff --git a/frontend/src/components/AllIntegrations/GamiPress/GamiPressAuthorization.jsx b/frontend/src/components/AllIntegrations/GamiPress/GamiPressAuthorization.jsx index b99b61924..2a64dc978 100644 --- a/frontend/src/components/AllIntegrations/GamiPress/GamiPressAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/GamiPress/GamiPressAuthorization.jsx @@ -1,103 +1,38 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function GamiPressAuthorization({ - formID, gamiPressConf, setGamiPressConf, step, setStep, - isLoading, - setIsLoading, - setSnackbar + isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'gamiPress_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with GamiPress Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(gamiPressConf) - newConf[e.target.name] = e.target.value - setGamiPressConf(newConf) - } - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - {isLoading === 'auth' && ( -
    - - Checking if GamiPress is active!!! -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'GamiPress' - )} -
    - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/GetResponse/GetResponseAuthorization.jsx b/frontend/src/components/AllIntegrations/GetResponse/GetResponseAuthorization.jsx index 78eeb9011..9cd8a2a7b 100644 --- a/frontend/src/components/AllIntegrations/GetResponse/GetResponseAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/GetResponse/GetResponseAuthorization.jsx @@ -1,10 +1,10 @@ -/* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +/* eslint-disable jsx-a11y/anchor-is-valid */ +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { fetchCustomFields, getresponseAuthentication } from './GetResponseCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { fetchCampaigns, fetchCustomFields } from './GetResponseCommonFunc' export default function GetResponseAuthorization({ getResponseConf, @@ -13,35 +13,47 @@ export default function GetResponseAuthorization({ setstep, loading, setLoading, - setSnackbar, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', auth_token: '' }) - useEffect(() => { - isAuthorized && fetchCustomFields(getResponseConf, setGetResponseConf, setLoading, 'default') - }, [isAuthorized]) + const loadCampaignsAndFields = useCallback( + async connectionId => { + const nextConf = connectionId + ? { ...getResponseConf, connection_id: connectionId } + : getResponseConf - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + await fetchCampaigns( + nextConf, + setGetResponseConf, + undefined, + undefined, + loading, + setLoading, + 'refreshCampaigns' + ) + await fetchCustomFields(nextConf, setGetResponseConf, setLoading, 'default') + }, + [getResponseConf, setGetResponseConf, loading, setLoading] + ) + + const handleConnectionSelected = useCallback( + async connectionId => { + await loadCampaignsAndFields(connectionId) + }, + [loadCampaignsAndFields] + ) - !getResponseConf?.default - setstep(2) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !getResponseConf?.campaigns?.length) { + loadCampaignsAndFields() + } - const handleInput = e => { - const newConf = { ...getResponseConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setGetResponseConf(newConf) - } + setstep(value) + }, + [getResponseConf, loadCampaignsAndFields, setstep] + ) - const note = ` -

    ${__('Step of generate API token:', 'bit-integrations')}

    + const note = `

    ${__('Step of generate API token:', 'bit-integrations')}

    • ${__( 'Goto', @@ -55,84 +67,29 @@ export default function GetResponseAuthorization({ 'bit-integrations' )}
    • ${__('Finally, click Authorize button.', 'bit-integrations')}
    • -
    - ` + ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('API Token:', 'bit-integrations')} -
    - -
    {error.auth_token}
    - - - {__('To Get API Token, Please Visit', 'bit-integrations')} -   - - {__('GetResponse API Token', 'bit-integrations')} - - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/GetResponse/GetResponseCommonFunc.js b/frontend/src/components/AllIntegrations/GetResponse/GetResponseCommonFunc.js index 93c06dd8d..7bca9f249 100644 --- a/frontend/src/components/AllIntegrations/GetResponse/GetResponseCommonFunc.js +++ b/frontend/src/components/AllIntegrations/GetResponse/GetResponseCommonFunc.js @@ -45,57 +45,65 @@ export const checkMappedFields = getResponseConf => { return true } -export const getresponseAuthentication = ( +const buildAuthRequestParams = confTmp => + confTmp.connection_id ? { connection_id: confTmp.connection_id } : { auth_token: confTmp.auth_token } + +export const fetchCampaigns = ( confTmp, setConf, setError, setisAuthorized, loading, setLoading, - type + type = 'authentication' ) => { - if (!confTmp.auth_token) { - setError({ + if (!confTmp.connection_id && !confTmp.auth_token) { + setError?.({ auth_token: !confTmp.auth_token ? __("Api Key can't be empty", 'bit-integrations') : '' }) return } - setError({}) + setError?.({}) + if (type === 'authentication') { setLoading({ ...loading, auth: true }) } + if (type === 'refreshCampaigns') { setLoading({ ...loading, customFields: true }) } - const requestParams = { auth_token: confTmp.auth_token } - bitsFetch(requestParams, 'getresponse_authentication').then(result => { + const requestParams = buildAuthRequestParams(confTmp) + + bitsFetch(requestParams, 'getresponse_fetch_all_list').then(result => { if (result && result.success) { const newConf = { ...confTmp } if (result.data) { newConf.campaigns = result.data } setConf(newConf) - setisAuthorized(true) + setisAuthorized?.(true) + if (type === 'authentication') { setLoading({ ...loading, auth: false }) toast.success(__('Authorized Successfully', 'bit-integrations')) } else if (type === 'refreshCampaigns') { setLoading({ ...loading, customFields: false }) - toast.success(__('Campaigns fectched successfully', 'bit-integrations')) + toast.success(__('Campaigns fetched successfully', 'bit-integrations')) } return } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed', 'bit-integrations')) + + setLoading({ ...loading, auth: false, customFields: false }) + toast.error(__('Campaigns fetching failed', 'bit-integrations')) }) } export const getAllTags = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, tags: true }) - const requestParams = { auth_token: confTmp.auth_token } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'getresponse_fetch_all_tags').then(result => { if (result && result.success) { @@ -119,7 +127,7 @@ export const fetchCustomFields = (confTmp, setConf, setLoading, type) => { setLoading({ ...setLoading, field: true }) } - const requestParams = { auth_token: confTmp.auth_token } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'getresponse_fetch_custom_fields').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/GetResponse/GetResponseIntegLayout.jsx b/frontend/src/components/AllIntegrations/GetResponse/GetResponseIntegLayout.jsx index b70e0134d..a7adcf692 100644 --- a/frontend/src/components/AllIntegrations/GetResponse/GetResponseIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/GetResponse/GetResponseIntegLayout.jsx @@ -1,8 +1,7 @@ -import { useState } from 'react' import { __ } from '../../../Utils/i18nwrap' import Loader from '../../Loaders/Loader' import GetResponseActions from './GetResponseActions' -import { fetchCustomFields, getresponseAuthentication } from './GetResponseCommonFunc' +import { fetchCampaigns, fetchCustomFields } from './GetResponseCommonFunc' import GetResponseFieldMap from './GetResponseFieldMap' import { addFieldMap } from './IntegrationHelpers' @@ -15,9 +14,6 @@ export default function GetResponseIntegLayout({ setLoading, setSnackbar }) { - const [error, setError] = useState({ name: '', auth_token: '' }) - const [isAuthorized, setisAuthorized] = useState(false) - return ( <>
    @@ -38,11 +34,11 @@ export default function GetResponseIntegLayout({ -
    - - - )} - + + {__('To get API , Please Visit', 'bit-integrations')}{' '} + + {__('Getgist API Console', 'bit-integrations')} + + + ) + }} + /> ) } diff --git a/frontend/src/components/AllIntegrations/GiveWp/GiveWpAuthorization.jsx b/frontend/src/components/AllIntegrations/GiveWp/GiveWpAuthorization.jsx index 4aae9ced7..7e9c6b140 100644 --- a/frontend/src/components/AllIntegrations/GiveWp/GiveWpAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/GiveWp/GiveWpAuthorization.jsx @@ -1,103 +1,29 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function GiveWpAuthorization({ - formID, - giveWpConf, - setGiveWpConf, - step, - setStep, - isLoading, - setIsLoading, - setSnackbar -}) { - const [isAuthorized, setisAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'giveWp_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with GiveWp Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(giveWpConf) - newConf[e.target.name] = e.target.value - setGiveWpConf(newConf) - } +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function GiveWpAuthorization({ giveWpConf, setGiveWpConf, step, setStep, isInfo }) { return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - {isLoading === 'auth' && ( -
    - - Checking if GiveWp affiliate is active!!! -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'GiveWp' - )} -
    - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendar.jsx b/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendar.jsx index 090e08b72..18a3185bc 100644 --- a/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendar.jsx +++ b/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendar.jsx @@ -1,11 +1,10 @@ /* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { setGrantTokenResponse } from '../IntegrationHelpers/GoogleIntegrationHelpers' import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import GoogleCalendarAuthorization from './GoogleCalendarAuthorization' @@ -39,10 +38,6 @@ function GoogleCalendar({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('googleCalendar') - }, []) - const saveConfig = () => { saveActionConf({ flow, diff --git a/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendarAuthorization.jsx b/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendarAuthorization.jsx index 446b9cfc3..19292d1b2 100644 --- a/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendarAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendarAuthorization.jsx @@ -1,155 +1,52 @@ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' -import { $appConfigState } from '../../../GlobalStates' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { getAllGoogleCalendarLists, handleAuthorize } from './GoogleCalendarCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function GoogleCalendarAuthorization({ - flowID, googleCalendarConf, setGoogleCalendarConf, step, setStep, - isLoading, - setIsLoading, - setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ clientId: '', clientSecret: '' }) - const btcbi = useRecoilValue($appConfigState) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - getAllGoogleCalendarLists(flowID, googleCalendarConf, setGoogleCalendarConf, setIsLoading) - setStep(2) - } - - const handleInput = e => { - const newConf = { ...googleCalendarConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setGoogleCalendarConf(newConf) - } + const note = ` +

    ${__('Google Calendar OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create OAuth client in Google API Console.', 'bit-integrations')}
    • +
    • ${__('Set homepage and redirect URI exactly from integration settings.', 'bit-integrations')}
    • +
    • ${__('Enable Google Calendar API and authorize with required scope.', 'bit-integrations')}
    • +
    + ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - - - - {__('To Get Client Id & Secret, Please Visit', 'bit-integrations')} -   - - {__('Google API Console', 'bit-integrations')} - - - -
    - {__('GoogleCalendar Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    - -
    - {__('GoogleCalendar Client Secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    - - {!isInfo && ( - <> - -
    - - - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendarCommonFunc.js b/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendarCommonFunc.js index 38b7cce6d..35d3be316 100644 --- a/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendarCommonFunc.js +++ b/frontend/src/components/AllIntegrations/GoogleCalendar/GoogleCalendarCommonFunc.js @@ -22,12 +22,19 @@ export const checkMappedFields = fieldsMapped => { return true } +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const getAllGoogleCalendarLists = (flowID, googleCalendarConf, setGoogleCalendarConf) => { const queryParams = { flowID: flowID ?? null, - clientId: googleCalendarConf.clientId, - clientSecret: googleCalendarConf.clientSecret, - tokenDetails: googleCalendarConf.tokenDetails + ...buildAuthRequestParams(googleCalendarConf) } const loadPostTypes = bitsFetch(queryParams, 'googleCalendar_get_all_lists').then(result => { if (result && result.success) { @@ -49,82 +56,3 @@ export const getAllGoogleCalendarLists = (flowID, googleCalendarConf, setGoogleC loading: __('Loading Google Calendar List...', 'bit-integrations') }) } - -export const handleAuthorize = (confTmp, setConf, setIsAuthorized, setIsLoading, setError, btcbi) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Client Secret can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - const scopes = 'https://www.googleapis.com/auth/calendar' - // eslint-disable-next-line no-undef - const apiEndpoint = `https://accounts.google.com/o/oauth2/v2/auth?scope=${scopes}&access_type=offline&prompt=consent&response_type=code&state=${encodeURIComponent( - window.location.href - )}/redirect&redirect_uri=${encodeURIComponent(`${btcbi.api}/redirect`)}&client_id=${confTmp.clientId}` - const authWindow = window.open(apiEndpoint, 'googleCalendar', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isAuthRedirectLocation = false - const bitsGoogleCalendar = localStorage.getItem('__googleCalendar') - if (bitsGoogleCalendar) { - isAuthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsGoogleCalendar) - localStorage.removeItem('__googleCalendar') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isAuthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - toast.error( - `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - ) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper(grantTokenResponse, newConf, setConf, setIsAuthorized, setIsLoading, btcbi) - } - } - }, 500) -} - -const tokenHelper = (grantToken, confTmp, setConf, setIsAuthorized, setIsLoading, btcbi) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - // eslint-disable-next-line no-undef - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - - bitsFetch(tokenRequestParams, 'googleCalendar_authorization').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setIsAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - toast.error( - `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - ) - } else { - toast.error(__('Authorization failed. please try again', 'bit-integrations')) - } - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContacts.jsx b/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContacts.jsx index c9eaf6278..40778ab8f 100644 --- a/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContacts.jsx +++ b/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContacts.jsx @@ -1,11 +1,10 @@ /* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { setGrantTokenResponse } from '../IntegrationHelpers/GoogleIntegrationHelpers' import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import GoogleContactsAuthorization from './GoogleContactsAuthorization' @@ -50,10 +49,6 @@ function GoogleContacts({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('googleContacts') - }, []) - const saveConfig = () => { saveActionConf({ flow, diff --git a/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContactsAuthorization.jsx b/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContactsAuthorization.jsx index 1a4287f8d..ed4818e4a 100644 --- a/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContactsAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContactsAuthorization.jsx @@ -1,154 +1,52 @@ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' -import { $appConfigState } from '../../../GlobalStates' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { handleAuthorize } from './GoogleContactsCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function GoogleContactsAuthorization({ - flowID, googleContactsConf, setGoogleContactsConf, step, setStep, - isLoading, - setIsLoading, - setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ clientId: '', clientSecret: '' }) - const btcbi = useRecoilValue($appConfigState) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - // getAllGoogleCalendarLists(flowID, googleContactsConf, setGoogleContactsConf, setIsLoading) - setStep(2) - } - - const handleInput = e => { - const newConf = { ...googleContactsConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setGoogleContactsConf(newConf) - } + const note = ` +

    ${__('Google Contacts OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create OAuth client in Google API Console.', 'bit-integrations')}
    • +
    • ${__('Set homepage and redirect URI exactly from integration settings.', 'bit-integrations')}
    • +
    • ${__('Enable Google People API and authorize with required scope.', 'bit-integrations')}
    • +
    + ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - - - - {__('To Get Client Id & Secret, Please Visit', 'bit-integrations')} -   - - {__('Google API Console', 'bit-integrations')} - - - -
    - {__('GoogleContacts Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    - -
    - {__('GoogleContacts Client Secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    - - {!isInfo && ( - <> - -
    - - - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContactsCommonFunc.js b/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContactsCommonFunc.js index 589042f3a..4d6e66ea0 100644 --- a/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContactsCommonFunc.js +++ b/frontend/src/components/AllIntegrations/GoogleContacts/GoogleContactsCommonFunc.js @@ -1,7 +1,4 @@ /* eslint-disable no-else-return */ -import toast from 'react-hot-toast' -import { __ } from '../../../Utils/i18nwrap' -import bitsFetch from '../../../Utils/bitsFetch' export const handleInput = (e, googleContactsConf, setGoogleContactsConf) => { const newConf = { ...googleContactsConf } @@ -22,85 +19,6 @@ export const checkMappedFields = fieldsMapped => { return true } -export const handleAuthorize = (confTmp, setConf, setIsAuthorized, setIsLoading, setError, btcbi) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Client Secret can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - const scopes = 'https://www.googleapis.com/auth/contacts' - // eslint-disable-next-line no-undef - const apiEndpoint = `https://accounts.google.com/o/oauth2/v2/auth?scope=${scopes}&access_type=offline&prompt=consent&response_type=code&state=${encodeURIComponent( - window.location.href - )}/redirect&redirect_uri=${encodeURIComponent(`${btcbi.api}/redirect`)}&client_id=${confTmp.clientId}` - const authWindow = window.open(apiEndpoint, 'googleContacts', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isAuthRedirectLocation = false - const bitsGoogleCalendar = localStorage.getItem('__googleContacts') - if (bitsGoogleCalendar) { - isAuthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsGoogleCalendar) - localStorage.removeItem('__googleContacts') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isAuthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - toast.error( - `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - ) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper(grantTokenResponse, newConf, setConf, setIsAuthorized, setIsLoading, btcbi) - } - } - }, 500) -} - -const tokenHelper = (grantToken, confTmp, setConf, setIsAuthorized, setIsLoading, btcbi) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - // eslint-disable-next-line no-undef - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - - bitsFetch(tokenRequestParams, 'googleContacts_authorization').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setIsAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - toast.error( - `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - ) - } else { - toast.error(__('Authorization failed. please try again', 'bit-integrations')) - } - setIsLoading(false) - }) -} - export const generateMappedField = googleContactsConf => { const requiredFlds = googleContactsConf?.default.filter(fld => fld.required === true) return requiredFlds.length > 0 diff --git a/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDrive.jsx b/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDrive.jsx index 5ec94f359..cfbbf1f4f 100644 --- a/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDrive.jsx +++ b/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDrive.jsx @@ -1,11 +1,10 @@ /* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { setGrantTokenResponse } from '../IntegrationHelpers/GoogleIntegrationHelpers' import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import GoogleDriveAuthorization from './GoogleDriveAuthorization' @@ -28,10 +27,6 @@ function GoogleDrive({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('googleDrive') - }, []) - const saveConfig = () => { saveActionConf({ flow, diff --git a/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDriveAuthorization.jsx b/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDriveAuthorization.jsx index 2dc37b3e3..01065aaf3 100644 --- a/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDriveAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDriveAuthorization.jsx @@ -1,154 +1,52 @@ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' -import { $appConfigState } from '../../../GlobalStates' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { getAllGoogleDriveFolders, handleAuthorize } from './GoogleDriveCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + export default function GoogleDriveAuthorization({ - flowID, googleDriveConf, setGoogleDriveConf, step, setStep, - isLoading, - setIsLoading, - setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ clientId: '', clientSecret: '' }) - const btcbi = useRecoilValue($appConfigState) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - getAllGoogleDriveFolders(flowID, googleDriveConf, setGoogleDriveConf, setIsLoading) - setStep(2) - } - - const handleInput = e => { - const newConf = { ...googleDriveConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setGoogleDriveConf(newConf) - } + const note = ` +

    ${__('Google Drive OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create OAuth client in Google API Console.', 'bit-integrations')}
    • +
    • ${__('Set homepage and redirect URI exactly from integration settings.', 'bit-integrations')}
    • +
    • ${__('Enable Google Drive API and authorize with required scope.', 'bit-integrations')}
    • +
    + ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - - - - {__('To Get Client Id & Secret, Please Visit', 'bit-integrations')} -   - - {__('Google API Console', 'bit-integrations')} - - - -
    - {__('GoogleDrive Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    - -
    - {__('GoogleDrive Client Secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    - - {!isInfo && ( - <> - -
    - - - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDriveCommonFunc.js b/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDriveCommonFunc.js index 317dffbd0..a2c144c6c 100644 --- a/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDriveCommonFunc.js +++ b/frontend/src/components/AllIntegrations/GoogleDrive/GoogleDriveCommonFunc.js @@ -14,12 +14,19 @@ export const handleInput = (e, googleDriveConf, setGoogleDriveConf) => { setGoogleDriveConf({ ...newConf }) } +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const getAllGoogleDriveFolders = (flowID, googleDriveConf, setGoogleDriveConf) => { const queryParams = { flowID: flowID ?? null, - clientId: googleDriveConf.clientId, - clientSecret: googleDriveConf.clientSecret, - tokenDetails: googleDriveConf.tokenDetails + ...buildAuthRequestParams(googleDriveConf) } const loadPostTypes = bitsFetch(queryParams, 'googleDrive_get_all_folders').then(result => { if (result && result.success) { @@ -41,82 +48,3 @@ export const getAllGoogleDriveFolders = (flowID, googleDriveConf, setGoogleDrive loading: __('Loading GoogleDrive Folders List...', 'bit-integrations') }) } - -export const handleAuthorize = (confTmp, setConf, setIsAuthorized, setIsLoading, setError, btcbi) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Client Secret can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - const scopes = 'https://www.googleapis.com/auth/drive' - // eslint-disable-next-line no-undef - const apiEndpoint = `https://accounts.google.com/o/oauth2/v2/auth?scope=${scopes}&access_type=offline&prompt=consent&response_type=code&state=${encodeURIComponent( - window.location.href - )}/redirect&redirect_uri=${encodeURIComponent(`${btcbi.api}/redirect`)}&client_id=${confTmp.clientId}` - const authWindow = window.open(apiEndpoint, 'googleDrive', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isAuthRedirectLocation = false - const bitsGoogleDrive = localStorage.getItem('__googleDrive') - if (bitsGoogleDrive) { - isAuthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsGoogleDrive) - localStorage.removeItem('__googleDrive') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isAuthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - toast.error( - `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - ) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper(grantTokenResponse, newConf, setConf, setIsAuthorized, setIsLoading, btcbi) - } - } - }, 500) -} - -const tokenHelper = (grantToken, confTmp, setConf, setIsAuthorized, setIsLoading, btcbi) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - // eslint-disable-next-line no-undef - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - - bitsFetch(tokenRequestParams, 'googleDrive_authorization').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setIsAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - toast.error( - `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - ) - } else { - toast.error(__('Authorization failed. please try again', 'bit-integrations')) - } - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheet.jsx b/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheet.jsx index 65cfee903..02c8005fc 100644 --- a/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheet.jsx +++ b/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheet.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' import BackIcn from '../../../Icons/BackIcn' diff --git a/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheetAuthorization.jsx b/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheetAuthorization.jsx index afa5c51d4..0e78f208b 100644 --- a/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheetAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheetAuthorization.jsx @@ -1,215 +1,46 @@ -import { useEffect, useState } from 'react' -import { useRecoilState, useRecoilValue } from 'recoil' -import { $appConfigState, authInfoAtom } from '../../../GlobalStates' -import BackIcn from '../../../Icons/BackIcn' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { handleAuthorize, refreshSpreadsheets, tokenHelper } from './GoogleSheetCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' -import SelectAuthorizationType from '../../OneClickRadioComponents/SelectAuthorizationType' -import AuthorizationAccountList from '../../OneClickRadioComponents/AuthorizationAccountList' -import bitsFetch from '../../../Utils/bitsFetch' -import Loader from '../../Loaders/Loader' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function GoogleSheetAuthorization({ sheetConf, setSheetConf, step, setstep, isInfo }) { + const note = ` +

    ${__('Google Sheets OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create OAuth client in Google API Console.', 'bit-integrations')}
    • +
    • ${__('Set homepage and redirect URI exactly from integration settings.', 'bit-integrations')}
    • +
    • ${__('Enable Google Drive API and authorize with required scope.', 'bit-integrations')}
    • +
    + ` -export default function GoogleSheetAuthorization({ - formID, - sheetConf, - setSheetConf, - step, - setstep, - isLoading, - setIsLoading, - setSnackbar, - redirectLocation, - isEdit, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ clientId: '', clientSecret: '' }) - const btcbi = useRecoilValue($appConfigState) - const [authData, setAuthData] = useState([]) - const [authInfo, setAuthInfo] = useRecoilState(authInfoAtom) - const [selectedAuthType, setSelectedAuthType] = useState('Custom Authorization') - - const processAuth = option => { - handleAuthorize(sheetConf, option, setError, setIsLoading, btcbi) - } - - const getAuthData = () => { - setIsLoading(true) - const queryParams = { - actionName: sheetConf.type - } - - bitsFetch(null, 'auth/get', queryParams, 'GET').then(res => { - if (res.success && res.data.data.length > 0) { - setAuthData(res.data.data) - } - setIsLoading(false) - }) - } - - const handleVerificationCode = async authInfo => { - await tokenHelper( - authInfo, - sheetConf, - setSheetConf, - setIsAuthorized, - selectedAuthType, - authData, - setAuthData, - setIsLoading, - setSnackbar, - btcbi - ) - setAuthInfo(undefined) - } - - useEffect(() => { - if (!authInfo || Object.keys(authInfo).length === 0) return - - handleVerificationCode(authInfo) - }, [authInfo]) - - const handleInput = e => { - const newConf = { ...sheetConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSheetConf(newConf) - } - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - setstep(2) - refreshSpreadsheets(formID, sheetConf, setSheetConf, setIsLoading, setSnackbar) - } return ( -
    - - - {selectedAuthType === 'Custom Authorization' && ( -
    -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - - - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Google API Console', 'bit-integrations')} - - - -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    - -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    - - -
    -
    - )} - {isLoading && selectedAuthType !== 'Custom Authorization' && ( - - )} - {isAuthorized && selectedAuthType === 'One Click Authorization' && ( - - )} -
    - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheetCommonFunc.js b/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheetCommonFunc.js index 59514367e..0e89903b4 100644 --- a/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheetCommonFunc.js +++ b/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheetCommonFunc.js @@ -1,8 +1,6 @@ import { __, sprintf } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' import { deepCopy } from '../../../Utils/Helpers' -import { handleAuthData } from '../GlobalIntegrationHelper' -import { create } from 'mutative' export const handleInput = ( e, @@ -70,10 +68,8 @@ export const refreshSpreadsheets = (formID, sheetConf, setSheetConf, setIsLoadin const refreshModulesRequestParams = { formID, id: sheetConf.id, - clientId: sheetConf.clientId, - clientSecret: sheetConf.clientSecret, - tokenDetails: sheetConf.tokenDetails, - ownerEmail: sheetConf.ownerEmail + ownerEmail: sheetConf.ownerEmail, + ...buildAuthRequestParams(sheetConf) } setIsLoading(true) @@ -123,7 +119,7 @@ export const refreshWorksheets = (formID, sheetConf, setSheetConf, setIsLoading, const refreshSpreadsheetsRequestParams = { formID, spreadsheetId, - tokenDetails: sheetConf.tokenDetails + ...buildAuthRequestParams(sheetConf) } bitsFetch(refreshSpreadsheetsRequestParams, 'gsheet_refresh_worksheets') .then(result => { @@ -165,9 +161,7 @@ export const refreshWorksheetHeaders = (formID, sheetConf, setSheetConf, setIsLo worksheetName, header, headerRow, - clientId: sheetConf.clientId, - clientSecret: sheetConf.clientSecret, - tokenDetails: sheetConf.tokenDetails + ...buildAuthRequestParams(sheetConf) } bitsFetch(refreshWorksheetHeadersRequestParams, 'gsheet_refresh_worksheet_headers') @@ -215,98 +209,6 @@ export const refreshWorksheetHeaders = (formID, sheetConf, setSheetConf, setIsLo .catch(() => setIsLoading(false)) } -export const handleAuthorize = (confTmp, selectedAuthType, setError, setIsLoading, btcbi) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty", 'bit-integrations') : '' - }) - return - } - - const clientId = confTmp.clientId - - setIsLoading(true) - - const scopes = 'https://www.googleapis.com/auth/drive' - // eslint-disable-next-line no-undef - const finalRedirectUri = `${btcbi.api}/redirect` - - const { href, hash } = window.location - const stateUrl = hash ? href.replace(hash, '#/auth-response/') : `${href}#/auth-response/` - - const apiEndpoint = `https://accounts.google.com/o/oauth2/v2/auth?scope=${scopes}&access_type=offline&prompt=consent&response_type=code&state=${encodeURIComponent( - stateUrl - )}&redirect_uri=${encodeURIComponent(finalRedirectUri)}&client_id=${clientId}` - const authWindow = window.open(apiEndpoint, 'googleSheet', 'width=400,height=609,toolbar=off') - if (selectedAuthType === 'Custom Authorization') { - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - setIsLoading(false) - } - }, 500) - } -} - -export const tokenHelper = async ( - authInfo, - confTmp, - setConf, - setIsAuthorized, - selectedAuthType, - authData, - setAuthData, - setIsLoading, - setSnackbar, - btcbi -) => { - if (!selectedAuthType) { - return - } - const tokenRequestParams = {} - tokenRequestParams.code = authInfo.code || '' - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - // eslint-disable-next-line no-undef - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - - setIsLoading(true) - await bitsFetch(tokenRequestParams, 'gsheet_generate_token') - .then(result => result) - .then(async result => { - if (result && result.success) { - setConf(prevConf => - create(prevConf, draftConf => { - draftConf.tokenDetails = result.data - }) - ) - - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Authorized Successfully', 'bit-integrations') - }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} - export const checkMappedFields = sheetconf => { const mappedFleld = sheetconf.field_map ? sheetconf.field_map.filter(mapped => !mapped.formField && !mapped.googleSheetField) @@ -317,18 +219,11 @@ export const checkMappedFields = sheetconf => { return true } -async function fetchUserInfo(tokenResponse) { - const accessToken = tokenResponse.access_token - - try { - const userInfoResponse = await fetch('https://www.googleapis.com/drive/v3/about?fields=user', { - headers: { - Authorization: `Bearer ${accessToken}` +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails } - }) - const userInfo = await userInfoResponse.json() - return userInfo - } catch (error) { - console.error('Error fetching user info:', error) - } -} diff --git a/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheetInfo.jsx b/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheetInfo.jsx deleted file mode 100644 index 70b37379a..000000000 --- a/frontend/src/components/AllIntegrations/GoogleSheet/GoogleSheetInfo.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import { useEffect, useState } from 'react' -import bitsFetch from '../../../Utils/bitsFetch' -import { __ } from '../../../Utils/i18nwrap' -import AuthorizationAccountList from '../../OneClickRadioComponents/AuthorizationAccountList' - -export default function GoogleSheetInfo({ sheetConf, isInfo }) { - const [authData, setAuthData] = useState([]) - - useEffect(() => { - const queryParams = { - id: sheetConf.authId - } - bitsFetch(null, 'auth/getbyId', queryParams, 'GET').then(res => { - if (res.success) { - if (res.data.data.length > 0) { - setAuthData(res.data.data) - } - } - }) - }, []) - return ( -
    - {sheetConf.tokenDetails.selectedAuthType == 'Custom Authorization' && ( -
    -

    - Account Details (Custom Authorization){' '} -

    -
    - {__('Client id:', 'bit-integrations')} -
    - - -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    - )} - {sheetConf.tokenDetails.selectedAuthType == 'One Click Authorization' && authData.length !== 0 && ( -
    -

    - Account Details (One Click Authorization){' '} -

    - -
    - )} - {sheetConf.tokenDetails.selectedAuthType == 'One Click Authorization' && authData.length === 0 && ( -
    -

    - The Authorized Account Has been Deleted. (One Click Authorization){' '} -

    -
    - )} -
    - ) -} diff --git a/frontend/src/components/AllIntegrations/Gravitec/GravitecAuthorization.jsx b/frontend/src/components/AllIntegrations/Gravitec/GravitecAuthorization.jsx index ce4df4972..7b378b918 100644 --- a/frontend/src/components/AllIntegrations/Gravitec/GravitecAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Gravitec/GravitecAuthorization.jsx @@ -1,155 +1,46 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { gravitecAuthentication } from './GravitecCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function GravitecAuthorization({ - gravitecConf, - setGravitecConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ site_url: '', app_key: '', app_secret: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !gravitecConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...gravitecConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setGravitecConf(newConf) - } - - const ActiveInstructions = ` -

    ${__('To Get App key & App Secret', 'bit-integrations')}

    -
      -
    • ${__('First go to your Gravitec dashboard.', 'bit-integrations')}
    • -
    • ${__('Click go to your "YOUR SITES" from left SideBar', 'bit-integrations')}
    • -
    • ${__('Then click "Settings"', 'bit-integrations')}
    • -
    • ${__('Then Click "REST API"', 'bit-integrations')}
    • -
    ` +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function GravitecAuthorization({ gravitecConf, setGravitecConf, step, setStep, isInfo }) { + const note = ` +

    ${__('To Get App key & App Secret', 'bit-integrations')}

    +
      +
    • ${__('First go to your Gravitec dashboard.', 'bit-integrations')}
    • +
    • ${__('Open your site from the left sidebar.', 'bit-integrations')}
    • +
    • ${__('Open Settings, then REST API.', 'bit-integrations')}
    • +
    • ${__('Use App key as Username and App secret as Password here.', 'bit-integrations')}
    • +
    ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Site Url:', 'bit-integrations')} -
    - -
    {error.site_url}
    - -
    - {__('App key:', 'bit-integrations')} -
    - -
    {error.app_key}
    - -
    - {__('App Secret:', 'bit-integrations')} -
    - -
    {error.app_secret}
    - - - {__('To Get App key & App Secret, Please Visit', 'bit-integrations')} -   - - {__('Gravitec App key & Secret', 'bit-integrations')} - - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Gravitec/GravitecCommonFunc.js b/frontend/src/components/AllIntegrations/Gravitec/GravitecCommonFunc.js index c61a77b58..ee1a2f640 100644 --- a/frontend/src/components/AllIntegrations/Gravitec/GravitecCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Gravitec/GravitecCommonFunc.js @@ -1,9 +1,3 @@ -/* eslint-disable no-console */ -/* eslint-disable no-else-return */ -import toast from 'react-hot-toast' -import bitsFetch from '../../../Utils/bitsFetch' -import { __ } from '../../../Utils/i18nwrap' - export const handleInput = (e, salesmateConf, setSalesmateConf) => { const newConf = { ...salesmateConf } const { name } = e.target @@ -40,41 +34,3 @@ export const checkMappedFields = gravitecConf => { } return true } - -export const gravitecAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.site_url || !confTmp.app_key || !confTmp.app_secret) { - setError({ - site_url: !confTmp.site_url ? __("Site Url can't be empty", 'bit-integrations') : '', - app_key: !confTmp.app_key ? __("App Key can't be empty", 'bit-integrations') : '', - app_secret: !confTmp.app_secret ? __("App Secret can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - site_url: confTmp.site_url, - app_key: confTmp.app_key, - app_secret: confTmp.app_secret - } - - bitsFetch(requestParams, 'gravitec_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid Public Id & Secret Key', 'bit-integrations')) - }) -} diff --git a/frontend/src/components/AllIntegrations/Groundhogg/Groundhogg.jsx b/frontend/src/components/AllIntegrations/Groundhogg/Groundhogg.jsx index 8e3a9fae3..d4b1a2320 100644 --- a/frontend/src/components/AllIntegrations/Groundhogg/Groundhogg.jsx +++ b/frontend/src/components/AllIntegrations/Groundhogg/Groundhogg.jsx @@ -1,6 +1,6 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' -import { useNavigate, useParams } from 'react-router' +import { useNavigate } from 'react-router' import { useRecoilValue } from 'recoil' import BackIcn from '../../../Icons/BackIcn' import { $appConfigState } from '../../../GlobalStates' @@ -15,7 +15,6 @@ import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' function Groundhogg({ formFields, setFlow, flow, allIntegURL }) { const navigate = useNavigate() - const { formID } = useParams() const btcbi = useRecoilValue($appConfigState) const { siteURL } = btcbi const [isLoading, setIsLoading] = useState(false) @@ -105,14 +104,11 @@ function Groundhogg({ formFields, setFlow, flow, allIntegURL }) { {/* STEP 1 */} {/* STEP 2 */} @@ -121,9 +117,7 @@ function Groundhogg({ formFields, setFlow, flow, allIntegURL }) { style={{ ...(step === 2 && { width: 900, height: 'auto', overflow: 'visible' }) }}> - handleInput(e, groundhoggConf, setGroundhoggConf, formID, setIsLoading, setSnackbar) - } + handleInput={e => handleInput(e, groundhoggConf, setGroundhoggConf, setIsLoading, setSnackbar)} groundhoggConf={groundhoggConf} setGroundhoggConf={setGroundhoggConf} isLoading={isLoading} diff --git a/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggActions.jsx b/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggActions.jsx index eb171e35e..8a2c76ad5 100644 --- a/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggActions.jsx +++ b/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggActions.jsx @@ -139,7 +139,7 @@ export default function GroundhoggActions({ groundhoggConf, setGroundhoggConf, f customValue /> -
    - - - )} - - + ) } diff --git a/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggCommonFunc.js b/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggCommonFunc.js index e0ec0fbea..5ac8cb5ab 100644 --- a/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggCommonFunc.js @@ -52,63 +52,27 @@ export const checkMetaMappedFields = groundhoggConf => { return true } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.public_key) { - setError({ - public_key: !confTmp.public_key ? __("Public Key can't be empty", 'bit-integrations') : '' - }) - return - } - if (!confTmp.token) { - setError({ token: !confTmp.token ? __("token can't be empty", 'bit-integrations') : '' }) - return - } - if (!confTmp.domainName) { - setError({ - domainName: !confTmp.domainName ? __("Domain Name can't be empty", 'bit-integrations') : '' - }) - return - } - setError({}) - setIsLoading(true) +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + public_key: confTmp.public_key, + token: confTmp.token, + domainName: confTmp.domainName + } - const requestParams = { - public_key: confTmp.public_key, - token: confTmp.token, - domainName: confTmp.domainName +export const fetchAllTags = (groundhoggConf, setGroundhoggConf, setIsLoading, setSnackbar) => { + if ( + !groundhoggConf.connection_id && + (!groundhoggConf.public_key || !groundhoggConf.token || !groundhoggConf.domainName) + ) { + toast.error(__('Authorization data is missing', 'bit-integrations')) + return } - bitsFetch(requestParams, 'groundhogg_authorization_and_fetch_contacts').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - setConf(newConf) - setisAuthorized(true) - setIsLoading(false) - toast.success(__('Authorization Successful', 'bit-integrations')) - return - } - setIsLoading(false) - toast.error(__('Authorization Failed', 'bit-integrations')) - }) -} - -export const fetchAllTags = (formID, groundhoggConf, setGroundhoggConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { - public_key: groundhoggConf.public_key, - token: groundhoggConf.token, - domainName: groundhoggConf.domainName - } - - bitsFetch(requestParams, 'groundhogg_fetch_all_tags') + bitsFetch(buildAuthRequestParams(groundhoggConf), 'groundhogg_fetch_all_tags') .then(result => { if (result && result.success) { const newConf = { ...groundhoggConf } diff --git a/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggIntegLayout.jsx b/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggIntegLayout.jsx index a3bf2010b..5074faf3f 100644 --- a/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/Groundhogg/GroundhoggIntegLayout.jsx @@ -207,7 +207,7 @@ export default function GroundhoggIntegLayout({ customValue /> -
    - - - )} - - + return ( + + data?.version === 'v2' + ? `https://services.leadconnectorhq.com/locations/${data?.location_id || ''}` + : 'https://rest.gohighlevel.com/v1/contacts/?limit=1', + method: 'GET', + headers: data => ({ + Accept: 'application/json', + ...(data?.version === 'v2' ? { Version: '2021-07-28' } : {}) + }), + extraFields: [ + { + name: 'version', + label: __('Select Version', 'bit-integrations'), + required: true, + type: 'select', + placeholder: __('Select Version', 'bit-integrations'), + options: versionOptions + }, + ...(isPro + ? [ + { + name: 'location_id', + label: __('Location ID', 'bit-integrations'), + required: false, + placeholder: __('Location ID...', 'bit-integrations') + } + ] + : []) + ] + }} + noteDetails={{ note: ActiveInstructions(highLevelConf?.version) }} + onConnectionSelected={hydrateConnectionExtras} + /> ) } diff --git a/frontend/src/components/AllIntegrations/HighLevel/HighLevelCommonFunc.js b/frontend/src/components/AllIntegrations/HighLevel/HighLevelCommonFunc.js index e12b08386..2bd2344c0 100644 --- a/frontend/src/components/AllIntegrations/HighLevel/HighLevelCommonFunc.js +++ b/frontend/src/components/AllIntegrations/HighLevel/HighLevelCommonFunc.js @@ -2,7 +2,6 @@ import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' import toast from 'react-hot-toast' -import { selector } from 'recoil' import { TASK_LIST_VALUES } from './highlevelConstants' export const handleInput = (e, highLevelConf, setHighLevelConf) => { @@ -11,49 +10,14 @@ export const handleInput = (e, highLevelConf, setHighLevelConf) => { setHighLevelConf({ ...newConf }) } -export const highLevelAuthentication = ( - highLevelConf, - setHighLevelConf, - setError, - setisAuthorized, - loading, - setLoading -) => { - const newConf = { ...highLevelConf } - - if (!newConf.name || !newConf.api_key) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - api_key: !newConf.api_key ? __("Access Api Token Key can't be empty", 'bit-integrations') : '' - }) - return - } - if (newConf?.version === 'v2' && !newConf?.location_id) { - setError({ - location_id: __("Location ID can't be empty for v2", 'bit-integrations') - }) - setLoading({ ...loading, auth: false }) - return - } - - const requestParams = { - api_key: newConf.api_key, - version: newConf?.version, - location_id: newConf?.location_id - } - - setLoading({ ...loading, auth: true }) - bitsFetch(requestParams, 'highLevel_authorization').then(result => { - if (result?.success) { - setisAuthorized(true) - toast.success('Authorized Successfully') - } else { - toast.error(result?.data?.message || __('Authorization Failed', 'bit-integrations')) - } - - setLoading({ ...loading, auth: false, accounts: false }) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + api_key: confTmp.api_key, + version: confTmp?.version, + location_id: confTmp?.location_id + } export const checkMappedFields = highLevelConf => { const mappedFields = highLevelConf?.field_map @@ -71,11 +35,7 @@ export const checkMappedFields = highLevelConf => { } export const getCustomFields = (confTmp, setConf, loading, setLoading) => { - const requestParams = { - api_key: confTmp.api_key, - version: confTmp?.version, - location_id: confTmp?.location_id - } + const requestParams = buildAuthRequestParams(confTmp) setLoading({ ...loading, customFields: true }) bitsFetch(requestParams, 'get_highLevel_contact_custom_fields').then(result => { @@ -94,11 +54,7 @@ export const getCustomFields = (confTmp, setConf, loading, setLoading) => { } export const getContacts = (confTmp, setConf, loading, setLoading) => { - const requestParams = { - api_key: confTmp.api_key, - version: confTmp?.version, - location_id: confTmp?.location_id - } + const requestParams = buildAuthRequestParams(confTmp) setLoading({ ...loading, contacts: true }) bitsFetch(requestParams, 'get_highLevel_contacts').then(result => { @@ -126,11 +82,7 @@ export const getContacts = (confTmp, setConf, loading, setLoading) => { } export const getUsers = (confTmp, setConf, loading, setLoading) => { - const requestParams = { - api_key: confTmp.api_key, - version: confTmp?.version, - location_id: confTmp?.location_id - } + const requestParams = buildAuthRequestParams(confTmp) setLoading({ ...loading, users: true }) bitsFetch(requestParams, 'get_highLevel_users').then(result => { @@ -149,9 +101,7 @@ export const getUsers = (confTmp, setConf, loading, setLoading) => { export const getHLTasks = (confTmp, setConf, loading, setLoading) => { const requestParams = { - api_key: confTmp.api_key, - version: confTmp?.version, - location_id: confTmp?.location_id, + ...buildAuthRequestParams(confTmp), contact_id: confTmp.selectedContact } @@ -171,11 +121,7 @@ export const getHLTasks = (confTmp, setConf, loading, setLoading) => { } export const getPipelines = (confTmp, setConf, loading, setLoading) => { - const requestParams = { - api_key: confTmp.api_key, - version: confTmp?.version, - location_id: confTmp?.location_id - } + const requestParams = buildAuthRequestParams(confTmp) setLoading({ ...loading, pipelines: true }) bitsFetch(requestParams, 'get_highLevel_pipelines').then(result => { @@ -201,9 +147,7 @@ export const getPipelines = (confTmp, setConf, loading, setLoading) => { export const getOpportunities = (confTmp, setConf, loading, setLoading) => { const requestParams = { - api_key: confTmp.api_key, - version: confTmp?.version, - location_id: confTmp?.location_id, + ...buildAuthRequestParams(confTmp), pipeline_id: confTmp.selectedPipeline } @@ -237,9 +181,7 @@ export const getHighLevelOptions = ( } const requestParams = { - api_key: confTmp.api_key, - version: confTmp?.version, - location_id: confTmp?.location_id + ...buildAuthRequestParams(confTmp) } setLoading({ ...loading, options: true }) diff --git a/frontend/src/components/AllIntegrations/Hubspot/HubspotAuthorization.jsx b/frontend/src/components/AllIntegrations/Hubspot/HubspotAuthorization.jsx index e222a6d9f..0f45a2bae 100644 --- a/frontend/src/components/AllIntegrations/Hubspot/HubspotAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Hubspot/HubspotAuthorization.jsx @@ -1,41 +1,12 @@ /* eslint-disable no-unused-vars */ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { hubspotAuthorization } from './HubspotCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function HubspotAuthorization({ - hubspotConf, - setHubspotConf, - step, - setstep, - isInfo, - loading, - setLoading -}) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_key: '' }) - const handleInput = e => { - const newConf = { ...hubspotConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setHubspotConf(newConf) - } - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - } +import Authorization from '../../Connections/Authorization' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +export default function HubspotAuthorization({ hubspotConf, setHubspotConf, step, setstep, isInfo }) { const note = `

    ${__('Step of generating Access Token:', 'bit-integrations')}

      @@ -53,62 +24,20 @@ export default function HubspotAuthorization({ ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Hubspot Access Token:', 'bit-integrations')} -
      - -
      {error.api_key}
      - - {!isInfo && ( - <> - -
      - - - )} - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Hubspot/HubspotCommonFunc.jsx b/frontend/src/components/AllIntegrations/Hubspot/HubspotCommonFunc.jsx index c12572770..bd0c6d72c 100644 --- a/frontend/src/components/AllIntegrations/Hubspot/HubspotCommonFunc.jsx +++ b/frontend/src/components/AllIntegrations/Hubspot/HubspotCommonFunc.jsx @@ -19,6 +19,9 @@ export const handleInput = (e, hubspotConf, setHubspotConf, setIsLoading) => { setHubspotConf({ ...newConf }) } +const buildAuthRequestParams = conf => + conf.connection_id ? { connection_id: conf.connection_id } : { api_key: conf.api_key } + export const checkMappedFields = hubspotConf => { const mappedFields = hubspotConf?.field_map ? hubspotConf.field_map.filter( @@ -42,34 +45,9 @@ export const generateMappedField = hubspotConf => { : [{ formField: '', hubspotField: '' }] } -export const hubspotAuthorization = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("Access token can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { api_key: confTmp.api_key } - - bitsFetch(requestParams, 'hubSpot_authorization').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid access token', 'bit-integrations')) - }) -} - export const getAllPipelines = (confTmp, setConf, setLoading, type, loading) => { setLoading({ ...setLoading, pipelines: true }) - const requestParams = { api_key: confTmp.api_key, type } + const requestParams = { ...buildAuthRequestParams(confTmp), type } bitsFetch(requestParams, 'hubspot_pipeline').then(result => { if (result.data) { @@ -96,7 +74,7 @@ export const getAllPipelines = (confTmp, setConf, setLoading, type, loading) => export const getAllOwners = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, owners: true, hubSpotFields: true }) - const requestParams = { api_key: confTmp.api_key } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'hubspot_owners').then(result => { if (result && result.success) { @@ -118,7 +96,7 @@ export const getAllOwners = (confTmp, setConf, setLoading) => { export const getAllContacts = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, contacts: true, hubSpotFields: true }) - const requestParams = { api_key: confTmp.api_key } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'hubspot_contacts').then(result => { if (result && result.success) { @@ -140,7 +118,7 @@ export const getAllContacts = (confTmp, setConf, setLoading) => { export const getAllCompany = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, companies: true, hubSpotFields: true }) - const requestParams = { api_key: confTmp.api_key } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'hubspot_company').then(result => { if (result && result.success) { @@ -162,7 +140,7 @@ export const getAllCompany = (confTmp, setConf, setLoading) => { export const getAllIndustry = (confTmp, setConf, setLoading) => { setLoading(prevLoading => ({ ...prevLoading, industry: true })) - const requestParams = { api_key: confTmp.api_key, type: 'company' } + const requestParams = { ...buildAuthRequestParams(confTmp), type: 'company' } bitsFetch(requestParams, 'hubspot_industry').then(result => { if (result && result.success) { @@ -187,7 +165,7 @@ export const getFields = (confTmp, setConf, setLoading, type, loading, refreshCu } else { setLoading({ ...setLoading, customFields: true }) } - const requestParams = { api_key: confTmp.api_key, type } + const requestParams = { ...buildAuthRequestParams(confTmp), type } bitsFetch(requestParams, 'getFields').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/Insightly/InsightlyAuthorization.jsx b/frontend/src/components/AllIntegrations/Insightly/InsightlyAuthorization.jsx index 8f09c5add..18a8c3165 100644 --- a/frontend/src/components/AllIntegrations/Insightly/InsightlyAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Insightly/InsightlyAuthorization.jsx @@ -1,131 +1,51 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { insightlyAuthentication } from './InsightlyCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + export default function InsightlyAuthorization({ insightlyConf, setInsightlyConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '', api_url: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !insightlyConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...insightlyConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setInsightlyConf(newConf) - } + const note = ` +

      ${__('Get Insightly API credentials', 'bit-integrations')}

      +
        +
      • ${__('Open your Insightly account settings.', 'bit-integrations')}
      • +
      • ${__('Copy your API key and account host (without https://api.).', 'bit-integrations')}
      • +
      • ${__('Use API key in Username field, leave password empty, then authorize.', 'bit-integrations')}
      • +
      + + ${__('Example host:', 'bit-integrations')} name.insightly.com + + ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Your API URL:', 'bit-integrations')} -
      - -
      {error.api_url}
      - {__('Example: {name}.insightly.com', 'bit-integrations')} -
      - {__('API Key:', 'bit-integrations')} -
      - -
      {error.api_key}
      - {insightlyConf.api_url && ( - - {__('To Get API Token, Please Visit', 'bit-integrations')} -   - - {__('Insightly API Token', 'bit-integrations')} - - - )} -
      -
      - - {!isInfo && ( -
      - -
      - -
      - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Insightly/InsightlyCommonFunc.js b/frontend/src/components/AllIntegrations/Insightly/InsightlyCommonFunc.js index 568aede59..3b5328b6d 100644 --- a/frontend/src/components/AllIntegrations/Insightly/InsightlyCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Insightly/InsightlyCommonFunc.js @@ -4,6 +4,14 @@ import toast from 'react-hot-toast' import bitsFetch from '../../../Utils/bitsFetch' import { __ } from '../../../Utils/i18nwrap' +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + api_key: confTmp.api_key, + api_url: confTmp.api_url + } + export const handleInput = (e, insightlyConf, setInsightlyConf) => { const newConf = { ...insightlyConf } const { name } = e.target @@ -61,43 +69,10 @@ export const checkRequiredFields = insightlyConf => { return false } -export const insightlyAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_url || !confTmp.api_key) { - setError({ - api_url: !confTmp.api_url ? __("API URL can't be empty", 'bit-integrations') : '', - api_key: !confTmp.api_key ? __("Api Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { api_key: confTmp.api_key, api_url: confTmp.api_url } - - bitsFetch(requestParams, 'insightly_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid api_url name & API key', 'bit-integrations')) - }) -} - export const getAllOrganisations = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, organisations: true }) - const requestParams = { api_key: confTmp.api_key, api_url: confTmp.api_url } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'insightly_fetch_all_organisations').then(result => { if (result && result.success) { @@ -120,8 +95,7 @@ export const getAllCategories = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, categories: true }) const requestParams = { - api_key: confTmp.api_key, - api_url: confTmp.api_url, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -145,7 +119,7 @@ export const getAllCategories = (confTmp, setConf, setLoading) => { export const getAllStatuses = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, statuses: true }) - const requestParams = { api_key: confTmp.api_key, api_url: confTmp.api_url } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'insightly_fetch_all_statuses').then(result => { if (result && result.success) { @@ -167,7 +141,7 @@ export const getAllStatuses = (confTmp, setConf, setLoading) => { export const getLeadSources = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, LeadSources: true }) - const requestParams = { api_key: confTmp.api_key, api_url: confTmp.api_url } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'insightly_fetch_all_LeadSources').then(result => { if (result && result.success) { @@ -194,7 +168,7 @@ export const getLeadSources = (confTmp, setConf, setLoading) => { export const getLeadStatuses = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, LeadStatues: true }) - const requestParams = { api_key: confTmp.api_key, api_url: confTmp.api_url } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'insightly_fetch_all_LeadStatuses').then(result => { if (result && result.success) { @@ -223,8 +197,7 @@ export const getAllCRMPipelines = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMPipelines: true }) const requestParams = { - api_key: confTmp.api_key, - api_url: confTmp.api_url, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -249,8 +222,7 @@ export const getAllCRMPipelineStages = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMPipelineStages: true }) const requestParams = { - api_key: confTmp.api_key, - api_url: confTmp.api_url, + ...buildAuthRequestParams(confTmp), selectedCRMPipeline: confTmp.selectedCRMPipeline } diff --git a/frontend/src/components/AllIntegrations/IntegrationHelpers/GoogleIntegrationHelpers.js b/frontend/src/components/AllIntegrations/IntegrationHelpers/GoogleIntegrationHelpers.js index cba68f863..393239f59 100644 --- a/frontend/src/components/AllIntegrations/IntegrationHelpers/GoogleIntegrationHelpers.js +++ b/frontend/src/components/AllIntegrations/IntegrationHelpers/GoogleIntegrationHelpers.js @@ -1,25 +1,6 @@ import bitsFetch from '../../../Utils/bitsFetch' import { __ } from '../../../Utils/i18nwrap' -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation - .replace(`${window.opener.location.href}/redirect`, '') - .split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] - } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} - const tokenHelper = ( ajaxInteg, grantToken, diff --git a/frontend/src/components/AllIntegrations/IntegrationHelpers/IntegrationHelpers.js b/frontend/src/components/AllIntegrations/IntegrationHelpers/IntegrationHelpers.js index 2749c5611..1de7deffa 100644 --- a/frontend/src/components/AllIntegrations/IntegrationHelpers/IntegrationHelpers.js +++ b/frontend/src/components/AllIntegrations/IntegrationHelpers/IntegrationHelpers.js @@ -462,25 +462,6 @@ export const saveActionConf = async ({ } } -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation - .replace(`${window.opener.location.href}/redirect`, '') - .split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] - } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} - export const handleAuthorize = ( integ, ajaxInteg, diff --git a/frontend/src/components/AllIntegrations/JetEngine/JetEngineAuthorization.jsx b/frontend/src/components/AllIntegrations/JetEngine/JetEngineAuthorization.jsx index a64c3f0b2..7a6e637ce 100644 --- a/frontend/src/components/AllIntegrations/JetEngine/JetEngineAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/JetEngine/JetEngineAuthorization.jsx @@ -1,86 +1,38 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { jetEngineAuthentication } from './jetEngineCommonFunctions' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function JetEngineAuthorization({ jetEngineConf, setJetEngineConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !jetEngineConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...jetEngineConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setJetEngineConf(newConf) - } - return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - {error.name &&
      {error.name}
      } -
      - - {!isInfo && ( -
      - -
      - -
      - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/JetEngine/jetEngineCommonFunctions.js b/frontend/src/components/AllIntegrations/JetEngine/jetEngineCommonFunctions.js index 4f2a0d904..5e3018caf 100644 --- a/frontend/src/components/AllIntegrations/JetEngine/jetEngineCommonFunctions.js +++ b/frontend/src/components/AllIntegrations/JetEngine/jetEngineCommonFunctions.js @@ -34,25 +34,6 @@ export const checkMappedFields = jetEngineConf => { return true } -export const jetEngineAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.name) { - setError({ name: !confTmp.name ? __("Name can't be empty", 'bit-integrations') : '' }) - return - } - - setLoading({ ...loading, auth: true }) - bitsFetch({}, 'jetEngine_authentication').then(result => { - if (result.success) { - setIsAuthorized(true) - toast.success(__('Connected Successfully', 'bit-integrations')) - setLoading({ ...loading, auth: false }) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Connection failed: install and active JetEngine plugin first!', 'bit-integrations')) - }) -} - export const getJetEngineOptions = ( route, actionOptions, diff --git a/frontend/src/components/AllIntegrations/Keap/Keap.jsx b/frontend/src/components/AllIntegrations/Keap/Keap.jsx index 3e0b5bf48..f8a69edb9 100644 --- a/frontend/src/components/AllIntegrations/Keap/Keap.jsx +++ b/frontend/src/components/AllIntegrations/Keap/Keap.jsx @@ -1,11 +1,10 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' import BackIcn from '../../../Icons/BackIcn' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { setGrantTokenResponse } from './KeapIntegrationHelpers' import KeapAuthorization from './KeapAuthorization' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import { handleInput, checkMappedFields } from './KeapCommonFunc' @@ -32,11 +31,6 @@ function Keap({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - // eslint-disable-next-line no-unused-expressions - window.opener && setGrantTokenResponse('keap') - }, []) - const nextPage = () => { setTimeout(() => { document.getElementById('btcd-settings-wrp').scrollTop = 0 diff --git a/frontend/src/components/AllIntegrations/Keap/KeapActions.jsx b/frontend/src/components/AllIntegrations/Keap/KeapActions.jsx index 3d43b1cd9..eb50860a4 100644 --- a/frontend/src/components/AllIntegrations/Keap/KeapActions.jsx +++ b/frontend/src/components/AllIntegrations/Keap/KeapActions.jsx @@ -72,7 +72,7 @@ export default function KeapActions({ keapConf, setKeapConf, loading, setLoading title={__('Tags', 'bit-integrations')}>
      {__('Select tags', 'bit-integrations')}
      - {loading.tags ? ( + {loading ? ( { - const newConf = { ...keapConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setKeapConf(newConf) - } - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - refreshCustomFields(formID, keapConf, setKeapConf, setIsLoading, setSnackbar) - setstep(2) - } - return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Homepage URL:', 'bit-integrations')} -
      - + const loadCustomFields = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...keapConf, connection_id: connectionId } : keapConf + refreshCustomFields(formID, nextConf, setKeapConf, setIsLoading, setSnackbar) + }, + [formID, keapConf, setKeapConf, setIsLoading, setSnackbar] + ) -
      - {__('Authorized Redirect URIs:', 'bit-integrations')} -
      - + const handleSetStep = useCallback( + value => { + if (value === 2 && !keapConf?.customFields) { + loadCustomFields() + } - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Get Keap client id and secret', 'bit-integrations')} - - + setstep(value) + }, + [keapConf?.customFields, loadCustomFields, setstep] + ) -
      - {__('Client id:', 'bit-integrations')} -
      - -
      {error.clientId}
      + const note = `

      ${__('Get Keap client id and secret', 'bit-integrations')}

      +
        +
      • ${__('Go to Keap developer apps page.', 'bit-integrations')}
      • +
      • ${__('Create or open app and copy Client ID and Client Secret.', 'bit-integrations')}
      • +
      • ${__('Use callback URL shown in connection form.', 'bit-integrations')}
      • +
      ` -
      - {__('Client secret:', 'bit-integrations')} -
      - -
      {error.clientSecret}
      - {!isInfo && ( - <> - -
      - - - )} -
      + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/Keap/KeapCommonFunc.js b/frontend/src/components/AllIntegrations/Keap/KeapCommonFunc.js index c0216b1bd..5481b3c50 100644 --- a/frontend/src/components/AllIntegrations/Keap/KeapCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Keap/KeapCommonFunc.js @@ -1,7 +1,17 @@ -import { __, sprintf } from '../../../Utils/i18nwrap' +import toast from 'react-hot-toast' +import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' import { contactFields } from './staticData' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const handleInput = ( e, keapConf, @@ -23,116 +33,6 @@ export const handleInput = ( setKeapConf({ ...newConf }) } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - const apiEndpoint = `https://accounts.infusionsoft.com/app/oauth/authorize?scope=full&access_type=offline&prompt=consent&response_type=code&state=${encodeURIComponent( - window.location.href - )}/redirect&redirect_uri=${encodeURIComponent(`${btcbi.api}/redirect`)}&client_id=${confTmp.clientId}` - const authWindow = window.open(apiEndpoint, 'keap', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitsGoogleSheet = localStorage.getItem('__keap') - if (bitsGoogleSheet) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsGoogleSheet) - localStorage.removeItem('__keap') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper( - grantTokenResponse, - newConf, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi - ) - } - } - }, 500) -} - -const tokenHelper = ( - grantToken, - confTmp, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - // eslint-disable-next-line no-undef - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - bitsFetch(tokenRequestParams, 'keap_generate_token') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Authorized Successfully', 'bit-integrations') - }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} - export const checkMappedFields = keapConf => { const mappedFleld = keapConf.field_map ? keapConf.field_map.filter(mapped => !mapped.formField && !mapped.keapField) @@ -152,35 +52,31 @@ export const generateMappedField = keapConf => { } export const getAllTags = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, tags: true }) + setLoading(true) const requestParams = { - clientId: confTmp.clientId, - clientSecret: confTmp.clientSecret, - tokenDetails: confTmp.tokenDetails + ...buildAuthRequestParams(confTmp) } - bitsFetch(requestParams, 'keap_fetch_all_tags').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - if (result.data) { - newConf.tags = result.data + bitsFetch(requestParams, 'keap_fetch_all_tags') + .then(result => { + if (result && result.success) { + const newConf = { ...confTmp } + if (result.data) { + newConf.tags = result.data + } + setConf(newConf) + setLoading(false) + toast.success(__('Tag Fetched Successfully', 'bit-integrations')) + return } - setConf(newConf) setLoading(false) - - setSnackbar({ - show: true, - msg: __('Tag Fetched Successfully', 'bit-integrations') - }) - return - } - setLoading({ ...setLoading, tags: false }) - setSnackbar({ - show: true, - msg: __("Tag Couldn't Fetched Successfully", 'bit-integrations') + toast.error(__("Tag Couldn't Fetched Successfully", 'bit-integrations')) + }) + .catch(() => { + setLoading(false) + toast.error(__("Tag Couldn't Fetched Successfully", 'bit-integrations')) }) - }) } export const refreshCustomFields = (id, confTmp, setConf, setIsLoading, setSnackbar) => { @@ -188,9 +84,7 @@ export const refreshCustomFields = (id, confTmp, setConf, setIsLoading, setSnack const requestParams = { id: id, - clientId: confTmp.clientId, - clientSecret: confTmp.clientSecret, - tokenDetails: confTmp.tokenDetails + ...buildAuthRequestParams(confTmp) } bitsFetch(requestParams, 'keap_fetch_all_custom_fields').then(result => { diff --git a/frontend/src/components/AllIntegrations/Keap/KeapIntegrationHelpers.js b/frontend/src/components/AllIntegrations/Keap/KeapIntegrationHelpers.js index d517fbd71..499e412fe 100644 --- a/frontend/src/components/AllIntegrations/Keap/KeapIntegrationHelpers.js +++ b/frontend/src/components/AllIntegrations/Keap/KeapIntegrationHelpers.js @@ -1,25 +1,6 @@ import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation - .replace(`${window.opener.location.href}/redirect`, '') - .split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] - } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} - export const addFieldMap = (i, confTmp, setConf, uploadFields, tab) => { const newConf = { ...confTmp } if (tab) { diff --git a/frontend/src/components/AllIntegrations/KirimEmail/EditKirimEmail.jsx b/frontend/src/components/AllIntegrations/KirimEmail/EditKirimEmail.jsx index 6f63627fc..23948203a 100644 --- a/frontend/src/components/AllIntegrations/KirimEmail/EditKirimEmail.jsx +++ b/frontend/src/components/AllIntegrations/KirimEmail/EditKirimEmail.jsx @@ -1,6 +1,6 @@ /* eslint-disable no-param-reassign */ import { useState } from 'react' -import { useNavigate, useParams } from 'react-router' +import { useNavigate } from 'react-router' import { useRecoilState, useRecoilValue } from 'recoil' import { $actionConf, $formFields, $newFlow } from '../../../GlobalStates' import { __ } from '../../../Utils/i18nwrap' @@ -15,7 +15,6 @@ import { checkMappedFields, handleInput } from './KirimEmailCommonFunc' function EditKirimEmail({ allIntegURL }) { const navigate = useNavigate() - const { formID } = useParams() const [kirimEmailConf, setKirimEmailConf] = useRecoilState($actionConf) const [flow, setFlow] = useRecoilState($newFlow) @@ -42,7 +41,6 @@ function EditKirimEmail({ allIntegURL }) { handleInput(e, kirimEmailConf, setKirimEmailConf, setIsLoading, setSnackbar)} kirimEmailConf={kirimEmailConf} setKirimEmailConf={setKirimEmailConf} isLoading={isLoading} diff --git a/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailAuthorization.jsx b/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailAuthorization.jsx index 7692ac9a7..d7a1b05ca 100644 --- a/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailAuthorization.jsx @@ -1,127 +1,77 @@ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { handleAuthorize } from './KirimEmailCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllList } from './KirimEmailCommonFunc' export default function KirmilEmailAuthorization({ - formID, kirimEmailConf, setKirimEmailConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const loadLists = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...kirimEmailConf, connection_id: connectionId } : kirimEmailConf - setstep(2) - } - const handleInput = e => { - const newConf = { ...kirimEmailConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setKirimEmailConf(newConf) - } - - return ( -
      - + await getAllList(nextConf, setKirimEmailConf, setIsLoading, setSnackbar) + }, + [kirimEmailConf, setKirimEmailConf, setIsLoading, setSnackbar] + ) -
      - {__('Integration Name:', 'bit-integrations')} -
      - + const handleSetStep = useCallback( + value => { + if (value === 2 && !kirimEmailConf?.default?.allLists?.length) { + loadLists() + } -
      - {__('Your username:', 'bit-integrations')} -
      - -
      {error.userName}
      + setstep(value) + }, + [kirimEmailConf?.default?.allLists?.length, loadLists, setstep] + ) -
      - {__('App api key:', 'bit-integrations')} -
      - -
      {error.api_key}
      - - {__('To get Api key , Please Visit ', 'bit-integrations')} - - {__('Kirim Email', 'bit-integrations')} - - -
      -
      + const note = `

      ${__('Get Kirim Email credentials', 'bit-integrations')}

      +
        +
      • ${__('Log in to your Kirim Email account.', 'bit-integrations')}
      • +
      • ${__('Copy your username and App API key.', 'bit-integrations')}
      • +
      • ${__('Authorize and save the connection.', 'bit-integrations')}
      • +
      ` - {!isInfo && ( - <> - -
      - - - )} -
      + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailCommonFunc.js b/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailCommonFunc.js index bbe98fcbf..c32734993 100644 --- a/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailCommonFunc.js +++ b/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailCommonFunc.js @@ -14,60 +14,17 @@ export const handleInput = (e, slackConf, setSlackConf) => { setSlackConf({ ...newConf }) } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setIsAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.api_key) { - setError({ api_key: !confTmp.api_key ? __("Api Key can't be empty", 'bit-integrations') : '' }) - return - } - setError({}) - setIsLoading(true) - - const tokenRequestParams = { username: confTmp.userName, api_key: confTmp.api_key } - - bitsFetch(tokenRequestParams, 'kirimEmail_authorization') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - if (!newConf.default) { - newConf.default = {} - } - if (result.data) { - newConf.default.allLists = result.data - } - setConf(newConf) - setIsAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) +const buildAuthRequestParams = confTmp => + confTmp?.connection_id + ? { connection_id: confTmp.connection_id } + : { + userName: confTmp.userName, + api_key: confTmp.api_key } - setIsLoading(false) - }) -} export const getAllList = (kirimEmailConf, setKirimEmailConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const tokenRequestParams = { username: kirimEmailConf.userName, api_key: kirimEmailConf.api_key } + const tokenRequestParams = buildAuthRequestParams(kirimEmailConf) bitsFetch(tokenRequestParams, 'kirimEmail_fetch_all_list') .then(result => { diff --git a/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailIntegLayout.jsx b/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailIntegLayout.jsx index 62edd2010..3ba1fa2d3 100644 --- a/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/KirimEmail/KirimEmailIntegLayout.jsx @@ -1,5 +1,4 @@ import MultiSelect from 'react-multiple-select-dropdown-lite' -import { useState } from 'react' import { __ } from '../../../Utils/i18nwrap' import Loader from '../../Loaders/Loader' import { addFieldMap } from './KirimEmailIntegrationHelpers' @@ -9,15 +8,12 @@ import TableCheckBox from '../../Utilities/TableCheckBox' import { getAllList } from './KirimEmailCommonFunc' export default function KirimEmailIntegLayout({ - formID, formFields, - handleInput, kirimEmailConf, setKirimEmailConf, isLoading, setIsLoading, - setSnackbar, - a + setSnackbar }) { const inputHandler = e => { const newConf = { ...kirimEmailConf } diff --git a/frontend/src/components/AllIntegrations/Klaviyo/KlaviyoAuthorization.jsx b/frontend/src/components/AllIntegrations/Klaviyo/KlaviyoAuthorization.jsx index dd8f82ae2..bbcd62216 100644 --- a/frontend/src/components/AllIntegrations/Klaviyo/KlaviyoAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Klaviyo/KlaviyoAuthorization.jsx @@ -1,37 +1,28 @@ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { handleAuthorize } from './KlaviyoCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import Authorization from '../../Connections/Authorization' +import { getAllLists } from './KlaviyoCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' -function KlaviyoAuthorization({ - klaviyoConf, - setKlaviyoConf, - step, - setStep, - isInfo, - loading, - setLoading -}) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', authKey: '' }) - const handleInput = e => { - const newConf = { ...klaviyoConf } - const koError = { ...error } - koError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(koError) - setKlaviyoConf(newConf) - } - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) +function KlaviyoAuthorization({ klaviyoConf, setKlaviyoConf, step, setStep, isInfo }) { + const loadLists = useCallback( + connectionId => { + const nextConf = connectionId ? { ...klaviyoConf, connection_id: connectionId } : klaviyoConf + getAllLists(nextConf, setKlaviyoConf, {}, () => {}) + }, + [klaviyoConf, setKlaviyoConf] + ) - setStep(2) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !klaviyoConf?.default?.lists) { + loadLists() + } + setStep(value) + }, + [klaviyoConf, loadLists, setStep] + ) const note = `

      ${__('Step of get API Key:', 'bit-integrations')}

      @@ -53,87 +44,29 @@ function KlaviyoAuthorization({ ` return ( -
      - - -
      -
      - {__('Integration Name:', 'bit-integrations')} -
      - - - -
      - {__('API Key:', 'bit-integrations')} -
      - - - {error.authKey &&
      {error.authKey}
      } - - - {__('To get API key, please visit', 'bit-integrations')} -   - - {__('here.', 'bit-integrations')} - - - {!isInfo && ( -
      - -
      - -
      - )} - -
      -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Klaviyo/KlaviyoCommonFunc.js b/frontend/src/components/AllIntegrations/Klaviyo/KlaviyoCommonFunc.js index 367c85cec..82a34f59c 100644 --- a/frontend/src/components/AllIntegrations/Klaviyo/KlaviyoCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Klaviyo/KlaviyoCommonFunc.js @@ -16,44 +16,17 @@ export const editHandleInput = (e, conf, setConf) => { setConf({ ...newConf }) } -export const handleAuthorize = (conf, setConf, setError, setisAuthorized, loading, setLoading) => { - if (!conf.authKey) { - setError({ - authKey: !conf.authKey ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { authKey: conf.authKey } - - bitsFetch(requestParams, 'klaviyo_handle_authorize').then(result => { - if (result && result.success) { - const newConf = { ...conf } - if (result.data) { - if (!newConf.default) { - newConf.default = {} - } - newConf.default.lists = result.data - } - setConf(newConf) - setisAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed', 'bit-integrations')) - }) -} +const buildAuthRequestParams = conf => + conf.connection_id ? { connection_id: conf.connection_id } : { authKey: conf.authKey } export const getAllLists = (conf, setConf, loading, setLoading) => { - setLoading({ ...loading, list: true }) + if (typeof setLoading === 'function') { + setLoading({ ...(loading || {}), list: true }) + } - const requestParams = { authKey: conf.authKey } + const requestParams = buildAuthRequestParams(conf) - bitsFetch(requestParams, 'klaviyo_handle_authorize').then(result => { + bitsFetch(requestParams, 'klaviyo_lists').then(result => { if (result && result.success) { const newConf = { ...conf } if (result.data) { @@ -63,12 +36,16 @@ export const getAllLists = (conf, setConf, loading, setLoading) => { newConf.default.lists = result.data } setConf(newConf) - setLoading({ ...loading, list: false }) + if (typeof setLoading === 'function') { + setLoading({ ...(loading || {}), list: false }) + } toast.success(__('List refresh successfully', 'bit-integrations')) return } - setLoading({ ...loading, list: false }) + if (typeof setLoading === 'function') { + setLoading({ ...(loading || {}), list: false }) + } toast.error(__('List refresh failed', 'bit-integrations')) }) } diff --git a/frontend/src/components/AllIntegrations/LMFWC/LMFWCAuthorization.jsx b/frontend/src/components/AllIntegrations/LMFWC/LMFWCAuthorization.jsx index 9c6d09fa8..b117c8f73 100644 --- a/frontend/src/components/AllIntegrations/LMFWC/LMFWCAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/LMFWC/LMFWCAuthorization.jsx @@ -1,43 +1,17 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ /* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' -import { lmfwcAuthentication } from './LMFWCCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function LMFWCAuthorization({ licenseManagerConf, setLicenseManagerConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '', api_secret: '' }) - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - if (isAuthorized) { - setStep(2) - } - } - - const handleInput = e => { - const newConf = { ...licenseManagerConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setLicenseManagerConf(newConf) - } - const ActiveInstructions = ` ${__('Requirements', 'bit-integrations')}

      ${__('WordPress permalinks must be enabled at', 'bit-integrations')}: ${__( @@ -58,98 +32,41 @@ export default function LMFWCAuthorization({

    ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Homepage URL:', 'bit-integrations')} -
    - -
    {error.base_url}
    - -
    - {__('Consumer key:', 'bit-integrations')} -
    - -
    {error.api_key}
    - -
    - {__('Consumer secret:', 'bit-integrations')} -
    - -
    {error.api_secret}
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ({ + Authorization: `Basic ${btoa(`${authData?.api_key || ''}:${authData?.api_secret || ''}`)}`, + 'Content-Type': 'application/json' + }), + extraFields: [ + { + name: 'base_url', + label: __('Homepage URL', 'bit-integrations'), + required: true, + placeholder: __('Homepage URL...', 'bit-integrations') + }, + { + name: 'api_secret', + label: __('Consumer Secret', 'bit-integrations'), + required: true, + placeholder: __('Consumer secret...', 'bit-integrations') + } + ], + encryptKeys: ['value', 'api_secret'] + }} + noteDetails={{ note: ActiveInstructions }} + /> ) } diff --git a/frontend/src/components/AllIntegrations/LMFWC/LMFWCCommonFunc.js b/frontend/src/components/AllIntegrations/LMFWC/LMFWCCommonFunc.js index 4229ef731..3353b55c2 100644 --- a/frontend/src/components/AllIntegrations/LMFWC/LMFWCCommonFunc.js +++ b/frontend/src/components/AllIntegrations/LMFWC/LMFWCCommonFunc.js @@ -41,56 +41,22 @@ export const checkMappedFields = licenseManagerConf => { return true } -export const lmfwcAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key || !confTmp.api_secret) { - setError({ - base_url: !confTmp.base_url ? __("Consumer key can't be empty", 'bit-integrations') : '', - api_key: !confTmp.api_key ? __("Consumer key can't be empty", 'bit-integrations') : '', - api_secret: !confTmp.api_secret ? __("Consumer secret can't be empty", 'bit-integrations') : '' - }) - return +const buildAuthRequestParams = confTmp => { + if (confTmp?.connection_id) { + return { connection_id: confTmp.connection_id } } - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { + return { base_url: confTmp.base_url, api_key: confTmp.api_key, api_secret: confTmp.api_secret } - - bitsFetch(requestParams, 'lmfwc_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error( - result?.data && typeof result.data === 'string' - ? result.data - : __('Authorized failed, Please enter valid Consumer key & Consumer secret', 'bit-integrations') - ) - }) } -export const getAllCustomer = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, customer: true }) +export const getAllCustomer = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, customer: true }) - const requestParams = { - base_url: confTmp.base_url, - api_key: confTmp.api_key, - api_secret: confTmp.api_secret - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'lmfwc_fetch_all_customer').then(result => { if (result && result.success) { @@ -100,27 +66,23 @@ export const getAllCustomer = (confTmp, setConf, setLoading) => { return prevConf }) - setLoading({ ...setLoading, customer: false }) + setLoading({ ...loading, customer: false }) toast.success(__('Customers fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, customer: false }) + setLoading({ ...loading, customer: false }) toast.error(__('Customers Not Found!', 'bit-integrations')) return } - setLoading({ ...setLoading, customer: false }) + setLoading({ ...loading, customer: false }) toast.error(__('Customers fetching failed', 'bit-integrations')) }) } -export const getAllProduct = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, product: true }) +export const getAllProduct = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, product: true }) - const requestParams = { - base_url: confTmp.base_url, - api_key: confTmp.api_key, - api_secret: confTmp.api_secret - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'lmfwc_fetch_all_product').then(result => { if (result && result.success) { @@ -130,27 +92,23 @@ export const getAllProduct = (confTmp, setConf, setLoading) => { return prevConf }) - setLoading({ ...setLoading, product: false }) + setLoading({ ...loading, product: false }) toast.success(__('Product fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, product: false }) + setLoading({ ...loading, product: false }) toast.error(__('Product Not Found!', 'bit-integrations')) return } - setLoading({ ...setLoading, product: false }) + setLoading({ ...loading, product: false }) toast.error(__('Product fetching failed', 'bit-integrations')) }) } -export const getAllOrder = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, order: true }) +export const getAllOrder = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, order: true }) - const requestParams = { - base_url: confTmp.base_url, - api_key: confTmp.api_key, - api_secret: confTmp.api_secret - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'lmfwc_fetch_all_order').then(result => { if (result && result.success) { @@ -160,27 +118,23 @@ export const getAllOrder = (confTmp, setConf, setLoading) => { return prevConf }) - setLoading({ ...setLoading, order: false }) + setLoading({ ...loading, order: false }) toast.success(__('Order fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, order: false }) + setLoading({ ...loading, order: false }) toast.error(__('Order Not Found!', 'bit-integrations')) return } - setLoading({ ...setLoading, order: false }) + setLoading({ ...loading, order: false }) toast.error(__('Order fetching failed', 'bit-integrations')) }) } -export const getAllLicense = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, license: true }) +export const getAllLicense = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, license: true }) - const requestParams = { - base_url: confTmp.base_url, - api_key: confTmp.api_key, - api_secret: confTmp.api_secret - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'lmfwc_fetch_all_license').then(result => { if (result && result.success) { @@ -190,27 +144,23 @@ export const getAllLicense = (confTmp, setConf, setLoading) => { return prevConf }) - setLoading({ ...setLoading, license: false }) + setLoading({ ...loading, license: false }) toast.success(__('License fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, license: false }) + setLoading({ ...loading, license: false }) toast.error(__('License Not Found!', 'bit-integrations')) return } - setLoading({ ...setLoading, license: false }) + setLoading({ ...loading, license: false }) toast.error(__('License fetching failed', 'bit-integrations')) }) } -export const getAllGenerator = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, generator: true }) +export const getAllGenerator = (confTmp, setConf, loading, setLoading) => { + setLoading({ ...loading, generator: true }) - const requestParams = { - base_url: confTmp.base_url, - api_key: confTmp.api_key, - api_secret: confTmp.api_secret - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'lmfwc_fetch_all_generator').then(result => { if (result && result.success) { @@ -220,15 +170,15 @@ export const getAllGenerator = (confTmp, setConf, setLoading) => { return prevConf }) - setLoading({ ...setLoading, generator: false }) + setLoading({ ...loading, generator: false }) toast.success(__('Generator fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, generator: false }) + setLoading({ ...loading, generator: false }) toast.error(__('Generator Not Found!', 'bit-integrations')) return } - setLoading({ ...setLoading, generator: false }) + setLoading({ ...loading, generator: false }) toast.error(__('Generator fetching failed', 'bit-integrations')) }) } diff --git a/frontend/src/components/AllIntegrations/LMFWC/LMFWCIntegLayout.jsx b/frontend/src/components/AllIntegrations/LMFWC/LMFWCIntegLayout.jsx index 6b3d24504..770ecf252 100644 --- a/frontend/src/components/AllIntegrations/LMFWC/LMFWCIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/LMFWC/LMFWCIntegLayout.jsx @@ -38,12 +38,12 @@ export default function LMFWCIntegLayout({ draftConf[name] = val if (name === 'module' && (val === 'create_license' || val === 'update_license')) { - getAllCustomer(licenseManagerConf, setLicenseManagerConf, setLoading) - getAllProduct(licenseManagerConf, setLicenseManagerConf, setLoading) - getAllOrder(licenseManagerConf, setLicenseManagerConf, setLoading) + getAllCustomer(licenseManagerConf, setLicenseManagerConf, loading, setLoading) + getAllProduct(licenseManagerConf, setLicenseManagerConf, loading, setLoading) + getAllOrder(licenseManagerConf, setLicenseManagerConf, loading, setLoading) if (val === 'update_license') { - getAllLicense(licenseManagerConf, setLicenseManagerConf, setLoading) + getAllLicense(licenseManagerConf, setLicenseManagerConf, loading, setLoading) draftConf.lmfwcFields = [ { label: __('License key', 'bit-integrations'), key: 'license_key', required: false }, ...draftConf.generalFields @@ -68,7 +68,7 @@ export default function LMFWCIntegLayout({ draftConf.field_map = generateMappedField(draftConf.lmfwcFields) } else if (name === 'module' && (val === 'create_generator' || val === 'update_generator')) { if (val === 'update_generator') { - getAllGenerator(licenseManagerConf, setLicenseManagerConf, setLoading) + getAllGenerator(licenseManagerConf, setLicenseManagerConf, loading, setLoading) draftConf.lmfwcFields = draftConf.generatorFields.map(fields => { return { ...fields, required: false } }) @@ -131,7 +131,9 @@ export default function LMFWCIntegLayout({ closeOnSelect /> - )} - - {isAuthorized && ( - - )} - + ) } diff --git a/frontend/src/components/AllIntegrations/Lemlist/LemlistAuthorization.jsx b/frontend/src/components/AllIntegrations/Lemlist/LemlistAuthorization.jsx index 1f76c8699..6e08ff883 100644 --- a/frontend/src/components/AllIntegrations/Lemlist/LemlistAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Lemlist/LemlistAuthorization.jsx @@ -1,81 +1,36 @@ -import { useState } from 'react' -import { toast } from 'react-hot-toast' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' +import Authorization from '../../Connections/Authorization' import { refreshLemlistCampaign } from './LemlistCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' export default function LemlistAuthorization({ - lemlistConf, + lemlistConf = {}, setLemlistConf, step, - setstep, + setstep = () => {}, setSnackbar, isInfo, - isLoading, setIsLoading }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_key: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const handleAuthorize = () => { - const newConf = { ...lemlistConf } - if (!newConf.name || !newConf.api_key) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - api_key: !newConf.api_key ? __("Access Api Key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading('auth') - - const data = { - api_key: newConf.api_key - } + const loadCampaigns = useCallback( + connectionId => { + const nextConf = connectionId ? { ...lemlistConf, connection_id: connectionId } : lemlistConf + refreshLemlistCampaign(nextConf, setLemlistConf, setIsLoading, setSnackbar) + }, + [lemlistConf, setIsLoading, setLemlistConf, setSnackbar] + ) - bitsFetch(data, 'lemlist_authorize').then(result => { - if (result && result.success) { - const newConf = { ...lemlistConf } - newConf.tokenDetails = result.data - setLemlistConf(newConf) - setisAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - toast.error( - `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - ) - } else { - toast.error(__('Authorization failed. please try again', 'bit-integrations')) + const handleSetStep = useCallback( + value => { + if (value === 2 && !lemlistConf?.default?.lemlistCampaigns) { + loadCampaigns() } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...lemlistConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setLemlistConf(newConf) - } - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - refreshLemlistCampaign(lemlistConf, setLemlistConf, setIsLoading, setSnackbar) - setstep(2) - } + setstep(value) + }, + [lemlistConf, loadCampaigns, setstep] + ) const ActiveInstructions = `

    ${__('Get Api key', 'bit-integrations')}

    @@ -87,89 +42,24 @@ export default function LemlistAuthorization({ ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - -
    {error.name}
    - -
    - {__('Access API Key:', 'bit-integrations')} -
    - -
    {error.api_key}
    - - - {__('To get API key, please visit', 'bit-integrations')} -   - - {__('Lemlist API Token', 'bit-integrations')} - - -
    -
    - {isLoading === 'auth' && ( -
    - - {__('Checking API Key!!!', 'bit-integrations')} -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {__('Sorry, Api key is invalid', 'bit-integrations')} -
    - )} - {!isInfo && ( - <> - -
    - - - )} - -
    + ({ Authorization: `Basic ${btoa(`:${data?.api_key || ''}`)}` }) + }} + noteDetails={{ note: ActiveInstructions }} + onConnectionSelected={loadCampaigns} + /> ) } diff --git a/frontend/src/components/AllIntegrations/Lemlist/LemlistCommonFunc.js b/frontend/src/components/AllIntegrations/Lemlist/LemlistCommonFunc.js index ab618e82a..c4ad8fd3d 100644 --- a/frontend/src/components/AllIntegrations/Lemlist/LemlistCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Lemlist/LemlistCommonFunc.js @@ -8,12 +8,13 @@ export const handleInput = (e, lemlistConf, setLemlistConf) => { setLemlistConf({ ...newConf }) } +const buildAuthRequestParams = conf => + conf?.connection_id ? { connection_id: conf.connection_id } : { api_key: conf.api_key } + // refreshMappedLists export const refreshLemlistCampaign = (lemlistConf, setLemlistConf, setIsLoading, setSnackbar) => { - const refreshListsRequestParams = { - account_id: lemlistConf.account_id, - api_key: lemlistConf.api_key - } + if (typeof setIsLoading === 'function') setIsLoading(true) + const refreshListsRequestParams = buildAuthRequestParams(lemlistConf) bitsFetch(refreshListsRequestParams, 'lemlist_campaigns') .then(result => { if (result && result.success) { @@ -44,9 +45,11 @@ export const refreshLemlistCampaign = (lemlistConf, setLemlistConf, setIsLoading msg: __('Lemlist campaigns refresh failed. please try again', 'bit-integrations') }) } - setIsLoading(false) + if (typeof setIsLoading === 'function') setIsLoading(false) + }) + .catch(() => { + if (typeof setIsLoading === 'function') setIsLoading(false) }) - .catch(() => setIsLoading(false)) } // refreshMappedFields @@ -83,7 +86,7 @@ export const refreshLemlistHeader = (lemlistConf, setLemlistConf, setIsLoading, msg: __('Lemlist fields refreshed', 'bit-integrations') }) setLemlistConf({ ...newConf }) - setIsLoading(false) + if (typeof setIsLoading === 'function') setIsLoading(false) } export const checkMappedFields = lemlistConf => { diff --git a/frontend/src/components/AllIntegrations/LifterLms/LifterLmsAuthorization.jsx b/frontend/src/components/AllIntegrations/LifterLms/LifterLmsAuthorization.jsx index 4eb989551..f2b1c4d73 100644 --- a/frontend/src/components/AllIntegrations/LifterLms/LifterLmsAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/LifterLms/LifterLmsAuthorization.jsx @@ -1,103 +1,38 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function LifterLmsAuthorization({ - formID, lifterLmsConf, setLifterLmsConf, step, setStep, - isLoading, - setIsLoading, - setSnackbar + isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'lifterLms_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with LifterLms Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(lifterLmsConf) - newConf[e.target.name] = e.target.value - setLifterLmsConf(newConf) - } - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - {isLoading === 'auth' && ( -
    - - Checking if LifterLms is active!!! -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'LifterLms' - )} -
    - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Line/Line.jsx b/frontend/src/components/AllIntegrations/Line/Line.jsx index 5dff1e24e..647cef12c 100644 --- a/frontend/src/components/AllIntegrations/Line/Line.jsx +++ b/frontend/src/components/AllIntegrations/Line/Line.jsx @@ -1,6 +1,6 @@ import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' -import { useNavigate, useParams } from 'react-router' +import { useNavigate } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' @@ -9,7 +9,6 @@ import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import LineAuthorization from './LineAuthorization' import { generateMappedField, - handleInput, validateLineConfiguration, getLineValidationMessages } from './LineCommonFunc' @@ -79,7 +78,6 @@ const messageField = [{ label: __('Message', 'bit-integrations'), value: 'messag function Line({ formFields, setFlow, flow, allIntegURL }) { const navigate = useNavigate() - const { formID } = useParams() const [isLoading, setIsLoading] = useState(false) const [step, setstep] = useState(1) const [snack, setSnackbar] = useState({ show: false }) @@ -151,16 +149,7 @@ function Line({ formFields, setFlow, flow, allIntegURL }) { {/* STEP 1 */} - + {/* STEP 2 */}
    handleInput(e, lineConf, setLineConf, setIsLoading, setSnackbar)} lineConf={lineConf} setLineConf={setLineConf} isLoading={isLoading} diff --git a/frontend/src/components/AllIntegrations/Line/LineAuthorization.jsx b/frontend/src/components/AllIntegrations/Line/LineAuthorization.jsx index 161cc482a..6c91b3255 100644 --- a/frontend/src/components/AllIntegrations/Line/LineAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Line/LineAuthorization.jsx @@ -1,121 +1,26 @@ -import { useState } from 'react' +/* eslint-disable jsx-a11y/anchor-is-valid */ +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { handleAuthorize } from './LineCommonFunc' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function LineAuthorization({ - formID, - lineConf, - setLineConf, - step, - setstep, - isLoading, - setIsLoading, - setSnackbar, - redirectLocation, - isInfo -}) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ accessToken: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - setstep(2) - } - - const handleInput = e => { - const newConf = { ...lineConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setLineConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function LineAuthorization({ lineConf, setLineConf, step, setstep, isInfo }) { return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - - {__('To get access Token , Please Visit', 'bit-integrations')}{' '} - - {__('Line Console', 'bit-integrations')} - - - -
    - {__('Access Token:', 'bit-integrations')} -
    - -
    {error.accessToken}
    - - {!isInfo && ( - <> - -
    - - - )} - - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Line/LineCommonFunc.js b/frontend/src/components/AllIntegrations/Line/LineCommonFunc.js index f5b1dc63a..b86bdb70a 100644 --- a/frontend/src/components/AllIntegrations/Line/LineCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Line/LineCommonFunc.js @@ -1,63 +1,10 @@ import { __ } from '../../../Utils/i18nwrap' -import bitsFetch from '../../../Utils/bitsFetch' export const handleInput = (e, lineConf, setLineConf) => { const { name, value } = e.target setLineConf(prev => ({ ...prev, [name]: value })) } -export const handleAuthorize = async ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.accessToken) { - setError({ accessToken: __("Access Token can't be empty", 'bit-integrations') }) - return - } - - setError({}) - setIsLoading(true) - - try { - const result = await bitsFetch({ accessToken: confTmp.accessToken }, 'line_authorization') - - if (result?.success) { - setConf({ ...confTmp, tokenDetails: result.data }) - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else { - const msg = result?.data?.data - ? `${__('Authorization failed Cause:', 'bit-integrations')} ${result.data.data}. ${__( - 'Please try again', - 'bit-integrations' - )}` - : typeof result?.data === 'string' - ? `${__('Authorization failed Cause:', 'bit-integrations')} ${result.data}. ${__( - 'Please try again', - 'bit-integrations' - )}` - : __('Authorization failed. Please try again', 'bit-integrations') - - setSnackbar({ show: true, msg }) - } - - setIsLoading(false) - } catch (error) { - setSnackbar({ - show: true, - msg: `${__('An error occurred during authorization:', 'bit-integrations')} ${ - error?.message || error - }` - }) - - setIsLoading(false) - } -} - const updateFieldMap = (prevConf, type, index, updater) => { const newConf = { ...prevConf } diff --git a/frontend/src/components/AllIntegrations/LionDesk/LionDesk.jsx b/frontend/src/components/AllIntegrations/LionDesk/LionDesk.jsx index 6a237cbfe..7a03f35f7 100644 --- a/frontend/src/components/AllIntegrations/LionDesk/LionDesk.jsx +++ b/frontend/src/components/AllIntegrations/LionDesk/LionDesk.jsx @@ -1,6 +1,6 @@ /* eslint-disable no-console */ /* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import toast from 'react-hot-toast' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate } from 'react-router' @@ -10,7 +10,7 @@ import Steps from '../../Utilities/Steps' import { saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import LionDeskAuthorization from './LionDeskAuthorization' -import { checkMappedFields, handleInput, setGrantTokenResponse } from './LionDeskCommonFunc' +import { checkMappedFields, handleInput } from './LionDeskCommonFunc' import LionDeskIntegLayout from './LionDeskIntegLayout' function LionDesk({ formFields, setFlow, flow, allIntegURL }) { @@ -58,10 +58,6 @@ function LionDesk({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('lionDesk') - }, []) - const saveConfig = () => { setIsLoading(true) const resp = saveIntegConfig( @@ -110,9 +106,6 @@ function LionDesk({ formFields, setFlow, flow, allIntegURL }) { setLionDeskConf={setLionDeskConf} step={step} setStep={setStep} - setSnackbar={setSnackbar} - isLoading={isLoading} - setIsLoading={setIsLoading} /> {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/LionDesk/LionDeskAuthorization.jsx b/frontend/src/components/AllIntegrations/LionDesk/LionDeskAuthorization.jsx index 4fa98e1c4..b037c4797 100644 --- a/frontend/src/components/AllIntegrations/LionDesk/LionDeskAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/LionDesk/LionDeskAuthorization.jsx @@ -1,162 +1,45 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' -import { $appConfigState } from '../../../GlobalStates' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import Note from '../../Utilities/Note' -import { handleAuthorize } from './LionDeskCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' -export default function LionDeskAuthorization({ - lionDeskConf, - setLionDeskConf, - step, - setStep, - setSnackbar, - isLoading, - setIsLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ session_token: '' }) - const btcbi = useRecoilValue($appConfigState) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !lionDeskConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...lionDeskConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setLionDeskConf(newConf) - } - - const ActiveInstructions = ` -

    ${__('Get the Redirect URI, Client Id and Client Secret', 'bit-integrations')}

    -
      -
    • ${__('First go to your Lion Desk Developer Center Apps.', 'bit-integrations')}
    • -
    • ${__('Then Click "New App+" from Right in the middle', 'bit-integrations')}
    • -
    • ${__('Then input the "Name and Redirect URI" then save', 'bit-integrations')}
    • -
    • ${__( - 'Then click "REVEAL CLIENT ID" and "REVEAL CLIENT SECRET", Then Copied', - 'bit-integrations' - )}
    • -
    ` +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function LionDeskAuthorization({ lionDeskConf, setLionDeskConf, step, setStep, isInfo }) { + const note = ` +

    ${__('Get Redirect URI, Client ID and Client Secret', 'bit-integrations')}

    +
      +
    • ${__('Go to LionDesk Developer Center Apps.', 'bit-integrations')}
    • +
    • ${__('Create a new app and set redirect URI from this form.', 'bit-integrations')}
    • +
    • ${__('Copy client ID and client secret from LionDesk app.', 'bit-integrations')}
    • +
    • ${__('Authorize to complete connection.', 'bit-integrations')}
    • +
    + ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - - -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    - -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    - - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')} -   - - {__('Lion Desk Apps', 'bit-integrations')} - - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/LionDesk/LionDeskCommonFunc.js b/frontend/src/components/AllIntegrations/LionDesk/LionDeskCommonFunc.js index 977b6bb65..b695c31e1 100644 --- a/frontend/src/components/AllIntegrations/LionDesk/LionDeskCommonFunc.js +++ b/frontend/src/components/AllIntegrations/LionDesk/LionDeskCommonFunc.js @@ -47,156 +47,32 @@ export const checkMappedFields = lionDeskConf => { return true } -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation - .replace(`${window.opener.location.href}/redirect`, '') - .split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + tokenDetails: conf.tokenDetails, + clientId: conf.clientId, + clientSecret: conf.clientSecret, + redirectURI: conf.redirectURI } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} - -export const handleAuthorize = ( - integ, - ajaxInteg, - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty", 'bit-integrations') : '' - }) - return - } - - setIsLoading(true) - const apiEndpoint = `https://api-v2.liondesk.com/oauth2/authorize?response_type=code&client_id=${ - confTmp.clientId - }&state=${encodeURIComponent(window.location.href)}/redirect&redirect_uri=${encodeURIComponent( - `${btcbi.api}` - )}/redirect&scope=['write','read']` - const authWindow = window.open(apiEndpoint, integ, 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitintegrationLionDesk = localStorage.getItem(`__${integ}`) - - if (bitintegrationLionDesk) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitintegrationLionDesk) - localStorage.removeItem(`__${integ}`) - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper( - ajaxInteg, - grantTokenResponse, - newConf, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi - ) - } - } - }, 500) -} - -const tokenHelper = ( - ajaxInteg, - grantToken, - confTmp, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - - bitsFetch(tokenRequestParams, `${ajaxInteg}_generate_token`) - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} -export const getCustomFields = (confTmp, setConf, setIsLoading, btcbi) => { +export const getCustomFields = (confTmp, setConf, setIsLoading) => { setIsLoading(true) const requestParams = { - token_details: confTmp.tokenDetails, - client_id: confTmp.clientId, - client_secret: confTmp.clientSecret, - redirect_uri: `${btcbi.api}/redirect` + ...buildAuthRequestParams(confTmp) } bitsFetch(requestParams, 'lionDesk_fetch_custom_fields').then(result => { if (result && result.success) { setIsLoading(false) - if (result.data) { + if (result.data?.customFields) { setConf(prevConf => { const newConf = { ...prevConf } - newConf.customFields = result.data + newConf.customFields = result.data.customFields + if (result.data.tokenDetails) { + newConf.tokenDetails = result.data.tokenDetails + } return newConf }) toast.success(__('Custom fields also fetched successfully', 'bit-integrations')) @@ -213,19 +89,19 @@ export const getCustomFields = (confTmp, setConf, setIsLoading, btcbi) => { export const getAllTags = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, tags: true }) const requestParams = { - token_details: confTmp.tokenDetails, - client_id: confTmp.clientId, - client_secret: confTmp.clientSecret, - redirect_uri: confTmp.redirectURI + ...buildAuthRequestParams(confTmp) } bitsFetch(requestParams, 'lionDesk_fetch_all_tags').then(result => { if (result && result.success) { setLoading({ ...setLoading, tags: false }) - if (result.data) { + if (result.data?.tags) { setConf(prevConf => { const newConf = { ...prevConf } - newConf.tags = result.data + newConf.tags = result.data.tags + if (result.data.tokenDetails) { + newConf.tokenDetails = result.data.tokenDetails + } return newConf }) toast.success(__('Tags fetched successfully', 'bit-integrations')) diff --git a/frontend/src/components/AllIntegrations/LionDesk/LionDeskIntegLayout.jsx b/frontend/src/components/AllIntegrations/LionDesk/LionDeskIntegLayout.jsx index 4805008fd..947fad04b 100644 --- a/frontend/src/components/AllIntegrations/LionDesk/LionDeskIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/LionDesk/LionDeskIntegLayout.jsx @@ -1,7 +1,5 @@ /* eslint-disable no-unused-vars */ import 'react-multiple-select-dropdown-lite/dist/index.css' -import { useRecoilValue } from 'recoil' -import { $appConfigState } from '../../../GlobalStates' import { __ } from '../../../Utils/i18nwrap' import Loader from '../../Loaders/Loader' import { addFieldMap } from './IntegrationHelpers' @@ -20,7 +18,6 @@ export default function LionDeskIntegLayout({ setIsLoading, setSnackbar }) { - const btcbi = useRecoilValue($appConfigState) const handleActionInput = e => { const newConf = { ...lionDeskConf } const { name } = e.target @@ -29,7 +26,7 @@ export default function LionDeskIntegLayout({ if (e.target.value !== '') { newConf[name] = e.target.value if (e.target.value === 'contact') { - getCustomFields(newConf, setLionDeskConf, setIsLoading, btcbi) + getCustomFields(newConf, setLionDeskConf, setIsLoading) } } else { delete newConf[name] @@ -85,7 +82,7 @@ export default function LionDeskIntegLayout({ {__('Field Map', 'bit-integrations')} {lionDeskConf.actionName === 'contact' && ( -
    - -
    - )} - - + ) } diff --git a/frontend/src/components/AllIntegrations/Livestorm/LivestormCommonFunc.js b/frontend/src/components/AllIntegrations/Livestorm/LivestormCommonFunc.js index c1e170378..3c08f5bc0 100644 --- a/frontend/src/components/AllIntegrations/Livestorm/LivestormCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Livestorm/LivestormCommonFunc.js @@ -41,40 +41,12 @@ export const checkMappedFields = livestormConf => { return true } -export const livestormAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - const requestParams = { api_key: confTmp.api_key } - - bitsFetch(requestParams, 'livestorm_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__(result?.data || 'Authorized failed, Please enter valid API Key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = conf => + conf?.connection_id ? { connection_id: conf.connection_id } : { api_key: conf.api_key } -export const getAllEvents = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, event: true }) - const requestParams = { api_key: confTmp.api_key } +export const getAllEvents = (confTmp, setConf, loading, setLoading, setSnackbar = null) => { + setLoading({ ...loading, event: true }) + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'livestorm_fetch_all_events').then(result => { if (result && result.success) { @@ -85,24 +57,33 @@ export const getAllEvents = (confTmp, setConf, setLoading) => { return prevConf }) - setLoading({ ...setLoading, event: false }) + setLoading({ ...loading, event: false }) toast.success(__('Events fetched successfully', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Events fetched successfully', 'bit-integrations') }) + } return } - setLoading({ ...setLoading, event: false }) + setLoading({ ...loading, event: false }) toast.error(__('Events Not Found!', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Events Not Found!', 'bit-integrations') }) + } return } - setLoading({ ...setLoading, event: false }) + setLoading({ ...loading, event: false }) toast.error(__('Events fetching failed', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Events fetching failed', 'bit-integrations') }) + } }) } -export const getAllSessions = (confTmp, setConf, event_id, setLoading) => { - setLoading({ ...setLoading, session: true }) +export const getAllSessions = (confTmp, setConf, event_id, loading, setLoading, setSnackbar = null) => { + setLoading({ ...loading, session: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), event_id: event_id } @@ -114,15 +95,24 @@ export const getAllSessions = (confTmp, setConf, event_id, setLoading) => { return prevConf }) - setLoading({ ...setLoading, session: false }) + setLoading({ ...loading, session: false }) toast.success(__('Sessions fetched successfully', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Sessions fetched successfully', 'bit-integrations') }) + } return } - setLoading({ ...setLoading, session: false }) + setLoading({ ...loading, session: false }) toast.error(__('Sessions Not Found!', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Sessions Not Found!', 'bit-integrations') }) + } return } - setLoading({ ...setLoading, session: false }) + setLoading({ ...loading, session: false }) toast.error(__('Sessions fetching failed', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Sessions fetching failed', 'bit-integrations') }) + } }) } diff --git a/frontend/src/components/AllIntegrations/Livestorm/LivestormIntegLayout.jsx b/frontend/src/components/AllIntegrations/Livestorm/LivestormIntegLayout.jsx index 1534a6e9a..265cdd393 100644 --- a/frontend/src/components/AllIntegrations/Livestorm/LivestormIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/Livestorm/LivestormIntegLayout.jsx @@ -19,7 +19,7 @@ export default function LivestormIntegLayout({ }) { const setChanges = (val, name) => { if (name === 'selectedEvent' && val !== '') { - getAllSessions(livestormConf, setLivestormConf, val, setLoading) + getAllSessions(livestormConf, setLivestormConf, val, loading, setLoading, setSnackbar) } setLivestormConf(prevConf => { @@ -55,7 +55,9 @@ export default function LivestormIntegLayout({ closeOnSelect /> -
    - - - )} - - + ) } diff --git a/frontend/src/components/AllIntegrations/MailBluster/MailBlusterCommonFunc.js b/frontend/src/components/AllIntegrations/MailBluster/MailBlusterCommonFunc.js index 944486969..3c9d02315 100644 --- a/frontend/src/components/AllIntegrations/MailBluster/MailBlusterCommonFunc.js +++ b/frontend/src/components/AllIntegrations/MailBluster/MailBlusterCommonFunc.js @@ -37,56 +37,53 @@ export const checkMappedFields = mailBlusterConf => { return true } -export const mailBlusterAuthentication = ( +const buildAuthRequestParams = confTmp => + confTmp.connection_id ? { connection_id: confTmp.connection_id } : { auth_token: confTmp.auth_token } + +export const fetchCustomFields = ( confTmp, setConf, setError, setIsAuthorized, loading, setLoading, - type + type = 'authentication' ) => { - if (!confTmp.auth_token) { - setError({ + if (!confTmp.connection_id && !confTmp.auth_token) { + setError?.({ auth_token: !confTmp.auth_token ? __("Api Key can't be empty", 'bit-integrations') : '' }) return } - setError({}) + setError?.({}) if (type === 'authentication') { setLoading({ ...loading, auth: true }) } + if (type === 'refreshCustomFields') { setLoading({ ...loading, customFields: true }) } - const requestParams = { auth_token: confTmp.auth_token } - bitsFetch(requestParams, 'mailBluster_authentication').then(result => { + bitsFetch(buildAuthRequestParams(confTmp), 'mailBluster_fetch_custom_fields').then(result => { if (result && result.success) { const newConf = { ...confTmp } - if (result.data) { - newConf.campaigns = result.data - } + newConf.customFields = result.data || [] setConf(newConf) - setIsAuthorized(true) + setIsAuthorized?.(true) + if (type === 'authentication') { - if (result.data) { - newConf.customFields = result.data - } setLoading({ ...loading, auth: false }) toast.success(__('Authorized Successfully', 'bit-integrations')) } else if (type === 'refreshCustomFields') { - if (result.data) { - newConf.customFields = result.data - } setLoading({ ...loading, customFields: false }) - toast.success(__('Custom fields fectched successfully', 'bit-integrations')) + toast.success(__('Custom fields fetched successfully', 'bit-integrations')) } return } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed', 'bit-integrations')) + + setLoading({ ...loading, auth: false, customFields: false }) + toast.error(__('Authorization failed', 'bit-integrations')) }) } diff --git a/frontend/src/components/AllIntegrations/MailBluster/MailBlusterIntegLayout.jsx b/frontend/src/components/AllIntegrations/MailBluster/MailBlusterIntegLayout.jsx index a486881d8..d2573a539 100644 --- a/frontend/src/components/AllIntegrations/MailBluster/MailBlusterIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/MailBluster/MailBlusterIntegLayout.jsx @@ -1,8 +1,7 @@ -import { useState } from 'react' import { __ } from '../../../Utils/i18nwrap' import Loader from '../../Loaders/Loader' import MailBlusterActions from './MailBlusterActions' -import { mailBlusterAuthentication } from './MailBlusterCommonFunc' +import { fetchCustomFields } from './MailBlusterCommonFunc' import MailBlusterFieldMap from './MailBlusterFieldMap' import { addFieldMap } from './IntegrationHelpers' @@ -15,9 +14,6 @@ export default function MailBlusterIntegLayout({ setLoading, setSnackbar }) { - const [error, setError] = useState({ name: '', auth_token: '' }) - const [isAuthorized, setIsAuthorized] = useState(false) - return ( <>
    @@ -51,11 +47,11 @@ export default function MailBlusterIntegLayout({ {__('Field Map', 'bit-integrations')} -
    - - - )} - + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/MailChimp/MailChimpCommonFunc.js b/frontend/src/components/AllIntegrations/MailChimp/MailChimpCommonFunc.js index be27fe405..9ec23f989 100644 --- a/frontend/src/components/AllIntegrations/MailChimp/MailChimpCommonFunc.js +++ b/frontend/src/components/AllIntegrations/MailChimp/MailChimpCommonFunc.js @@ -1,8 +1,16 @@ import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' import { __, sprintf } from '../../../Utils/i18nwrap' import { create } from 'mutative' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const handleInput = ( e, mailChimpConf, @@ -83,9 +91,7 @@ export const refreshAudience = (formID, mailChimpConf, setMailChimpConf, setIsLo setIsLoading(true) const refreshModulesRequestParams = { formID, - clientId: mailChimpConf.clientId, - clientSecret: mailChimpConf.clientSecret, - tokenDetails: mailChimpConf.tokenDetails + ...buildAuthRequestParams(mailChimpConf) } bitsFetch(refreshModulesRequestParams, 'mChimp_refresh_audience') .then(result => { @@ -140,9 +146,7 @@ export const refreshTags = ( setLoading({ ...loading, tags: true }) const refreshModulesRequestParams = { formID, - clientId: mailChimpConf.clientId, - clientSecret: mailChimpConf.clientSecret, - tokenDetails: mailChimpConf.tokenDetails, + ...buildAuthRequestParams(mailChimpConf), listId: mailChimpConf.listId } bitsFetch(refreshModulesRequestParams, 'mChimp_refresh_tags') @@ -199,9 +203,7 @@ export const refreshFields = ( formID, listId, module: mailChimpConf?.module, - clientId: mailChimpConf.clientId, - clientSecret: mailChimpConf.clientSecret, - tokenDetails: mailChimpConf.tokenDetails + ...buildAuthRequestParams(mailChimpConf) } bitsFetch(refreshSpreadsheetsRequestParams, 'mChimp_refresh_fields') .then(result => { @@ -237,139 +239,6 @@ export const refreshFields = ( .catch(() => setLoading({ ...loading, refreshFields: false })) } -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation.replace(`${window.opener.location.href}`, '').split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] - } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} - -export const handleMailChimpAuthorize = ( - integ, - ajaxInteg, - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - - const apiEndpoint = `https://login.mailchimp.com/oauth2/authorize?client_id=${ - confTmp.clientId - }&redirect_uri=${encodeURIComponent(window.location.href)}&response_type=code` - const authWindow = window.open(apiEndpoint, integ, 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitsMailChimp = localStorage.getItem(`__${integ}`) - if (bitsMailChimp) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsMailChimp) - localStorage.removeItem(`__${integ}`) - if (grantTokenResponse.code.search('#')) { - const [code] = grantTokenResponse.code.split('#') - grantTokenResponse.code = code - } - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper( - ajaxInteg, - grantTokenResponse, - newConf, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar - ) - } - } - }, 500) -} - -const tokenHelper = ( - ajaxInteg, - grantToken, - confTmp, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - tokenRequestParams.redirectURI = window.location.href - - bitsFetch(tokenRequestParams, `${ajaxInteg}_generate_token`) - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Authorized Successfully', 'bit-integrations') - }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} - export const checkMappedFields = mailChimpConf => { const mappedFleld = mailChimpConf.field_map ? mailChimpConf.field_map.filter(mapped => !mapped.formField || !mapped.mailChimpField) diff --git a/frontend/src/components/AllIntegrations/MailMint/MailMintAuthorization.jsx b/frontend/src/components/AllIntegrations/MailMint/MailMintAuthorization.jsx index 3ccba5376..f037cc595 100644 --- a/frontend/src/components/AllIntegrations/MailMint/MailMintAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/MailMint/MailMintAuthorization.jsx @@ -1,107 +1,49 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { getAllList, getAllTags, mailMintRefreshFields } from './MailMintCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllList, getAllTags } from './MailMintCommonFunc' export default function MailMintAuthorization({ - formID, mailMintConf, setMailMintConf, step, setStep, - isLoading, + isInfo, setIsLoading, setSnackbar }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'mailmint_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Mail Mint Successfully', 'bit-integrations') - }) + const handleSetStep = useCallback( + value => { + if (value === 2) { + getAllList(mailMintConf, setMailMintConf, setIsLoading, setSnackbar) + getAllTags(mailMintConf, setMailMintConf, setIsLoading, setSnackbar) } - setIsLoading(false) - setShowAuthMsg(true) - // mailMintRefreshFields(mailMintConf, setMailMintConf, setIsLoading, setSnackbar) - getAllList(mailMintConf, setMailMintConf, setIsLoading, setSnackbar) - getAllTags(mailMintConf, setMailMintConf, setIsLoading, setSnackbar) - }) - } - - const handleInput = e => { - const newConf = deepCopy(mailMintConf) - newConf[e.target.name] = e.target.value - setMailMintConf(newConf) - } + setStep(value) + }, + [setStep, mailMintConf, setMailMintConf, setIsLoading, setSnackbar] + ) return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - {isLoading === 'auth' && ( -
    - - {__('Checking if Mail Mint is active!!!', 'bit-integrations')} -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'Mail Mint' - )} -
    - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/MailPoet/MailPoetAuthorization.jsx b/frontend/src/components/AllIntegrations/MailPoet/MailPoetAuthorization.jsx index aba76d46a..7169bfb70 100644 --- a/frontend/src/components/AllIntegrations/MailPoet/MailPoetAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/MailPoet/MailPoetAuthorization.jsx @@ -1,93 +1,36 @@ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import bitsFetch from '../../../Utils/bitsFetch' -import LoaderSm from '../../Loaders/LoaderSm' -import BackIcn from '../../../Icons/BackIcn' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function MailPoetAuthorization({ - formID, mailPoetConf, setMailPoetConf, step, nextPage, - setSnackbar, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ integrationName: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const handleAuthorize = () => { - setIsLoading('auth') - bitsFetch({}, 'mail_poet_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...mailPoetConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setMailPoetConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - {isLoading === 'auth' && ( -
    - - {__('Checking if MailPoet is active!!!', 'bit-integrations')} -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {__('Please! First Install Mailpoet Plugins', 'bit-integrations')} -
    - )} - -
    - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/MailRelay/MailRelayActions.jsx b/frontend/src/components/AllIntegrations/MailRelay/MailRelayActions.jsx index 365966eae..5058fd1ea 100644 --- a/frontend/src/components/AllIntegrations/MailRelay/MailRelayActions.jsx +++ b/frontend/src/components/AllIntegrations/MailRelay/MailRelayActions.jsx @@ -9,13 +9,19 @@ import 'react-multiple-select-dropdown-lite/dist/index.css' import Loader from '../../Loaders/Loader' import { getAllGroups } from './MailRelayCommonFunc' -export default function MailRelayActions({ mailRelayConf, setMailRelayConf, loading, setLoading }) { +export default function MailRelayActions({ + mailRelayConf, + setMailRelayConf, + loading, + setLoading, + setSnackbar +}) { const [actionMdl, setActionMdl] = useState({ show: false, action: () => {} }) const actionHandler = (e, type) => { const newConf = { ...mailRelayConf } if (type === 'group') { if (e.target?.checked) { - getAllGroups(mailRelayConf, setMailRelayConf, setLoading) + getAllGroups(mailRelayConf, setMailRelayConf, loading, setLoading, setSnackbar) newConf.actions.groups = true } else { setActionMdl({ show: false }) @@ -101,7 +107,9 @@ export default function MailRelayActions({ mailRelayConf, setMailRelayConf, load onChange={val => setChanges(val)} /> -
    - - - )} - + ${__('Example domain:', 'bit-integrations')} bitapps${ + domain + ? `${__('To get API token, visit', 'bit-integrations')} ${__('MailRelay API Token', 'bit-integrations')}` + : '' + }` + }} + onConnectionSelected={loadCustomFields} + /> ) } diff --git a/frontend/src/components/AllIntegrations/MailRelay/MailRelayCommonFunc.js b/frontend/src/components/AllIntegrations/MailRelay/MailRelayCommonFunc.js index 9f3a21589..b231ed97b 100644 --- a/frontend/src/components/AllIntegrations/MailRelay/MailRelayCommonFunc.js +++ b/frontend/src/components/AllIntegrations/MailRelay/MailRelayCommonFunc.js @@ -37,70 +37,46 @@ export const checkMappedFields = mailRelayConf => { return true } -export const mailRelayAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading, - type -) => { - if (!confTmp.domain) { - setError({ - domain: !confTmp.domain ? __("Account Name can't be empty", 'bit-integrations') : '' - }) - return - } - if (!confTmp.auth_token) { - setError({ - auth_token: !confTmp.auth_token ? __("Api Key can't be empty", 'bit-integrations') : '' - }) - return - } +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + auth_token: conf?.auth_token || conf?.api_key, + domain: conf?.domain + } - setError({}) +export const refreshCustomFields = (confTmp, setConf, loading, setLoading, setSnackbar = null) => { + setLoading({ ...loading, customFields: true }) - if (type === 'authentication') { - setLoading({ ...loading, auth: true }) - } - if (type === 'refreshCustomFields') { - setLoading({ ...loading, customFields: true }) - } - const requestParams = { auth_token: confTmp.auth_token, domain: confTmp.domain } + const requestParams = buildAuthRequestParams(confTmp) - bitsFetch(requestParams, 'mailRelay_authentication').then(result => { + bitsFetch(requestParams, 'mailRelay_fetch_custom_fields').then(result => { if (result && result.success) { const newConf = { ...confTmp } if (result.data) { - newConf.campaigns = result.data + newConf.customFields = result.data } setConf(newConf) - setIsAuthorized(true) - if (type === 'authentication') { - if (result.data) { - newConf.customFields = result.data - } - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if (type === 'refreshCustomFields') { - if (result.data) { - newConf.customFields = result.data - } - setLoading({ ...loading, customFields: false }) - toast.success(__('Custom fields fectched successfully', 'bit-integrations')) + setLoading({ ...loading, customFields: false }) + toast.success(__('Custom fields fetched successfully', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Custom fields fetched successfully', 'bit-integrations') }) } return } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid domain name & API key', 'bit-integrations')) + + setLoading({ ...loading, customFields: false }) + toast.error(__('Custom fields fetch failed', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Custom fields fetch failed', 'bit-integrations') }) + } }) } -export const getAllGroups = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, groups: true }) +export const getAllGroups = (confTmp, setConf, loading, setLoading, setSnackbar = null) => { + setLoading({ ...loading, groups: true }) - const requestParams = { auth_token: confTmp.auth_token, domain: confTmp.domain } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'mailRelay_fetch_all_groups').then(result => { if (result && result.success) { @@ -109,12 +85,18 @@ export const getAllGroups = (confTmp, setConf, setLoading) => { newConf.groups = result.data } setConf(newConf) - setLoading({ ...setLoading, groups: false }) + setLoading({ ...loading, groups: false }) toast.success(__('Groups fetch successfully', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Groups fetch successfully', 'bit-integrations') }) + } return } - setLoading({ ...setLoading, groups: false }) + setLoading({ ...loading, groups: false }) toast.error(__('Groups fetch failed', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Groups fetch failed', 'bit-integrations') }) + } }) } diff --git a/frontend/src/components/AllIntegrations/MailRelay/MailRelayIntegLayout.jsx b/frontend/src/components/AllIntegrations/MailRelay/MailRelayIntegLayout.jsx index ef8797a98..54699cbc2 100644 --- a/frontend/src/components/AllIntegrations/MailRelay/MailRelayIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/MailRelay/MailRelayIntegLayout.jsx @@ -1,8 +1,7 @@ -import { useState } from 'react' import { __ } from '../../../Utils/i18nwrap' import Loader from '../../Loaders/Loader' import MailRelayActions from './MailRelayActions' -import { mailRelayAuthentication } from './MailRelayCommonFunc' +import { refreshCustomFields } from './MailRelayCommonFunc' import MailRelayFieldMap from './MailRelayFieldMap' import { addFieldMap } from './IntegrationHelpers' import Note from '../../Utilities/Note' @@ -16,9 +15,6 @@ export default function MailRelayIntegLayout({ setLoading, setSnackbar }) { - const [error, setError] = useState({ name: '', auth_token: '' }) - const [isAuthorized, setIsAuthorized] = useState(false) - const info = `

    ${__( 'Phone numbers from the following countries will work only in the Mailrelay SMS Phone number field:', 'bit-integrations' @@ -64,15 +60,7 @@ export default function MailRelayIntegLayout({ {__('Field Map', 'bit-integrations')} -
    - - - )} - - + + data?.version === 'v2' + ? 'https://connect.mailerlite.com/api/subscribers' + : 'https://api.mailerlite.com/api/v2/me', + method: 'GET', + key: 'X-Mailerlite-Apikey', + addTo: 'header', + headers: data => + data?.version === 'v2' + ? { + Authorization: 'Bearer {api_key}', + Accept: 'application/json' + } + : {}, + extraFields: [ + { + name: 'version', + label: __('MailerLite Version', 'bit-integrations'), + required: true, + type: 'select', + placeholder: __('Select version', 'bit-integrations'), + options: [ + { label: __('MailerLite Classic (v1)', 'bit-integrations'), value: 'v1' }, + { label: __('MailerLite New (v2)', 'bit-integrations'), value: 'v2' } + ] + } + ] + }} + noteDetails={{ note }} + onConnectionSelected={syncVersionFromConnection} + /> ) } diff --git a/frontend/src/components/AllIntegrations/MailerLite/MailerLiteCommonFunc.js b/frontend/src/components/AllIntegrations/MailerLite/MailerLiteCommonFunc.js index bd1a7d848..cc96fac4e 100644 --- a/frontend/src/components/AllIntegrations/MailerLite/MailerLiteCommonFunc.js +++ b/frontend/src/components/AllIntegrations/MailerLite/MailerLiteCommonFunc.js @@ -43,37 +43,16 @@ export const checkMappedFields = mailerLiteConf => { return true } -export const authorization = (confTmp, setIsAuthorized, loading, setLoading) => { - if (!confTmp.auth_token) { - toast.error(__("API Key can't be empty", 'bit-integrations')) - - return - } - - setLoading({ ...loading, auth: true }) - - const requestParams = { - auth_token: confTmp.auth_token, - version: confTmp.version - } - - bitsFetch(requestParams, 'mailerlite_authorization').then(result => { - setLoading({ ...loading, auth: false }) - - if (result && result.success) { - setIsAuthorized(true) - - toast.success(__('Authorized Successfully', 'bit-integrations')) - - return - } - - toast.error(__('Authorized failed', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + auth_token: confTmp.auth_token, + version: confTmp.version + } export const mailerliteRefreshFields = (confTmp, setConf, loading, setLoading) => { - if (!confTmp.auth_token) { + if (!confTmp.connection_id && !confTmp.auth_token) { toast.error(__("API Key can't be empty", 'bit-integrations')) return @@ -103,10 +82,7 @@ export const mailerliteRefreshFields = (confTmp, setConf, loading, setLoading) = return } - const requestParams = { - auth_token: confTmp.auth_token, - version: confTmp.version - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'mailerlite_refresh_fields').then(result => { setLoading({ ...loading, field: false }) @@ -129,12 +105,9 @@ export const mailerliteRefreshFields = (confTmp, setConf, loading, setLoading) = } export const getAllGroups = (confTmp, setConf, loding, setLoading) => { - setLoading({ ...setLoading, group: true }) + setLoading({ ...loding, group: true }) - const requestParams = { - auth_token: confTmp.auth_token, - version: confTmp.version - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'mailerlite_fetch_all_groups').then(result => { if (result && result.success) { @@ -143,12 +116,12 @@ export const getAllGroups = (confTmp, setConf, loding, setLoading) => { newConf.groups = result.data } setConf(newConf) - setLoading({ ...setLoading, group: false }) + setLoading({ ...loding, group: false }) toast.success(__('Group fetch successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, group: false }) + setLoading({ ...loding, group: false }) toast.error(__('Group fetch failed', 'bit-integrations')) }) } diff --git a/frontend/src/components/AllIntegrations/MailerPress/MailerPressAuthorization.jsx b/frontend/src/components/AllIntegrations/MailerPress/MailerPressAuthorization.jsx index 2f2f376fa..bcffe3bdb 100644 --- a/frontend/src/components/AllIntegrations/MailerPress/MailerPressAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/MailerPress/MailerPressAuthorization.jsx @@ -1,111 +1,36 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function MailerPressAuthorization({ - formID, mailerPressConf, setMailerPressConf, step, nextPage, - isLoading, - setIsLoading, - setSnackbar + isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'mailer_press_authorize').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with MailerPress Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = { ...mailerPressConf } - newConf[e.target.name] = e.target.value - setMailerPressConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - {isLoading === 'auth' && ( -
    - - {__('Checking if MailerPress is active!!!', 'bit-integrations')} -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    -
    -
    - -
    -
    - {__('MailerPress is not activated or not installed', 'bit-integrations')} -
    -
    -
    - )} - - {showAuthMsg && isAuthorized && !isLoading && ( -
    -
    - -
    -
    {__('MailerPress is activated', 'bit-integrations')}
    -
    - )} - - -
    - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Mailercloud/EditMailercloud.jsx b/frontend/src/components/AllIntegrations/Mailercloud/EditMailercloud.jsx index 16330396e..5fbaaa38f 100644 --- a/frontend/src/components/AllIntegrations/Mailercloud/EditMailercloud.jsx +++ b/frontend/src/components/AllIntegrations/Mailercloud/EditMailercloud.jsx @@ -14,7 +14,7 @@ function EditMailercloud({ allIntegURL }) { const [snack, setSnackbar] = useState({ show: false }) const flow = useRecoilValue($newFlow) const [mailercloudConf, setMailercloudConf] = useRecoilState($actionConf) - const [error, setError] = useState({ name: '', authKey: '' }) + const [error, setError] = useState({ name: '' }) const formFields = useRecoilValue($formFields) const [loading, setLoading] = useState({ auth: false, diff --git a/frontend/src/components/AllIntegrations/Mailercloud/Mailercloud.jsx b/frontend/src/components/AllIntegrations/Mailercloud/Mailercloud.jsx index d3345a80c..9ff4ab2b5 100644 --- a/frontend/src/components/AllIntegrations/Mailercloud/Mailercloud.jsx +++ b/frontend/src/components/AllIntegrations/Mailercloud/Mailercloud.jsx @@ -19,7 +19,7 @@ function Mailercloud({ formFields, setFlow, flow, allIntegURL }) { const [mailercloudConf, setMailercloudConf] = useState({ name: 'Mailercloud', type: 'Mailercloud', - authKey: '', + api_key: '', field_map: [{ formFields: '', mailercloudFormField: '' }], listId: '', contactType: '', diff --git a/frontend/src/components/AllIntegrations/Mailercloud/MailercloudAuthorization.jsx b/frontend/src/components/AllIntegrations/Mailercloud/MailercloudAuthorization.jsx index baf2290de..b59e29df9 100644 --- a/frontend/src/components/AllIntegrations/Mailercloud/MailercloudAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Mailercloud/MailercloudAuthorization.jsx @@ -1,13 +1,9 @@ -import { useState } from 'react' -import AuthorizeButton from '../../Utilities/AuthorizeButton' -import ErrorField from '../../Utilities/ErrorField' -import GetInfo from '../../Utilities/GetInfo' -import Input from '../../Utilities/Input' -import Note from '../../Utilities/Note' -import StepPage from '../../Utilities/StepPage' -import { getAllLists, handleAuthorize, handleInput } from './MailercloudCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' import { __ } from '../../../Utils/i18nwrap' +import Authorization from '../../Connections/Authorization' +import { getAllLists } from './MailercloudCommonFunc' function MailercloudAuthorization({ mailercloudConf, @@ -16,22 +12,29 @@ function MailercloudAuthorization({ setStep, isInfo, loading, - setLoading + setLoading, + setSnackbar }) { - const [authorized, setAuthorized] = useState(false) - const [error, setError] = useState({ name: '', authKey: '' }) - const nextPage = async () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const loadLists = useCallback( + connectionId => { + const nextConf = connectionId + ? { ...mailercloudConf, connection_id: connectionId } + : mailercloudConf - setStep(2) - setLoading({ ...loading, page: true }) - const data = await getAllLists(mailercloudConf, setMailercloudConf) - if (data) { - setLoading({ ...loading, page: false }) - } - } + getAllLists(nextConf, setMailercloudConf, loading, setLoading, setSnackbar) + }, + [mailercloudConf, loading, setLoading, setMailercloudConf, setSnackbar] + ) + + const handleSetStep = useCallback( + value => { + if (value === 2 && !mailercloudConf?.default?.lists) { + loadLists() + } + setStep(value) + }, + [loadLists, mailercloudConf?.default?.lists, setStep] + ) const note = `

    ${__('Step of get API Key:', 'bit-integrations')}

    @@ -53,45 +56,28 @@ function MailercloudAuthorization({ ` return ( - - - -
    - {/* Mailercloud Authorization */} - - handleInput(e, mailercloudConf, setMailercloudConf, error, setError)} - /> - handleInput(e, mailercloudConf, setMailercloudConf, error, setError)} - /> - - - {!isInfo && ( - - handleAuthorize(mailercloudConf, setError, setAuthorized, loading, setLoading) - } - nextPage={nextPage} - auth={authorized} - loading={loading.auth} - /> - )} -
    - - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Mailercloud/MailercloudCommonFunc.js b/frontend/src/components/AllIntegrations/Mailercloud/MailercloudCommonFunc.js index b7875752c..7967a7f8d 100644 --- a/frontend/src/components/AllIntegrations/Mailercloud/MailercloudCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Mailercloud/MailercloudCommonFunc.js @@ -13,31 +13,14 @@ export const handleInput = (e, conf, setConf, error, setError) => { setConf(newConf) } -export const handleAuthorize = (conf, setError, setAuthorized, loading, setLoading) => { - if (!conf.authKey) { - setError({ authKey: !conf.authKey ? __("API Key can't be empty") : '' }) - return - } - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { authKey: conf.authKey } +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { api_key: conf?.api_key || conf?.authKey } - bitsFetch(requestParams, 'mailercloud_handle_authorize').then(result => { - if (!result.data.errors) { - setAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed')) - }) -} - -export const getAllLists = async (conf, setConf, loading, setLoading) => { +export const getAllLists = async (conf, setConf, loading, setLoading, setSnackbar = null) => { setLoading && setLoading({ ...loading, list: true }) - const requestParams = { authKey: conf.authKey } + const requestParams = buildAuthRequestParams(conf) const result = await bitsFetch(requestParams, 'mailercloud_get_all_lists') if (result.success) { const { data } = result.data @@ -51,6 +34,9 @@ export const getAllLists = async (conf, setConf, loading, setLoading) => { if (setLoading) { setLoading({ ...loading, list: false }) toast.success(__('Tag refresh successfully')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Tag refresh successfully') }) + } } } return true @@ -58,13 +44,16 @@ export const getAllLists = async (conf, setConf, loading, setLoading) => { if (setLoading) { setLoading({ ...loading, list: false }) toast.success(__('Tag refresh failed')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Tag refresh failed') }) + } } return false } -export const getAllFields = async (conf, setConf, loading, setLoading) => { +export const getAllFields = async (conf, setConf, loading, setLoading, setSnackbar = null) => { setLoading && setLoading({ ...loading, field: true }) - const requestParams = { authKey: conf.authKey } + const requestParams = buildAuthRequestParams(conf) const result = await bitsFetch(requestParams, 'mailercloud_get_all_fields') if (result.success) { const { data } = result @@ -78,6 +67,9 @@ export const getAllFields = async (conf, setConf, loading, setLoading) => { if (setLoading) { setLoading({ ...loading, field: false }) toast.success(__('Tag refresh successfully')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Tag refresh successfully') }) + } } } return true @@ -85,6 +77,9 @@ export const getAllFields = async (conf, setConf, loading, setLoading) => { if (setLoading) { setLoading({ ...loading, field: false }) toast.success(__('Tag refresh failed')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Tag refresh failed') }) + } } return false } diff --git a/frontend/src/components/AllIntegrations/Mailify/MailifyAuthorization.jsx b/frontend/src/components/AllIntegrations/Mailify/MailifyAuthorization.jsx index d1804b5ab..e4eb229ac 100644 --- a/frontend/src/components/AllIntegrations/Mailify/MailifyAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Mailify/MailifyAuthorization.jsx @@ -1,85 +1,36 @@ -import { useState } from 'react' -import { toast } from 'react-hot-toast' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' +import Authorization from '../../Connections/Authorization' import { refreshMailifyList } from './MailifyCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' export default function MailifyAuthorization({ - formID, mailifyConf, setMailifyConf, step, setstep, setSnackbar, isInfo, - isLoading, setIsLoading }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_key: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) + const loadLists = useCallback( + connectionId => { + const nextConf = connectionId ? { ...mailifyConf, connection_id: connectionId } : mailifyConf + refreshMailifyList(nextConf, setMailifyConf, setIsLoading, setSnackbar) + }, + [mailifyConf, setMailifyConf, setIsLoading, setSnackbar] + ) - const handleAuthorize = () => { - const newConf = { ...mailifyConf } - if (!newConf.name || !newConf.api_key) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - account_id: !newConf.account_id - ? __("Access Account ID can't be empty", 'bit-integrations') - : '', - api_key: !newConf.api_key ? __("Access Api Key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading('auth') - const data = { - account_id: newConf.account_id, - api_key: newConf.api_key - } - bitsFetch(data, 'mailify_authorize').then(result => { - if (result && result.success) { - const newConf = { ...mailifyConf } - newConf.tokenDetails = result.data - setMailifyConf(newConf) - setisAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - toast.error( - `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - ) - } else { - toast.error(__('Authorization failed. please try again', 'bit-integrations')) + const handleSetStep = useCallback( + value => { + if (value === 2 && !mailifyConf?.default?.mailifyLists) { + loadLists() } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...mailifyConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setMailifyConf(newConf) - } - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - refreshMailifyList(mailifyConf, setMailifyConf, setIsLoading, setSnackbar) - setstep(2) - } + setstep(value) + }, + [loadLists, mailifyConf?.default?.mailifyLists, setstep] + ) const ActiveInstructions = `

    ${__('Get Account Id and Api key', 'bit-integrations')}

    @@ -96,103 +47,21 @@ export default function MailifyAuthorization({ ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - -
    {error.name}
    - -
    - {__('Access Account ID:', 'bit-integrations')} -
    - -
    {error.account_id}
    - -
    - {__('Access API Key:', 'bit-integrations')} -
    - -
    {error.api_key}
    - - - {__('To Get Account Id and Api Key, Please Visit', 'bit-integrations')} -   - - {__('Mailify API Token', 'bit-integrations')} - - -
    -
    - {isLoading === 'auth' && ( -
    - - {__('Checking API Key!!!', 'bit-integrations')} -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {__('Sorry, Api key is invalid', 'bit-integrations')} -
    - )} - {!isInfo && ( - <> - -
    - - - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Mailify/MailifyCommonFunc.js b/frontend/src/components/AllIntegrations/Mailify/MailifyCommonFunc.js index cf5b2933b..a99d0c07e 100644 --- a/frontend/src/components/AllIntegrations/Mailify/MailifyCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Mailify/MailifyCommonFunc.js @@ -8,12 +8,15 @@ export const handleInput = (e, mailifyConf, setMailifyConf) => { setMailifyConf({ ...newConf }) } +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { account_id: conf.account_id, api_key: conf.api_key } + // refreshMappedLists export const refreshMailifyList = (mailifyConf, setMailifyConf, setIsLoading, setSnackbar) => { - const refreshListsRequestParams = { - account_id: mailifyConf.account_id, - api_key: mailifyConf.api_key - } + if (typeof setIsLoading === 'function') setIsLoading(true) + const refreshListsRequestParams = buildAuthRequestParams(mailifyConf) bitsFetch(refreshListsRequestParams, 'mailify_lists') .then(result => { if (result && result.success) { @@ -51,9 +54,9 @@ export const refreshMailifyList = (mailifyConf, setMailifyConf, setIsLoading, se // refreshMappedFields export const refreshMailifyHeader = (mailifyConf, setMailifyConf, setIsLoading, setSnackbar) => { + if (typeof setIsLoading === 'function') setIsLoading(true) const refreshListsRequestParams = { - account_id: mailifyConf.account_id, - api_key: mailifyConf.api_key, + ...buildAuthRequestParams(mailifyConf), list_id: mailifyConf.listId } diff --git a/frontend/src/components/AllIntegrations/Mailjet/MailjetAuthorization.jsx b/frontend/src/components/AllIntegrations/Mailjet/MailjetAuthorization.jsx index c086f91ba..eda35c886 100644 --- a/frontend/src/components/AllIntegrations/Mailjet/MailjetAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Mailjet/MailjetAuthorization.jsx @@ -1,10 +1,10 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { mailjetAuthentication } from './MailjetCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllLists } from './MailjetCommonFunc' export default function MailjetAuthorization({ mailjetConf, @@ -15,116 +15,47 @@ export default function MailjetAuthorization({ setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '', secretKey: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const loadLists = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...mailjetConf, connection_id: connectionId } : mailjetConf + await getAllLists(nextConf, setMailjetConf, loading, setLoading, 'fetch') + }, + [mailjetConf, setMailjetConf, loading, setLoading] + ) - !mailjetConf?.default - setStep(2) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !(mailjetConf?.lists || []).length) { + loadLists() + } + setStep(value) + }, + [mailjetConf, loadLists, setStep] + ) - const handleInput = e => { - const newConf = { ...mailjetConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setMailjetConf(newConf) - } + const note = ` +

    ${__('To Get API key & Secret Key', 'bit-integrations')}

    +
      +
    • ${__('Open your Mailjet account API keys page.', 'bit-integrations')}
    • +
    • ${__('Use API Key as Username and Secret Key as Password in this form.', 'bit-integrations')}
    • +
    ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('API Key:', 'bit-integrations')} -
    - -
    {error.apiKey}
    -
    - {__('Secret Key:', 'bit-integrations')} -
    - -
    {error.secretKey}
    - - {__('To Get API key & Secret Key, Please Visit', 'bit-integrations')} -   - - {__('Mailjet API Token', 'bit-integrations')} - - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Mailjet/MailjetCommonFunc.js b/frontend/src/components/AllIntegrations/Mailjet/MailjetCommonFunc.js index d5d5422eb..73dddcd01 100644 --- a/frontend/src/components/AllIntegrations/Mailjet/MailjetCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Mailjet/MailjetCommonFunc.js @@ -40,6 +40,14 @@ export const checkMappedFields = mailjetConf => { return true } +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + apiKey: confTmp.apiKey || confTmp.username, + secretKey: confTmp.secretKey || confTmp.password + } + export const mailjetAuthentication = ( confTmp, setConf, @@ -49,11 +57,11 @@ export const mailjetAuthentication = ( setLoading, type ) => { - if (!confTmp.apiKey) { + if (!confTmp.connection_id && !confTmp.apiKey) { setError({ apiKey: !confTmp.apiKey ? __("API key can't be empty", 'bit-integrations') : '' }) return } - if (!confTmp.secretKey) { + if (!confTmp.connection_id && !confTmp.secretKey) { setError({ secretKey: !confTmp.secretKey ? __("Secret key can't be empty", 'bit-integrations') : '' }) @@ -68,9 +76,9 @@ export const mailjetAuthentication = ( if (type === 'refreshLists') { setLoading({ ...loading, lists: true }) } - const requestParams = { apiKey: confTmp.apiKey, secretKey: confTmp.secretKey } + const requestParams = buildAuthRequestParams(confTmp) - bitsFetch(requestParams, 'mailjet_authentication').then(result => { + bitsFetch(requestParams, 'mailjet_fetch_all_lists').then(result => { if (result && result.success) { const newConf = { ...confTmp } setIsAuthorized(true) @@ -96,10 +104,21 @@ export const mailjetAuthentication = ( }) } +export const getAllLists = (confTmp, setConf, loading, setLoading, type = 'refresh') => + mailjetAuthentication( + confTmp, + setConf, + () => {}, + () => {}, + loading, + setLoading, + type === 'fetch' ? 'authentication' : 'refreshLists' + ) + export const getCustomFields = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, customFields: true }) + setLoading(prev => ({ ...prev, customFields: true })) - const requestParams = { apiKey: confTmp.apiKey, secretKey: confTmp.secretKey } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'mailjet_fetch_all_custom_fields').then(result => { if (result && result.success) { @@ -108,12 +127,12 @@ export const getCustomFields = (confTmp, setConf, setLoading) => { newConf.customFields = result.data } setConf(newConf) - setLoading({ ...setLoading, customFields: false }) + setLoading(prev => ({ ...prev, customFields: false })) toast.success(__('Custom fields fetch successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, customFields: false }) + setLoading(prev => ({ ...prev, customFields: false })) toast.error(__('Custom fields fetch failed', 'bit-integrations')) }) } diff --git a/frontend/src/components/AllIntegrations/Mailster/MailsterAuthorization.jsx b/frontend/src/components/AllIntegrations/Mailster/MailsterAuthorization.jsx index 819758813..a0734a1bd 100644 --- a/frontend/src/components/AllIntegrations/Mailster/MailsterAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Mailster/MailsterAuthorization.jsx @@ -1,96 +1,29 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { mailsterAuthentication } from './MailsterCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function MailsterAuthorization({ - mailsterConf, - setMailsterConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !mailsterConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...mailsterConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setMailsterConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function MailsterAuthorization({ mailsterConf, setMailsterConf, step, setStep, isInfo }) { return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - {error.name &&
    {error.name}
    } -
    - - {!isInfo && ( -
    - -
    - -
    - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Mailster/MailsterCommonFunc.js b/frontend/src/components/AllIntegrations/Mailster/MailsterCommonFunc.js index f4be43b55..8ad624087 100644 --- a/frontend/src/components/AllIntegrations/Mailster/MailsterCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Mailster/MailsterCommonFunc.js @@ -40,33 +40,6 @@ export const checkMappedFields = mailsterConf => { return true } -export const mailsterAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.name) { - setError({ name: !confTmp.name ? __("Name can't be empty", 'bit-integrations') : '' }) - return - } - - setLoading({ ...loading, auth: true }) - bitsFetch({}, 'mailster_authentication').then(result => { - if (result.success) { - setIsAuthorized(true) - toast.success(__('Connected Successfully', 'bit-integrations')) - setLoading({ ...loading, auth: false }) - mailsterFields(confTmp, setConf, loading, setLoading) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Connection failed: install and active Mailster plugin first!', 'bit-integrations')) - }) -} - export const mailsterFields = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, fields: true }) diff --git a/frontend/src/components/AllIntegrations/Mailup/Mailup.jsx b/frontend/src/components/AllIntegrations/Mailup/Mailup.jsx index b937f17c8..d62a9293c 100644 --- a/frontend/src/components/AllIntegrations/Mailup/Mailup.jsx +++ b/frontend/src/components/AllIntegrations/Mailup/Mailup.jsx @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import toast from 'react-hot-toast' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' @@ -9,7 +9,7 @@ import Steps from '../../Utilities/Steps' import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import MailupAuthorization from './MailupAuthorization' -import { handleInput, setGrantTokenResponse, checkMappedFields } from './MailupCommonFunc' +import { handleInput, checkMappedFields } from './MailupCommonFunc' import MailupIntegLayout from './MailupIntegLayout' function Mailup({ formFields, setFlow, flow, allIntegURL }) { @@ -33,11 +33,6 @@ function Mailup({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - // eslint-disable-next-line no-unused-expressions - window.opener && setGrantTokenResponse('mailup') - }, []) - const nextPage = pageNo => { setTimeout(() => { document.getElementById('btcd-settings-wrp').scrollTop = 0 diff --git a/frontend/src/components/AllIntegrations/Mailup/MailupAuthorization.jsx b/frontend/src/components/AllIntegrations/Mailup/MailupAuthorization.jsx index f9d44b180..1a9b253b8 100644 --- a/frontend/src/components/AllIntegrations/Mailup/MailupAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Mailup/MailupAuthorization.jsx @@ -1,128 +1,72 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { handleMailupAuthorize, fetchAllList } from './MailupCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { fetchAllList } from './MailupCommonFunc' export default function MailupAuthorization({ - formID, mailupConf, setMailupConf, step, setStep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setStep(2) - fetchAllList(mailupConf, setMailupConf, setIsLoading, setSnackbar) - } - - const handleInput = e => { - const newConf = { ...mailupConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setMailupConf(newConf) - } + const loadLists = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...mailupConf, connection_id: connectionId } : mailupConf + fetchAllList(nextConf, setMailupConf, setIsLoading, setSnackbar) + }, + [mailupConf, setMailupConf, setIsLoading, setSnackbar] + ) - return ( -
    - + const handleSetStep = useCallback( + value => { + if (value === 2 && !mailupConf?.allList?.length) { + loadLists() + } -
    - {__('Integration Name:', 'bit-integrations')} -
    - + setStep(value) + }, + [loadLists, mailupConf?.allList?.length, setStep] + ) -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    + const note = `

    ${__('Get Mailup client id and secret', 'bit-integrations')}

    +
      +
    • ${__('Go to Mailup developer settings and create or open your app.', 'bit-integrations')}
    • +
    • ${__('Copy Client ID and Client Secret.', 'bit-integrations')}
    • +
    • ${__('Use callback URL shown in connection form.', 'bit-integrations')}
    • +
    ` -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/Mailup/MailupCommonFunc.js b/frontend/src/components/AllIntegrations/Mailup/MailupCommonFunc.js index c91e5675d..dd9f3dafd 100644 --- a/frontend/src/components/AllIntegrations/Mailup/MailupCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Mailup/MailupCommonFunc.js @@ -1,18 +1,27 @@ /* eslint-disable no-console */ -import toast from 'react-hot-toast' import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' import { sprintf, __ } from '../../../Utils/i18nwrap' import { create } from 'mutative' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + tokenDetails: conf.tokenDetails, + clientId: conf.clientId, + clientSecret: conf.clientSecret + } + export const handleInput = (e, mailupConf, setMailupConf, setIsLoading, setSnackbar) => { const newConf = { ...mailupConf } - const { name } = e.target + const { name, value } = e.target - if (e.target.value !== '') { - newConf[name] = e.target.value - fetchAllGroup(newConf, setMailupConf, setIsLoading, setSnackbar) - fetchAllField(newConf, setMailupConf, setIsLoading, setSnackbar) + if (value !== '') { + newConf[name] = value + if (name === 'listId') { + fetchAllGroup(newConf, setMailupConf, setIsLoading, setSnackbar) + fetchAllField(newConf, setMailupConf, setIsLoading, setSnackbar) + } } else { delete newConf[name] } @@ -22,11 +31,7 @@ export const handleInput = (e, mailupConf, setMailupConf, setIsLoading, setSnack export const fetchAllList = (mailupConf, setMailupConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { - tokenDetails: mailupConf.tokenDetails, - clientId: mailupConf.clientId, - clientSecret: mailupConf.clientSecret - } + const requestParams = buildAuthRequestParams(mailupConf) bitsFetch(requestParams, 'mailup_fetch_all_list') .then(result => { if (result && result.success) { @@ -52,11 +57,7 @@ export const fetchAllList = (mailupConf, setMailupConf, setIsLoading, setSnackba export const fetchAllField = (mailupConf, setMailupConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { - tokenDetails: mailupConf.tokenDetails, - clientId: mailupConf.clientId, - clientSecret: mailupConf.clientSecret - } + const requestParams = buildAuthRequestParams(mailupConf) bitsFetch(requestParams, 'mailup_fetch_all_field') .then(result => { if (result && result.success) { @@ -84,9 +85,7 @@ export const fetchAllField = (mailupConf, setMailupConf, setIsLoading, setSnackb export const fetchAllGroup = (mailupConf, setMailupConf, setIsLoading, setSnackbar) => { setIsLoading(true) const requestParams = { - tokenDetails: mailupConf.tokenDetails, - clientId: mailupConf.clientId, - clientSecret: mailupConf.clientSecret, + ...buildAuthRequestParams(mailupConf), listId: mailupConf.listId } bitsFetch(requestParams, 'mailup_fetch_all_group') @@ -109,120 +108,6 @@ export const fetchAllGroup = (mailupConf, setMailupConf, setIsLoading, setSnackb .catch(() => setIsLoading(false)) } -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation.replace(`${window.opener.location.href}`, '').split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] - } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} - -export const handleMailupAuthorize = ( - integ, - confTmp, - setConf, - setError, - setIsAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.clientId) { - setError({ - clientId: !confTmp.clientId ? __("Client ID can't be empty", 'bit-integrations') : '' - }) - return - } - if (!confTmp.clientSecret) { - setError({ - clientSecret: !confTmp.clientSecret ? __("Client Secret can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - - const apiEndpoint = `https://services.mailup.com/Authorization/OAuth/LogOn?client_id=${ - confTmp.clientId - }&response_type=code&redirect_uri=${encodeURIComponent(window.location.href)}` - - const authWindow = window.open(apiEndpoint, integ, 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitsMailup = localStorage.getItem(`__${integ}`) - - if (bitsMailup) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsMailup) - localStorage.removeItem(`__${integ}`) - - if (grantTokenResponse.token) { - grantTokenResponse.code = grantTokenResponse.token - } - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - tokenHelper(grantTokenResponse, newConf, setConf, setIsAuthorized, setIsLoading) - } - } - setIsLoading(false) - }, 500) -} - -const tokenHelper = (grantToken, confTmp, setConf, setIsAuthorized, setIsLoading) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - // eslint-disable-next-line no-undef - - bitsFetch(tokenRequestParams, 'mailup_authorization').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setIsAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - toast.error( - `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - ) - } else { - toast.error(__('Authorization failed. please try again', 'bit-integrations')) - } - setIsLoading(false) - }) -} - export const generateMappedField = mailupConf => { const requiredFlds = mailupConf?.staticFields.filter(fld => fld.required === true) return requiredFlds.length > 0 diff --git a/frontend/src/components/AllIntegrations/MasterStudyLms/MasterStudyLmsAuthorization.jsx b/frontend/src/components/AllIntegrations/MasterStudyLms/MasterStudyLmsAuthorization.jsx index b5e22c932..1a9353993 100644 --- a/frontend/src/components/AllIntegrations/MasterStudyLms/MasterStudyLmsAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/MasterStudyLms/MasterStudyLmsAuthorization.jsx @@ -1,102 +1,53 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' -export default function MasterStudyLmsAuthorization({ - formID, - msLmsConf, - setMsLmsConf, - step, - setStep, - isLoading, - setIsLoading, - setSnackbar -}) { - const [isAuthorized, setisAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'MasterStudyLms_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with MasterStudyLMs Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(msLmsConf) - newConf[e.target.name] = e.target.value - setMsLmsConf(newConf) - } +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function MasterStudyLmsAuthorization({ msLmsConf, setMsLmsConf, step, setStep, isInfo }) { return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - {isLoading === 'auth' && ( -
    - - Checking if MasterStudyLms is active!!! -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'MasterStudyLms' - )} -
    - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Mautic/Mautic.jsx b/frontend/src/components/AllIntegrations/Mautic/Mautic.jsx index e13280eff..009f4065d 100644 --- a/frontend/src/components/AllIntegrations/Mautic/Mautic.jsx +++ b/frontend/src/components/AllIntegrations/Mautic/Mautic.jsx @@ -1,15 +1,15 @@ /* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' -import { useNavigate, useParams } from 'react-router' +import { useNavigate } from 'react-router' import BackIcn from '../../../Icons/BackIcn' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' +import { checkMappedFields, handleInput } from './MauticCommonFunc' import MauticAuthorization from './MauticAuthorization' -import { setGrantTokenResponse, checkMappedFields, handleInput } from './MauticCommonFunc' import MauticIntegLayout from './MauticIntegLayout' function Mautic({ formFields, setFlow, flow, allIntegURL }) { @@ -26,10 +26,6 @@ function Mautic({ formFields, setFlow, flow, allIntegURL }) { field_map: [{ formField: '', mauticField: '' }], actions: {} }) - - useEffect(() => { - window.opener && setGrantTokenResponse('mautic') - }, []) const nextPage = () => { setTimeout(() => { document.getElementById('btcd-settings-wrp').scrollTop = 0 diff --git a/frontend/src/components/AllIntegrations/Mautic/MauticAuthorization.jsx b/frontend/src/components/AllIntegrations/Mautic/MauticAuthorization.jsx index a0fd83abb..f6a69882b 100644 --- a/frontend/src/components/AllIntegrations/Mautic/MauticAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Mautic/MauticAuthorization.jsx @@ -1,167 +1,77 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { handleMauticAuthorize, getAllFields } from './MauticCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllFields } from './MauticCommonFunc' + export default function MauticAuthorization({ mauticConf, setMauticConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ baseUrl: '', clientId: '', clientSecret: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - getAllFields(mauticConf, setMauticConf, setIsLoading, setSnackbar) - } - - const handleInput = e => { - const newConf = { ...mauticConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setMauticConf(newConf) - } - - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - - - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Mautic API Console', 'bit-integrations')} - - - -
    - {__('Mautic Base URL:', 'bit-integrations')} -
    - -
    {error.baseUrl}
    + const loadFields = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...mauticConf, connection_id: connectionId } : mauticConf + await getAllFields(nextConf, setMauticConf, setIsLoading, setSnackbar) + }, + [mauticConf, setMauticConf, setIsLoading, setSnackbar] + ) - - {__('Example: https://mautic.bit-integration.pro', 'bit-integrations')} - + const handleSetStep = useCallback( + value => { + if (value === 2 && !mauticConf?.default?.fields) { + loadFields() + } -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    + setstep(value) + }, + [mauticConf, loadFields, setstep] + ) -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    + const note = `

    ${__('Mautic OAuth2 Setup', 'bit-integrations')}

    +
      +
    1. ${__('Open your Mautic account and create an OAuth2 API credential.', 'bit-integrations')}
    2. +
    3. ${__('Set the callback URL exactly as shown below.', 'bit-integrations')}
    4. +
    5. ${__('Use your Mautic base URL (example: https://mautic.example.com).', 'bit-integrations')}
    6. +
    ` - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/Mautic/MauticCommonFunc.js b/frontend/src/components/AllIntegrations/Mautic/MauticCommonFunc.js index d0f7df5ff..a7756ae62 100644 --- a/frontend/src/components/AllIntegrations/Mautic/MauticCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Mautic/MauticCommonFunc.js @@ -1,6 +1,5 @@ import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { sprintf, __ } from '../../../Utils/i18nwrap' +import { __ } from '../../../Utils/i18nwrap' export const handleInput = (e, sheetConf, setSheetConf) => { const newConf = { ...sheetConf } @@ -8,14 +7,19 @@ export const handleInput = (e, sheetConf, setSheetConf) => { setSheetConf({ ...newConf }) } +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + baseUrl: conf.baseUrl, + tokenDetails: conf.tokenDetails + } + export const getAllFields = (mauticConf, setMauticConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { - clientId: mauticConf.clientId, - clientSecret: mauticConf.clientSecret, - baseUrl: mauticConf.baseUrl, - tokenDetails: mauticConf.tokenDetails - } + const requestParams = buildAuthRequestParams(mauticConf) bitsFetch(requestParams, 'mautic_get_fields') .then(result => { if (result && result.success) { @@ -51,12 +55,7 @@ export const getAllFields = (mauticConf, setMauticConf, setIsLoading, setSnackba } export const getAllTags = (mauticConf, setMauticConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { - clientId: mauticConf.clientId, - clientSecret: mauticConf.clientSecret, - baseUrl: mauticConf.baseUrl, - tokenDetails: mauticConf.tokenDetails - } + const requestParams = buildAuthRequestParams(mauticConf) bitsFetch(requestParams, 'mautic_get_tags') .then(result => { if (result && result.success) { @@ -92,12 +91,7 @@ export const getAllTags = (mauticConf, setMauticConf, setIsLoading, setSnackbar) export const getAllUsers = (mauticConf, setMauticConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { - clientId: mauticConf.clientId, - clientSecret: mauticConf.clientSecret, - baseUrl: mauticConf.baseUrl, - tokenDetails: mauticConf.tokenDetails - } + const requestParams = buildAuthRequestParams(mauticConf) bitsFetch(requestParams, 'mautic_get_users') .then(result => { if (result && result.success) { @@ -131,124 +125,6 @@ export const getAllUsers = (mauticConf, setMauticConf, setIsLoading, setSnackbar .catch(() => setIsLoading(false)) } -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation.replace(`${window.opener.location.href}`, '').split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] - } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} - -export const handleMauticAuthorize = ( - integ, - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.clientId || !confTmp.clientSecret || !confTmp.baseUrl) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty", 'bit-integrations') : '', - baseUrl: !confTmp.baseUrl ? __("Base Url can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - - const apiEndpoint = `${confTmp.baseUrl}/oauth/v2/authorize?client_id=${ - confTmp.clientId - }&redirect_uri=${encodeURIComponent(window.location.href)}&response_type=code` - const authWindow = window.open(apiEndpoint, integ, 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitsMautic = localStorage.getItem(`__${integ}`) - if (bitsMautic) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsMautic) - localStorage.removeItem(`__${integ}`) - if (grantTokenResponse.code.search('#')) { - const [code] = grantTokenResponse.code.split('#') - grantTokenResponse.code = code - } - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper(grantTokenResponse, newConf, setConf, setisAuthorized, setIsLoading, setSnackbar) - } - } - }, 500) -} - -const tokenHelper = (grantToken, confTmp, setConf, setisAuthorized, setIsLoading, setSnackbar) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - tokenRequestParams.baseUrl = confTmp.baseUrl - tokenRequestParams.redirectURI = window.location.href - - bitsFetch(tokenRequestParams, 'mautic_generate_token') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Authorized Successfully', 'bit-integrations') - }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} - export const checkMappedFields = mauticConf => { const mappedFleld = mauticConf.field_map ? mauticConf.field_map.filter(mapped => !mapped.formField && !mapped.mauticField) diff --git a/frontend/src/components/AllIntegrations/Memberpress/MemberpressAuthorization.jsx b/frontend/src/components/AllIntegrations/Memberpress/MemberpressAuthorization.jsx index 9ba9d3b69..09d2ce28c 100644 --- a/frontend/src/components/AllIntegrations/Memberpress/MemberpressAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Memberpress/MemberpressAuthorization.jsx @@ -1,107 +1,52 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' import { getAllMemberShip, paymentGateway } from './MemberpressCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' export default function MemberpressAuthorization({ - formID, memberpressConf, setMemberpressConf, step, setStep, - isLoading, + isInfo, setIsLoading, setSnackbar }) { - const [isAuthorized, setisAuthorized] = useState(false) - // const [isLoading, setIsLoading] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'memberpress_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Memberpress Successfully', 'bit-integrations') - }) + const handleSetStep = useCallback( + value => { + if (value === 2) { + getAllMemberShip(memberpressConf, setMemberpressConf, setIsLoading, setSnackbar) + paymentGateway(memberpressConf, setMemberpressConf, setIsLoading, setSnackbar) } - setIsLoading(false) - setShowAuthMsg(true) - getAllMemberShip(memberpressConf, setMemberpressConf, setIsLoading, setSnackbar) - paymentGateway(memberpressConf, setMemberpressConf, setIsLoading, setSnackbar) - }) - } - - const handleInput = e => { - const newConf = deepCopy(memberpressConf) - newConf[e.target.name] = e.target.value - setMemberpressConf(newConf) - } + setStep(value) + }, + [setStep, memberpressConf, setMemberpressConf, setIsLoading, setSnackbar] + ) return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - {isLoading === 'auth' && ( -
    - - Checking if Memberpress is active!!! -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'Memberpress' - )} -
    - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/MondayCom/MondayCom.jsx b/frontend/src/components/AllIntegrations/MondayCom/MondayCom.jsx index 2f8c10c04..36e555577 100644 --- a/frontend/src/components/AllIntegrations/MondayCom/MondayCom.jsx +++ b/frontend/src/components/AllIntegrations/MondayCom/MondayCom.jsx @@ -94,9 +94,7 @@ function MondayCom({ formFields, setFlow, flow, allIntegURL }) { setMondayComConf={setMondayComConf} step={step} setStep={setStep} - loading={loading} setLoading={setLoading} - setSnackbar={setSnackbar} /> {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/MondayCom/MondayComAuthorization.jsx b/frontend/src/components/AllIntegrations/MondayCom/MondayComAuthorization.jsx index 7145e29db..092f60fa5 100644 --- a/frontend/src/components/AllIntegrations/MondayCom/MondayComAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/MondayCom/MondayComAuthorization.jsx @@ -1,47 +1,38 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' -import TutorialLink from '../../Utilities/TutorialLink' -import { mondayComAuthentication } from './MondayComCommonFunc' -import { create } from 'mutative' +import Authorization from '../../Connections/Authorization' +import { getAllBoards } from './MondayComCommonFunc' export default function MondayComAuthorization({ mondayComConf, setMondayComConf, step, setStep, - loading, setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ apiToken: '' }) + const loadBoards = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...mondayComConf, connection_id: connectionId } : mondayComConf + getAllBoards(nextConf, setMondayComConf, setLoading) + }, + [mondayComConf, setMondayComConf, setLoading] + ) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setStep(2) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !mondayComConf?.boards?.length) { + loadBoards() + } - const handleInput = e => { - const { name, value } = e.target - setError(err => - create(err, draft => { - draft[name] = '' - }) - ) - setMondayComConf(prev => - create(prev, draft => { - draft[name] = value - }) - ) - } + setStep(value) + }, + [loadBoards, mondayComConf?.boards?.length, setStep] + ) - const ActiveInstructions = ` + const note = `

    ${__('To Get Monday.com API Token', 'bit-integrations')}

    • ${__('Log in to your Monday.com account.', 'bit-integrations')}
    • @@ -51,76 +42,28 @@ export default function MondayComAuthorization({
    ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('API Token:', 'bit-integrations')} -
    - -
    {error.apiToken}
    - - - {__('To Get API Token, Please Visit', 'bit-integrations')} -   - - {__('Monday.com Developers', 'bit-integrations')} - - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/MondayCom/MondayComCommonFunc.js b/frontend/src/components/AllIntegrations/MondayCom/MondayComCommonFunc.js index 12fa97eb6..22edec09b 100644 --- a/frontend/src/components/AllIntegrations/MondayCom/MondayComCommonFunc.js +++ b/frontend/src/components/AllIntegrations/MondayCom/MondayComCommonFunc.js @@ -6,6 +6,13 @@ import { __ } from '../../../Utils/i18nwrap' import { create } from 'mutative' import { staticFieldsMap, needsColumnMap } from './staticData' +const buildAuthRequestParams = confTmp => + confTmp?.connection_id + ? { connection_id: confTmp.connection_id } + : { + apiToken: confTmp?.apiToken + } + export const handleInput = (e, mondayComConf, setMondayComConf) => { setMondayComConf(mondayComConf => create(mondayComConf, draftConf => { @@ -48,45 +55,10 @@ export const checkMappedFields = mondayComConf => { return true } -export const mondayComAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.apiToken) { - setError({ - apiToken: !confTmp.apiToken ? __("API Token can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - apiToken: confTmp.apiToken - } - - bitsFetch(requestParams, 'mondayCom_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - const message = typeof result?.data === 'string' ? result.data : result?.data?.message - const authErrorMessage = result?.message || result?.error || message - toast.error( - authErrorMessage - ? `${__('Authorization failed', 'bit-integrations')}: ${authErrorMessage}` - : __('Authorization failed', 'bit-integrations') - ) - }) -} - export const getAllBoards = (confTmp, setConf, setLoading) => { setLoading(prev => ({ ...prev, board: true })) - const requestParams = { - apiToken: confTmp.apiToken - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'mondayCom_fetch_boards').then(result => { if (result && result.success) { @@ -114,7 +86,7 @@ export const getAllGroups = (confTmp, setConf, boardId, setLoading) => { setLoading(prev => ({ ...prev, group: true })) const requestParams = { - apiToken: confTmp.apiToken, + ...buildAuthRequestParams(confTmp), boardId } @@ -143,7 +115,7 @@ export const getAllColumns = (confTmp, setConf, boardId, setLoading) => { setLoading(prev => ({ ...prev, column: true })) const requestParams = { - apiToken: confTmp.apiToken, + ...buildAuthRequestParams(confTmp), boardId } @@ -177,7 +149,7 @@ export const getAllItems = (confTmp, setConf, boardId, setLoading) => { setLoading(prev => ({ ...prev, item: true })) const requestParams = { - apiToken: confTmp.apiToken, + ...buildAuthRequestParams(confTmp), boardId } diff --git a/frontend/src/components/AllIntegrations/Moosend/MoosendAuthorization.jsx b/frontend/src/components/AllIntegrations/Moosend/MoosendAuthorization.jsx index abf38069e..3e16a0eeb 100644 --- a/frontend/src/components/AllIntegrations/Moosend/MoosendAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Moosend/MoosendAuthorization.jsx @@ -1,13 +1,9 @@ -import { useState } from 'react' -import AuthorizeButton from '../../Utilities/AuthorizeButton' -import ErrorField from '../../Utilities/ErrorField' -import GetInfo from '../../Utilities/GetInfo' -import Input from '../../Utilities/Input' -import Note from '../../Utilities/Note' -import StepPage from '../../Utilities/StepPage' -import { getAllLists, handleAuthorize, handleInput } from './MoosendCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllLists } from './MoosendCommonFunc' function MoosendAuthorization({ moosendConf, @@ -18,21 +14,28 @@ function MoosendAuthorization({ setLoading, isInfo }) { - const [authorized, setAuthorized] = useState(false) - const [error, setError] = useState({ name: '', authKey: '' }) + const loadLists = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...moosendConf, connection_id: connectionId } : moosendConf - const nextPage = async () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + setLoading({ ...loading, page: true }) + const loaded = await getAllLists(nextConf, setMoosendConf, loading, setLoading) + if (loaded) { + setLoading({ ...loading, page: false }) + } + }, + [moosendConf, setMoosendConf, loading, setLoading] + ) - setStep(2) - setLoading({ ...loading, page: true }) - const data = await getAllLists(moosendConf, setMoosendConf) - if (data) { - setLoading({ ...loading, page: false }) - } - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !moosendConf?.default?.lists) { + loadLists() + } + setStep(value) + }, + [moosendConf, loadLists, setStep] + ) const note = `

    ${__('Step of get API Key:', 'bit-integrations')}

    @@ -48,43 +51,24 @@ function MoosendAuthorization({ ` return ( - - - -
    - {/* Moosend Authorization */} - - handleInput(e, moosendConf, setMoosendConf, error, setError)} - /> - handleInput(e, moosendConf, setMoosendConf, error, setError)} - /> - - - {!isInfo && ( - handleAuthorize(moosendConf, setError, setAuthorized, loading, setLoading)} - nextPage={nextPage} - auth={authorized} - loading={loading.auth} - /> - )} -
    - - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Moosend/MoosendCommonFunc.js b/frontend/src/components/AllIntegrations/Moosend/MoosendCommonFunc.js index 920e62921..a288755ae 100644 --- a/frontend/src/components/AllIntegrations/Moosend/MoosendCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Moosend/MoosendCommonFunc.js @@ -14,31 +14,20 @@ export const handleInput = (e, conf, setConf, error, setError) => { } export const handleAuthorize = (conf, setError, setAuthorized, loading, setLoading) => { - if (!conf.authKey) { - setError({ authKey: !conf.authKey ? __("API Key can't be empty") : '' }) - return - } + // Legacy local authorization has been replaced by shared Connection Authorization. + // Kept as a no-op for backward import safety in older component trees. setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { authKey: conf.authKey } - - bitsFetch(requestParams, 'moosend_handle_authorize').then(result => { - if (result.data.Code === 0) { - setAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed')) - }) + setAuthorized(true) + setLoading({ ...loading, auth: false }) } +const buildAuthRequestParams = conf => + conf.connection_id ? { connection_id: conf.connection_id } : { authKey: conf.authKey || conf.api_key } + export const getAllLists = async (conf, setConf, loading, setLoading) => { setLoading && setLoading({ ...loading, list: true }) - const requestParams = { authKey: conf.authKey } - const result = await bitsFetch(requestParams, 'moosend_handle_authorize') + const requestParams = buildAuthRequestParams(conf) + const result = await bitsFetch(requestParams, 'moosend_lists') if (result.success && result.data.Code === 0) { const { MailingLists } = result.data.Context const newConf = { ...conf } diff --git a/frontend/src/components/AllIntegrations/MoxieCRM/MoxieCRMAuthorization.jsx b/frontend/src/components/AllIntegrations/MoxieCRM/MoxieCRMAuthorization.jsx index b60f22181..f4a33f56a 100644 --- a/frontend/src/components/AllIntegrations/MoxieCRM/MoxieCRMAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/MoxieCRM/MoxieCRMAuthorization.jsx @@ -1,131 +1,43 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { moxiecrmAuthentication } from './MoxieCRMCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function MoxieCRMAuthorization({ - moxiecrmConf, - setMoxieCRMConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '', api_url: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !moxiecrmConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...moxiecrmConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setMoxieCRMConf(newConf) - } - - const ActiveInstructions = ` -

    ${__('Get api secret key', 'bit-integrations')}

    -
      -
    • ${__('First go to your Moxie dashboard.', 'bit-integrations')}
    • -
    • ${__('Then click Workspace Settings from bottom left corner.', 'bit-integrations')}
    • -
    • ${__('Click "Connneted Apps", Then click "Integrations"', 'bit-integrations')}
    • -
    • ${__('Select "Custom Integrations"', 'bit-integrations')}
    • -
    ` +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function MoxieCRMAuthorization({ moxiecrmConf, setMoxieCRMConf, step, setStep, isInfo }) { + const note = ` +

    ${__('Get API Key', 'bit-integrations')}

    +
      +
    • ${__('Go to your Moxie dashboard.', 'bit-integrations')}
    • +
    • ${__('Open Workspace Settings from the bottom-left corner.', 'bit-integrations')}
    • +
    • ${__('Go to Connected Apps, then Integrations.', 'bit-integrations')}
    • +
    • ${__('Open Custom Integrations and copy your API key.', 'bit-integrations')}
    • +
    ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Your API URL:', 'bit-integrations')} -
    - -
    {error.api_url}
    - {__('Example: {name}.withmoxie.com', 'bit-integrations')} -
    - {__('API Key:', 'bit-integrations')} -
    - -
    {error.api_key}
    - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/MoxieCRM/MoxieCRMCommonFunc.js b/frontend/src/components/AllIntegrations/MoxieCRM/MoxieCRMCommonFunc.js index 1af1ea7a2..a2428e220 100644 --- a/frontend/src/components/AllIntegrations/MoxieCRM/MoxieCRMCommonFunc.js +++ b/frontend/src/components/AllIntegrations/MoxieCRM/MoxieCRMCommonFunc.js @@ -49,41 +49,13 @@ export const checkMappedFields = moxiecrmConf => { return true } -export const moxiecrmAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_url || !confTmp.api_key) { - setError({ - api_url: !confTmp.api_url ? __("API URL can't be empty", 'bit-integrations') : '', - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - api_key: confTmp.api_key, - api_url: confTmp.api_url - } - - bitsFetch(requestParams, 'moxiecrm_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid api_url name & API key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + api_key: confTmp.api_key, + api_url: confTmp.api_url + } // export const getCustomFields = (confTmp, setConf, setLoading) => { // setLoading({ ...setLoading, customFields: true }); @@ -119,10 +91,7 @@ export const moxiecrmAuthentication = ( export const getAllClients = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, clients: true }) - const requestParams = { - api_key: confTmp.api_key, - api_url: confTmp.api_url - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'moxiecrm_fetch_all_clients').then(result => { if (result && result.success) { @@ -145,8 +114,7 @@ export const getAllPipelineStages = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, pipelineStages: true }) const requestParams = { - api_key: confTmp.api_key, - api_url: confTmp.api_url, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } diff --git a/frontend/src/components/AllIntegrations/Newsletter/NewsletterAuthorization.jsx b/frontend/src/components/AllIntegrations/Newsletter/NewsletterAuthorization.jsx index 90c9f9cc3..10a77b07a 100644 --- a/frontend/src/components/AllIntegrations/Newsletter/NewsletterAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Newsletter/NewsletterAuthorization.jsx @@ -1,86 +1,35 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { newsletterAuthentication } from './NewsletterCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function NewsletterAuthorization({ newsletterConf, setNewsletterConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !newsletterConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...newsletterConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setNewsletterConf(newConf) - } - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - {error.name &&
    {error.name}
    } -
    - - {!isInfo && ( -
    - -
    - -
    - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Newsletter/NewsletterCommonFunc.js b/frontend/src/components/AllIntegrations/Newsletter/NewsletterCommonFunc.js index 3ee59c4d1..a4019d583 100644 --- a/frontend/src/components/AllIntegrations/Newsletter/NewsletterCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Newsletter/NewsletterCommonFunc.js @@ -1,7 +1,3 @@ -/* eslint-disable no-console */ -/* eslint-disable no-else-return */ -import toast from 'react-hot-toast' -import bitsFetch from '../../../Utils/bitsFetch' import { __ } from '../../../Utils/i18nwrap' import { create } from 'mutative' @@ -40,25 +36,6 @@ export const checkMappedFields = newsletterConf => { return true } -export const newsletterAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.name) { - setError({ name: !confTmp.name ? __("Name can't be empty", 'bit-integrations') : '' }) - return - } - - setLoading({ ...loading, auth: true }) - bitsFetch({}, 'newsletter_authentication').then(result => { - if (result.success) { - setIsAuthorized(true) - toast.success(__('Connected Successfully', 'bit-integrations')) - setLoading({ ...loading, auth: false }) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Connection failed: install and active Newsletter plugin first!', 'bit-integrations')) - }) -} - export const staticFields = [ { key: 'email', label: __('Email', 'bit-integrations'), required: true }, { key: 'name', label: __('First Name', 'bit-integrations'), required: false }, diff --git a/frontend/src/components/AllIntegrations/Nimble/NimbleAuthorization.jsx b/frontend/src/components/AllIntegrations/Nimble/NimbleAuthorization.jsx index 3e60659ba..cfea4b411 100644 --- a/frontend/src/components/AllIntegrations/Nimble/NimbleAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Nimble/NimbleAuthorization.jsx @@ -1,41 +1,36 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { getAllFields, nimbleAuthentication } from './NimbleCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import Authorization from '../../Connections/Authorization' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import { getAllFields } from './NimbleCommonFunc' export default function NimbleAuthorization({ nimbleConf, setNimbleConf, step, setStep, - loading, setLoading, - isInfo + isInfo, + setSnackbar }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !nimbleConf?.default - setStep(2) - getAllFields(nimbleConf, setNimbleConf, setLoading) - } + const loadFields = useCallback( + connectionId => { + const nextConf = connectionId ? { ...nimbleConf, connection_id: connectionId } : nimbleConf + getAllFields(nextConf, setNimbleConf, setLoading, setSnackbar) + }, + [nimbleConf, setLoading, setNimbleConf, setSnackbar] + ) - const handleInput = e => { - const newConf = { ...nimbleConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setNimbleConf(newConf) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !nimbleConf?.peopleFields?.length && !nimbleConf?.companyFields?.length) { + loadFields() + } + setStep(value) + }, + [loadFields, nimbleConf?.companyFields?.length, nimbleConf?.peopleFields?.length, setStep] + ) const ActiveInstructions = `

    ${__('To Get API Token', 'bit-integrations')}

    @@ -47,79 +42,28 @@ export default function NimbleAuthorization({ ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('API Key:', 'bit-integrations')} -
    - -
    {error.api_key}
    - - - {__('To get API key, please visit', 'bit-integrations')} -   - - {__('Nimble API Token', 'bit-integrations')} - - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Nimble/NimbleCommonFunc.js b/frontend/src/components/AllIntegrations/Nimble/NimbleCommonFunc.js index cc88490fa..961c28836 100644 --- a/frontend/src/components/AllIntegrations/Nimble/NimbleCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Nimble/NimbleCommonFunc.js @@ -41,40 +41,12 @@ export const checkMappedFields = nimbleConf => { return true } -export const nimbleAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - const requestParams = { api_key: confTmp.api_key } - - bitsFetch(requestParams, 'nimble_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid API Key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp?.connection_id ? { connection_id: confTmp.connection_id } : { api_key: confTmp.api_key } -export const getAllFields = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, allFields: true }) - const requestParams = { api_key: confTmp.api_key } +export const getAllFields = (confTmp, setConf, setLoading, setSnackbar = null) => { + setLoading(prev => ({ ...prev, allFields: true })) + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'nimble_fetch_all_fields').then(result => { if (result && result.success) { @@ -90,15 +62,24 @@ export const getAllFields = (confTmp, setConf, setLoading) => { return prevConf }) - setLoading({ ...setLoading, event: false }) + setLoading(prev => ({ ...prev, allFields: false })) toast.success(__('Fields fetched successfully', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Fields fetched successfully', 'bit-integrations') }) + } return } - setLoading({ ...setLoading, event: false }) + setLoading(prev => ({ ...prev, allFields: false })) toast.error(__('Fields Not Found!', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Fields Not Found!', 'bit-integrations') }) + } return } - setLoading({ ...setLoading, event: false }) + setLoading(prev => ({ ...prev, allFields: false })) toast.error(__('Fields fetching failed', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Fields fetching failed', 'bit-integrations') }) + } }) } diff --git a/frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesAuthorization.jsx b/frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesAuthorization.jsx index ea479e079..cccb093fd 100644 --- a/frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesAuthorization.jsx @@ -1,116 +1,40 @@ -import { useState } from 'react' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function NinjaTablesAuthorization({ - formID, ninjaTablesConf, setNinjaTablesConf, step, nextPage, - isLoading, - setIsLoading, - setSnackbar, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - - const handleAuthorize = () => { - setIsLoading('auth') - const requestParams = { formID } - bitsFetch(requestParams, 'ninja_tables_authorize').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Ninja Tables successfully', 'bit-integrations') - }) - setIsLoading(false) - } else { - setShowAuthMsg(true) - setSnackbar({ - show: true, - msg: __( - result?.data - ? result.data - : 'Connection failed. Please make sure Ninja Tables is installed and activated', - 'bit-integrations' - ) - }) - setIsLoading(false) - } - }) - } + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( - <> - - -
    -
    - {__('Integration Name:', 'bit-integrations')} -
    - { - const newConf = { ...ninjaTablesConf } - newConf.name = e.target.value - setNinjaTablesConf(newConf) - }} - name="name" - value={ninjaTablesConf.name} - type="text" - placeholder={__('Integration Name...', 'bit-integrations')} - /> - - -
    - {showAuthMsg && ( -
    - - {__('Reminder:', 'bit-integrations')}{' '} - {__( - 'Please make sure Ninja Tables plugin is installed and activated.', - 'bit-integrations' - )} - -
    - )} - - {!isInfo && ( -
    -
    - -
    - )} -
    - + ) } diff --git a/frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesCommonFunc.js b/frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesCommonFunc.js index d0246cf42..9612a5a1e 100644 --- a/frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesCommonFunc.js +++ b/frontend/src/components/AllIntegrations/NinjaTables/NinjaTablesCommonFunc.js @@ -6,8 +6,7 @@ const API_ENDPOINTS = { REFRESH_TABLES: 'refresh_ninja_tables', REFRESH_ROWS: 'refresh_ninja_tables_rows', REFRESH_USERS: 'refresh_ninja_tables_users', - REFRESH_COLUMNS: 'refresh_ninja_tables_columns', - AUTHORIZE: 'ninja_tables_authorize' + REFRESH_COLUMNS: 'refresh_ninja_tables_columns' } const ACTIONS = { diff --git a/frontend/src/components/AllIntegrations/NotificationX/NotificationXAuthorization.jsx b/frontend/src/components/AllIntegrations/NotificationX/NotificationXAuthorization.jsx index 4f31f615d..6f57dfdd0 100644 --- a/frontend/src/components/AllIntegrations/NotificationX/NotificationXAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/NotificationX/NotificationXAuthorization.jsx @@ -1,84 +1,36 @@ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' -import { notificationXAuthentication } from './NotificationXCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function NotificationXAuthorization({ - formID, notificationXConf, setNotificationXConf, step, nextPage, - isLoading, - setIsLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - const handleInput = e => { - const newConf = { ...notificationXConf } - newConf[e.target.name] = e.target.value - setNotificationXConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - -
    {error.name}
    - - - - {!isInfo && ( - <> - -
    - - - )} -
    + ) + }} + /> ) } diff --git a/frontend/src/components/AllIntegrations/NotificationX/NotificationXCommonFunc.js b/frontend/src/components/AllIntegrations/NotificationX/NotificationXCommonFunc.js index 89e20293f..c4bc5eda1 100644 --- a/frontend/src/components/AllIntegrations/NotificationX/NotificationXCommonFunc.js +++ b/frontend/src/components/AllIntegrations/NotificationX/NotificationXCommonFunc.js @@ -60,39 +60,3 @@ export const refreshNotificationsBySource = ( setIsLoading(false) }) } - -export const notificationXAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - setIsLoading -) => { - if (!confTmp.name) { - setError({ - name: !confTmp.name ? __("Integration name can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setIsLoading(true) - - const requestParams = { name: confTmp.name } - - bitsFetch(requestParams, 'notificationx_authorize') - .then(result => { - if (result && result.success) { - setIsAuthorized(true) - } else { - const errorMsg = typeof result?.data === 'string' ? result.data : result?.message - setError({ name: errorMsg || __('Authorization failed', 'bit-integrations') }) - } - - setIsLoading(false) - }) - .catch(() => { - setError({ name: __('Authorization failed', 'bit-integrations') }) - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/Notion/Notion.jsx b/frontend/src/components/AllIntegrations/Notion/Notion.jsx index f51578d61..0c5b5c326 100644 --- a/frontend/src/components/AllIntegrations/Notion/Notion.jsx +++ b/frontend/src/components/AllIntegrations/Notion/Notion.jsx @@ -1,12 +1,11 @@ /* eslint-disable import/no-named-as-default */ /* eslint-disable import/no-named-as-default-member */ /* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import { useNavigate } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import StepPage from '../../Utilities/StepPage' import Steps from '../../Utilities/Steps' -import { setGrantTokenResponse } from '../IntegrationHelpers/GoogleIntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import NotionAuthorization from './NotionAuthorization' import { nextPage, saveConfig } from './NotionCommonFunc' @@ -14,13 +13,9 @@ import NotionIntegLayout from './NotionIntegLayout' function Notion({ formFields, setFlow, flow, allIntegURL }) { const navigate = useNavigate() - useEffect(() => { - window.opener && setGrantTokenResponse('notion') - }, []) const [step, setStep] = useState(1) const [loading, setLoading] = useState({ - auth: false, list: false, page: false, field: false @@ -48,8 +43,6 @@ function Notion({ formFields, setFlow, flow, allIntegURL }) { setStep={setStep} notionConf={notionConf} setNotionConf={setNotionConf} - loading={loading} - setLoading={setLoading} /> {/* --- STEP 2 --- */} diff --git a/frontend/src/components/AllIntegrations/Notion/NotionAuthorization.jsx b/frontend/src/components/AllIntegrations/Notion/NotionAuthorization.jsx index 4042f6fdd..8af55401e 100644 --- a/frontend/src/components/AllIntegrations/Notion/NotionAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Notion/NotionAuthorization.jsx @@ -1,33 +1,9 @@ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' -import { $appConfigState } from '../../../GlobalStates' -import AuthorizeButton from '../../Utilities/AuthorizeButton' -import ErrorField from '../../Utilities/ErrorField' -import GetInfo from '../../Utilities/GetInfo' -import Input from '../../Utilities/Input' -import Note from '../../Utilities/Note' -import StepPage from '../../Utilities/StepPage' -import { getAllDatabaseLists, handleAuthorize, handleInput } from './NotionCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' import { __ } from '../../../Utils/i18nwrap' +import Authorization from '../../Connections/Authorization' -function NotionAuthorization({ notionConf, setNotionConf, step, setStep, isInfo, loading, setLoading }) { - const btcbi = useRecoilValue($appConfigState) - const [authorized, setAuthorized] = useState(false) - const [error, setError] = useState({ clientId: '', clientSecret: '' }) - const nextPage = async () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - setStep(2) - setLoading({ ...loading, page: true }) - const data = await getAllDatabaseLists(notionConf, setNotionConf) - if (data) { - setLoading({ ...loading, page: false }) - } - } - +function NotionAuthorization({ notionConf, setNotionConf, step, setStep, isInfo }) { const note = `

    ${__('Step of get Client Id & Client Secret', 'bit-integrations')}

      @@ -49,64 +25,32 @@ function NotionAuthorization({ notionConf, setNotionConf, step, setStep, isInfo,
    ` return ( - - - -
    - {/* Notion Authorization */} - - handleInput(e, notionConf, setNotionConf, error, setError)} - /> - - - handleInput(e, notionConf, setNotionConf, error, setError)} - /> - - handleInput(e, notionConf, setNotionConf, error, setError)} - /> - - - {!isInfo && ( - - handleAuthorize( - notionConf, - setNotionConf, - error, - setError, - setAuthorized, - loading, - setLoading, - btcbi - ) - } - nextPage={nextPage} - auth={authorized} - loading={loading.auth} - /> - )} -
    - - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Notion/NotionCommonFunc.js b/frontend/src/components/AllIntegrations/Notion/NotionCommonFunc.js index 3686a8cf6..97722304b 100644 --- a/frontend/src/components/AllIntegrations/Notion/NotionCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Notion/NotionCommonFunc.js @@ -20,95 +20,14 @@ export const handleInput = (e, conf, setConf, error, setError) => { setConf(newConf) } -export const handleAuthorize = ( - conf, - setConf, - error, - setError, - setAuthorized, - loading, - setLoading, - btcbi -) => { - if (!conf.clientId || !conf.clientSecret) { - setError({ - clientId: !conf.clientId ? __("Client Id can't be empty") : '', - clientSecret: !conf.clientSecret ? __("Client Secret can't be empty") : '' - }) - return - } - setError({}) - setLoading({ ...loading, auth: true }) - const apiEndpoint = `https://api.notion.com/v1/oauth/authorize?client_id=${ - conf.clientId - }&response_type=code&owner=user&state=${encodeURIComponent( - window.location.href - )}/redirect&redirect_uri=${encodeURIComponent(`${btcbi.api}`)}/redirect` - const authWindow = window.open(apiEndpoint, 'Notion', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isAuthRedirectLocation = false - const notionStoreValue = localStorage.getItem('__notion') - if (notionStoreValue) { - isAuthRedirectLocation = true - grantTokenResponse = JSON.parse(notionStoreValue) - localStorage.removeItem('__notion') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isAuthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - toast.error( - `${__('Authorization failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - ) - setLoading({ ...loading, auth: false }) - } else { - tokenHelper(grantTokenResponse, conf, setConf, setAuthorized, loading, setLoading, btcbi) - } - } - }, 500) -} - -const tokenHelper = (grantToken, conf, setConf, setAuthorized, loading, setLoading, btcbi) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = conf.clientId - tokenRequestParams.clientSecret = conf.clientSecret - // eslint-disable-next-line no-undef - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - bitsFetch(tokenRequestParams, 'notion_authorization').then(result => { - if (result && result.success) { - const newConf = { ...conf } - newConf.tokenDetails = result.data - setConf(newConf) - setAuthorized(true) - toast.success(__('Authorized Successfully')) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - toast.error( - `${__('Authorization failed Cause:')}${result.data.data || result.data}. ${__( - 'please try again' - )}` - ) - } else { - toast.error(__('Authorization failed. please try again')) - } - setLoading({ ...loading, auth: false }) - }) -} +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { accessToken: conf?.tokenDetails?.access_token } export const getAllDatabaseLists = async (conf, setConf, loading, setLoading) => { setLoading && setLoading({ ...loading, list: true }) - const requestParams = { accessToken: conf.tokenDetails.access_token } + const requestParams = buildAuthRequestParams(conf) const result = await bitsFetch(requestParams, 'notion_database_lists') if (result.success && result.data.results) { const data = result?.data.results @@ -139,7 +58,7 @@ export const getAllDatabaseLists = async (conf, setConf, loading, setLoading) => export const getFieldsProperties = async (conf, setConf, loading, setLoading) => { setLoading && setLoading({ ...loading, field: true }) const requestParams = { - accessToken: conf.tokenDetails.access_token, + ...buildAuthRequestParams(conf), databaseId: conf.databaseId } const result = await bitsFetch(requestParams, 'notion_database_properties') diff --git a/frontend/src/components/AllIntegrations/Notion/NotionIntegLayout.jsx b/frontend/src/components/AllIntegrations/Notion/NotionIntegLayout.jsx index c2864fdf2..cdc38861a 100644 --- a/frontend/src/components/AllIntegrations/Notion/NotionIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/Notion/NotionIntegLayout.jsx @@ -45,7 +45,7 @@ function NotionIntegLayout({ notionConf, setNotionConf, formFields, loading, set return (
    - {!loading.page && notionConf?.default?.databaseLists && ( + {!loading.page && (
    {__('Database List:', 'bit-integrations')} - -
    - {__('User Name:', 'bit-integrations')} -
    - -
    {error.user_name}
    - -
    - {__('API Token:', 'bit-integrations')} -
    - -
    {error.api_token}
    - - - {__('To Get User Name & API Token, Please Visit', 'bit-integrations')} -   - - {__('NutshellCRM User Name & API Token', 'bit-integrations')} - - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/NutshellCRM/NutshellCRMCommonFunc.js b/frontend/src/components/AllIntegrations/NutshellCRM/NutshellCRMCommonFunc.js index d35598942..57c349833 100644 --- a/frontend/src/components/AllIntegrations/NutshellCRM/NutshellCRMCommonFunc.js +++ b/frontend/src/components/AllIntegrations/NutshellCRM/NutshellCRMCommonFunc.js @@ -45,49 +45,18 @@ export const checkMappedFields = nutshellCRMConf => { return true } -export const nutshellCRMAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.user_name || !confTmp.api_token) { - setError({ - user_name: !confTmp.user_name ? __("User Name can't be empty", 'bit-integrations') : '', - api_token: !confTmp.api_token ? __("API Token can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - user_name: confTmp.user_name, - api_token: confTmp.api_token - } - - bitsFetch(requestParams, 'nutshellcrm_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid User Name & Api Token', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + user_name: confTmp.user_name, + api_token: confTmp.api_token + } export const getAllCompanies = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, companies: true }) + setLoading(prev => ({ ...prev, companies: true })) - const requestParams = { - user_name: confTmp.user_name, - api_token: confTmp.api_token - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'nutshellcrm_fetch_all_companies').then(result => { if (result && result.success) { @@ -96,23 +65,20 @@ export const getAllCompanies = (confTmp, setConf, setLoading) => { newConf.companies = result.data } setConf(newConf) - setLoading({ ...setLoading, companies: false }) + setLoading(prev => ({ ...prev, companies: false })) toast.success(__('Companies fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, companies: false }) + setLoading(prev => ({ ...prev, companies: false })) toast.error(__('Companies fetching failed', 'bit-integrations')) }) } export const getAllContacts = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, contacts: true }) + setLoading(prev => ({ ...prev, contacts: true })) - const requestParams = { - user_name: confTmp.user_name, - api_token: confTmp.api_token - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'nutshellcrm_fetch_all_contacts').then(result => { if (result && result.success) { @@ -121,23 +87,20 @@ export const getAllContacts = (confTmp, setConf, setLoading) => { newConf.contacts = result.data } setConf(newConf) - setLoading({ ...setLoading, contacts: false }) + setLoading(prev => ({ ...prev, contacts: false })) toast.success(__('Contacts fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, contacts: false }) + setLoading(prev => ({ ...prev, contacts: false })) toast.error(__('Contacts fetching failed', 'bit-integrations')) }) } export const getAllSources = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, sources: true }) + setLoading(prev => ({ ...prev, sources: true })) - const requestParams = { - user_name: confTmp.user_name, - api_token: confTmp.api_token - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'nutshellcrm_fetch_all_sources').then(result => { if (result && result.success) { @@ -146,22 +109,19 @@ export const getAllSources = (confTmp, setConf, setLoading) => { newConf.sources = result.data } setConf(newConf) - setLoading({ ...setLoading, sources: false }) + setLoading(prev => ({ ...prev, sources: false })) toast.success(__('Sources fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, sources: false }) + setLoading(prev => ({ ...prev, sources: false })) toast.error(__('Sources fetching failed', 'bit-integrations')) }) } export const getAllTags = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, tags: true }) + setLoading(prev => ({ ...prev, tags: true })) - const requestParams = { - user_name: confTmp.user_name, - api_token: confTmp.api_token - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'nutshellcrm_fetch_all_tags').then(result => { if (result && result.success) { @@ -170,23 +130,20 @@ export const getAllTags = (confTmp, setConf, setLoading) => { newConf.tags = result.data } setConf(newConf) - setLoading({ ...setLoading, tags: false }) + setLoading(prev => ({ ...prev, tags: false })) toast.success(__('Tags fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, tags: false }) + setLoading(prev => ({ ...prev, tags: false })) toast.error(__('Tags fetching failed', 'bit-integrations')) }) } export const getAllProducts = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, products: true }) + setLoading(prev => ({ ...prev, products: true })) - const requestParams = { - user_name: confTmp.user_name, - api_token: confTmp.api_token - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'nutshellcrm_fetch_all_products').then(result => { if (result && result.success) { @@ -195,23 +152,20 @@ export const getAllProducts = (confTmp, setConf, setLoading) => { newConf.products = result.data } setConf(newConf) - setLoading({ ...setLoading, products: false }) + setLoading(prev => ({ ...prev, products: false })) toast.success(__('Products fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, products: false }) + setLoading(prev => ({ ...prev, products: false })) toast.error(__('Products fetching failed', 'bit-integrations')) }) } export const getAllCompanyTypes = (confTmp, setConf, setLoading) => { - setLoading({ ...setLoading, companyTypes: true }) + setLoading(prev => ({ ...prev, companyTypes: true })) - const requestParams = { - user_name: confTmp.user_name, - api_token: confTmp.api_token - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'nutshellcrm_fetch_all_companytypes').then(result => { if (result && result.success) { @@ -220,12 +174,12 @@ export const getAllCompanyTypes = (confTmp, setConf, setLoading) => { newConf.companyTypes = result.data } setConf(newConf) - setLoading({ ...setLoading, companyTypes: false }) + setLoading(prev => ({ ...prev, companyTypes: false })) toast.success(__('CompanyTypes fetched successfully', 'bit-integrations')) return } - setLoading({ ...setLoading, companyTypes: false }) + setLoading(prev => ({ ...prev, companyTypes: false })) toast.error(__('CompanyTypes fetching failed', 'bit-integrations')) }) } diff --git a/frontend/src/components/AllIntegrations/OmniSend/OmniSendAuthorization.jsx b/frontend/src/components/AllIntegrations/OmniSend/OmniSendAuthorization.jsx index 2d44bbe1a..c8b3e23a4 100644 --- a/frontend/src/components/AllIntegrations/OmniSend/OmniSendAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/OmniSend/OmniSendAuthorization.jsx @@ -1,38 +1,10 @@ /* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { handleOmniSendAuthorize } from './OmniSendCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import Authorization from '../../Connections/Authorization' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' -export default function OmniSendAuthorization({ - omniSendConf, - setOmniSendConf, - step, - setstep, - loading, - setLoading, - setSnackbar, - isInfo -}) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - } - - const handleInput = e => { - const newConf = { ...omniSendConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setOmniSendConf(newConf) - } +export default function OmniSendAuthorization({ omniSendConf, setOmniSendConf, step, setstep, isInfo }) { const note = `

    ${__('Step of generate API token:', 'bit-integrations')}

      @@ -52,86 +24,22 @@ export default function OmniSendAuthorization({ ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('API Token:', 'bit-integrations')} -
      - -
      {error.api_key}
      - - - {__('To Get API Token, Please Visit', 'bit-integrations')} -   - - {__('OmniSend API Token', 'bit-integrations')} - - -
      -
      - - {!isInfo && ( -
      - -
      - -
      - )} - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/OmniSend/OmniSendCommonFunc.js b/frontend/src/components/AllIntegrations/OmniSend/OmniSendCommonFunc.js index d77d72651..41dfc86b4 100644 --- a/frontend/src/components/AllIntegrations/OmniSend/OmniSendCommonFunc.js +++ b/frontend/src/components/AllIntegrations/OmniSend/OmniSendCommonFunc.js @@ -1,7 +1,5 @@ /* eslint-disable no-else-return */ -import toast from 'react-hot-toast' import { __ } from '../../../Utils/i18nwrap' -import bitsFetch from '../../../Utils/bitsFetch' export const handleInput = ( e, @@ -47,36 +45,3 @@ export const checkMappedFields = omniSendConf => { } return true } -export const handleOmniSendAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { api_key: confTmp.api_key } - - bitsFetch(requestParams, 'Omnisend_authorization').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - setConf(newConf) - setisAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed', 'bit-integrations')) - }) -} diff --git a/frontend/src/components/AllIntegrations/OneDrive/OneDrive.jsx b/frontend/src/components/AllIntegrations/OneDrive/OneDrive.jsx index e39b77d93..82a24213c 100644 --- a/frontend/src/components/AllIntegrations/OneDrive/OneDrive.jsx +++ b/frontend/src/components/AllIntegrations/OneDrive/OneDrive.jsx @@ -1,11 +1,9 @@ -/* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { setGrantTokenResponse } from '../IntegrationHelpers/GoogleIntegrationHelpers' import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import OneDriveAuthorization from './OneDriveAuthorization' @@ -31,10 +29,6 @@ function OneDrive({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('oneDrive') - }, []) - const saveConfig = () => { saveActionConf({ flow, @@ -56,14 +50,10 @@ function OneDrive({ formFields, setFlow, flow, allIntegURL }) { {/* STEP 1 */}
      { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - getAllOneDriveFolders(flowID, oneDriveConf, setOneDriveConf, setIsLoading) - setStep(2) - } - - const handleInput = e => { - const newConf = { ...oneDriveConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setOneDriveConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function OneDriveAuthorization({ oneDriveConf, setOneDriveConf, step, setStep, isInfo }) { + const note = ` +

      ${__('OneDrive OAuth setup', 'bit-integrations')}

      +
        +
      • ${__('Create app in Azure Portal and add redirect URI from integration settings.', 'bit-integrations')}
      • +
      • ${__('Use delegated permissions for OneDrive read/write with offline access.', 'bit-integrations')}
      • +
      + ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Homepage URL:', 'bit-integrations')} -
      - - -
      - {__('Authorized Redirect URIs:', 'bit-integrations')} -
      - - - - {__('To Get Client Id & Secret, Please Visit', 'bit-integrations')} -   - - {__('Azure Portal', 'bit-integrations')} - - - -
      - {__('OneDrive Client id:', 'bit-integrations')} -
      - -
      {error.clientId}
      - -
      - {__('OneDrive Client Secret:', 'bit-integrations')} -
      - -
      {error.clientSecret}
      - - {!isInfo && ( - <> - -
      - - - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/OneDrive/OneDriveCommonFunc.js b/frontend/src/components/AllIntegrations/OneDrive/OneDriveCommonFunc.js index 4e8af869c..276f3eb24 100644 --- a/frontend/src/components/AllIntegrations/OneDrive/OneDriveCommonFunc.js +++ b/frontend/src/components/AllIntegrations/OneDrive/OneDriveCommonFunc.js @@ -50,6 +50,15 @@ const folderChange = (oneDriveConf, formID, setOneDriveConf, setIsLoading, setSn return newConf } +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const getAllOneDriveFolders = ( flowID, oneDriveConf, @@ -60,9 +69,7 @@ export const getAllOneDriveFolders = ( setIsLoading(true) const queryParams = { flowID: flowID ?? null, - clientId: oneDriveConf.clientId, - clientSecret: oneDriveConf.clientSecret, - tokenDetails: oneDriveConf.tokenDetails + ...buildAuthRequestParams(oneDriveConf) } const loadPostTypes = bitsFetch(queryParams, 'oneDrive_get_all_folders') .then(result => { @@ -103,9 +110,7 @@ export const getSingleOneDriveFolders = ( const refreshSubFoldersRequestParams = { formID, dataCenter: oneDriveConf.dataCenter, - clientId: oneDriveConf.clientId, - clientSecret: oneDriveConf.clientSecret, - tokenDetails: oneDriveConf.tokenDetails, + ...buildAuthRequestParams(oneDriveConf), team: oneDriveConf.team, folder, teamType: 'teamType' in oneDriveConf ? 'private' : 'team' @@ -141,115 +146,3 @@ export const getSingleOneDriveFolders = ( }) .catch(() => setIsLoading(false)) } - -// export const getSingleOneDriveFolders = (flowID, oneDriveConf, setOneDriveConf) => { -// // const folder = ind ? oneDriveConf.folderMap[ind] : oneDriveConf.folder -// const queryParams = { -// flowID: flowID ?? null, -// clientId: oneDriveConf.clientId, -// clientSecret: oneDriveConf.clientSecret, -// tokenDetails: oneDriveConf.tokenDetails, -// subFolderId: oneDriveConf.id, -// } -// const loadPostTypes = bitsFetch(queryParams, 'oneDrive_get_single_folder') -// .then(result => { -// if (result && result.success) { -// const newConf = { ...oneDriveConf } -// if (result.data.oneDriveFoldersList) { -// newConf.foldersList = result.data.oneDriveFoldersList -// newConf.tokenDetails = result.data.tokenDetails -// } - -// setOneDriveConf(newConf) -// return 'OneDrive single Folders List refreshed successfully' -// } else { -// return 'OneDrive single Folders List refresh failed. please try again' -// } -// }) -// toast.promise(loadPostTypes, { -// success: data => data, -// error: __('Error Occurred', 'bit-integrations'), -// loading: __('Loading OneDrive Folders List...', 'bit-integrations'), -// }) -// } - -export const handleAuthorize = (confTmp, setConf, setIsAuthorized, setIsLoading, setError, btcbi) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Client Secret can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - const scopes = 'onedrive.readwrite offline_access Files.ReadWrite.All' - // eslint-disable-next-line no-undef - const apiEndpoint = `https://login.live.com/oauth20_authorize.srf?client_id=${ - confTmp.clientId - }&scope=${scopes}&access_type=offline&prompt=consent&response_type=code&state=${encodeURIComponent( - window.location.href - )}/redirect&redirect_uri=${encodeURIComponent(`${btcbi.api}/redirect`)}` - const authWindow = window.open(apiEndpoint, 'oneDrive', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isAuthRedirectLocation = false - const bitsOneDrive = localStorage.getItem('__oneDrive') - if (bitsOneDrive) { - isAuthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsOneDrive) - localStorage.removeItem('__oneDrive') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isAuthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - toast.error( - `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - ) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper(grantTokenResponse, newConf, setConf, setIsAuthorized, setIsLoading, btcbi) - } - } - }, 500) -} - -const tokenHelper = (grantToken, confTmp, setConf, setIsAuthorized, setIsLoading, btcbi) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - // eslint-disable-next-line no-undef - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - - bitsFetch(tokenRequestParams, 'oneDrive_authorization').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setIsAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - toast.error( - `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - ) - } else { - toast.error(__('Authorization failed. please try again', 'bit-integrations')) - } - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/OneHashCRM/OneHashCRMAuthorization.jsx b/frontend/src/components/AllIntegrations/OneHashCRM/OneHashCRMAuthorization.jsx index c859081b7..ad9b25e00 100644 --- a/frontend/src/components/AllIntegrations/OneHashCRM/OneHashCRMAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/OneHashCRM/OneHashCRMAuthorization.jsx @@ -1,161 +1,63 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ /* eslint-disable no-unused-expressions */ -import { useState } from 'react' -import { toast } from 'react-hot-toast' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { oneHashCRMAuthentication } from './OneHashCRMCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import Authorization from '../../Connections/Authorization' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' export default function OneHashCRMAuthorization({ oneHashCRMConf, setOneHashCRMConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_token: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !oneHashCRMConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...oneHashCRMConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setOneHashCRMConf(newConf) - } - - const handleApiTokenLink = () => { - oneHashCRMConf.domain - ? window.open(`${oneHashCRMConf.domain}/app/user`, '_blank', 'noreferrer') - : toast.error(__('Access API URL is required!', 'bit-integrations')) - } - - const ActiveInstructions = ` -

      ${__('Get API Token', 'bit-integrations')}

      -
        -
      • ${__( - "Go to your OneHash CRM's user dashboard and click the profile buttom from Right top corner", - 'bit-integrations' - )}
      • -
      • ${__('Then select "My Settings"', 'bit-integrations')}
      • -
      • ${__('Then go to "API Access → Generates Keys"', 'bit-integrations')}
      • -
      ` + const note = ` +

      ${__('Get API credentials', 'bit-integrations')}

      +
        +
      • ${__( + 'Go to your OneHash CRM user dashboard and click profile from top-right corner.', + 'bit-integrations' + )}
      • +
      • ${__('Select My Settings.', 'bit-integrations')}
      • +
      • ${__('Then go to API Access → Generate Keys.', 'bit-integrations')}
      • +
      ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Access API URL:', 'bit-integrations')} -
      - -
      {error.domain}
      - -
      - {__('API Key:', 'bit-integrations')} -
      - -
      {error.api_key}
      - -
      - {__('API Secret:', 'bit-integrations')} -
      - -
      {error.api_secret}
      - - - {__('To Get API Key & API Secret, Please Visit', 'bit-integrations')} -   - - {__('OneHashCRM API Key & API Secret', 'bit-integrations')} - - -
      -
      - - {!isInfo && ( -
      - -
      - -
      - )} - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/OneHashCRM/OneHashCRMCommonFunc.js b/frontend/src/components/AllIntegrations/OneHashCRM/OneHashCRMCommonFunc.js index 2c5737e79..e452ce1c7 100644 --- a/frontend/src/components/AllIntegrations/OneHashCRM/OneHashCRMCommonFunc.js +++ b/frontend/src/components/AllIntegrations/OneHashCRM/OneHashCRMCommonFunc.js @@ -1,7 +1,5 @@ /* eslint-disable no-console */ /* eslint-disable no-else-return */ -import toast from 'react-hot-toast' -import bitsFetch from '../../../Utils/bitsFetch' import { __ } from '../../../Utils/i18nwrap' export const handleInput = (e, oneHashCRMConf, setOneHashCRMConf) => { @@ -44,43 +42,3 @@ export const checkMappedFields = oneHashCRMConf => { } return true } - -export const oneHashCRMAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key || !confTmp.api_secret || !confTmp.domain) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '', - api_secret: !confTmp.api_secret ? __("Api Secret can't be empty", 'bit-integrations') : '', - domain: !confTmp.domain ? __("API URL can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - api_key: confTmp.api_key, - api_secret: confTmp.api_secret, - domain: confTmp.domain - } - - bitsFetch(requestParams, 'onehashcrm_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error( - __('Authorized failed, Please enter valid API Key & Secret or Access Api URL', 'bit-integrations') - ) - }) -} diff --git a/frontend/src/components/AllIntegrations/PCloud/PCloud.jsx b/frontend/src/components/AllIntegrations/PCloud/PCloud.jsx index 3cdae0a8f..17917e12f 100644 --- a/frontend/src/components/AllIntegrations/PCloud/PCloud.jsx +++ b/frontend/src/components/AllIntegrations/PCloud/PCloud.jsx @@ -1,12 +1,11 @@ /* eslint-disable no-console */ /* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' -import { useNavigate, useParams } from 'react-router' +import { useNavigate } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { setGrantTokenResponse } from '../IntegrationHelpers/GoogleIntegrationHelpers' import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import PCloudAuthorization from './PCloudAuthorization' @@ -15,7 +14,6 @@ import PCloudIntegLayout from './PCloudIntegLayout' function PCloud({ formFields, setFlow, flow, allIntegURL }) { const navigate = useNavigate() - const { flowID } = useParams() const [isLoading, setIsLoading] = useState(false) const [step, setStep] = useState(1) const [snack, setSnackbar] = useState({ show: false }) @@ -30,10 +28,6 @@ function PCloud({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('pCloud') - }, []) - const saveConfig = () => { saveActionConf({ flow, @@ -55,14 +49,10 @@ function PCloud({ formFields, setFlow, flow, allIntegURL }) { {/* STEP 1 */} {/* STEP 2 */} @@ -76,7 +66,6 @@ function PCloud({ formFields, setFlow, flow, allIntegURL }) { }) }}> { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - getAllPCloudFolders(pCloudConf, setPCloudConf, 'fetch') - setStep(2) - } - - const handleInput = e => { - const newConf = { ...pCloudConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setPCloudConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function PCloudAuthorization({ pCloudConf, setPCloudConf, step, setStep, isInfo }) { + const note = ` +

      ${__('PCloud OAuth setup', 'bit-integrations')}

      +
        +
      • ${__('Create an app from PCloud API apps.', 'bit-integrations')}
      • +
      • ${__('Set the redirect URI exactly as shown below.', 'bit-integrations')}
      • +
      • ${__('Use your app Client ID and Client Secret to authorize.', 'bit-integrations')}
      • +
      + ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Authorized Redirect URIs:', 'bit-integrations')} -
      - - - - {__('To Get Client Id & Secret, Please Visit', 'bit-integrations')} -   - - {__('pCloud API apps', 'bit-integrations')} - - - -
      - {__('PCloud Client id:', 'bit-integrations')} -
      - -
      {error.clientId}
      - -
      - {__('PCloud Client Secret:', 'bit-integrations')} -
      - -
      {error.clientSecret}
      - - {!isInfo && ( - <> - -
      - - - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/PCloud/PCloudCommonFunc.js b/frontend/src/components/AllIntegrations/PCloud/PCloudCommonFunc.js index 7777ba590..8c3aa6d80 100644 --- a/frontend/src/components/AllIntegrations/PCloud/PCloudCommonFunc.js +++ b/frontend/src/components/AllIntegrations/PCloud/PCloudCommonFunc.js @@ -25,8 +25,11 @@ export const checkMappedFields = pCloudConf => { return true } +const buildAuthRequestParams = conf => + conf?.connection_id ? { connection_id: conf.connection_id } : { tokenDetails: conf.tokenDetails } + export const getAllPCloudFolders = (pCloudConf, setPCloudConf, type) => { - const queryParams = { tokenDetails: pCloudConf.tokenDetails } + const queryParams = buildAuthRequestParams(pCloudConf) const loadPostTypes = bitsFetch(queryParams, 'pCloud_get_all_folders').then(result => { if (result && result.success) { const newConf = { ...pCloudConf } @@ -48,88 +51,3 @@ export const getAllPCloudFolders = (pCloudConf, setPCloudConf, type) => { loading: __('Loading PCloud Folders List...', 'bit-integrations') }) } - -export const handleAuthorization = ( - confTmp, - setConf, - setIsAuthorized, - setIsLoading, - setError, - btcbi -) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Client Secret can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - // eslint-disable-next-line no-undef - const apiEndpoint = `https://my.pcloud.com/oauth2/authorize?client_id=${ - confTmp.clientId - }&response_type=code&redirect_uri=${btcbi.api}/redirect&state=${encodeURIComponent( - window.location.href - )}/redirect` - const authWindow = window.open(apiEndpoint, 'pCloud', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isAuthRedirectLocation = false - const bitsPCloud = localStorage.getItem('__pCloud') - if (bitsPCloud) { - isAuthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsPCloud) - localStorage.removeItem('__pCloud') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isAuthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - toast.error( - `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - ) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper(grantTokenResponse, newConf, setConf, setIsAuthorized, setIsLoading, btcbi) - } - } - }, 500) -} - -const tokenHelper = (grantToken, confTmp, setConf, setIsAuthorized, setIsLoading, btcbi) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - // eslint-disable-next-line no-undef - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - - bitsFetch(tokenRequestParams, 'pCloud_authorization').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setIsAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if ((result && result.data) || (!result.success && typeof result.data === 'string')) { - toast.error( - `${__('Authorization failed Cause:', 'bit-integrations')}${result.data}. ${__( - 'please try again', - 'bit-integrations' - )}` - ) - } else { - toast.error(__('Authorization failed. please try again', 'bit-integrations')) - } - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/PCloud/PCloudIntegLayout.jsx b/frontend/src/components/AllIntegrations/PCloud/PCloudIntegLayout.jsx index c32e6aefb..889c98e41 100644 --- a/frontend/src/components/AllIntegrations/PCloud/PCloudIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/PCloud/PCloudIntegLayout.jsx @@ -5,7 +5,7 @@ import PCloudFieldMap from './PCloudFieldMap' import PCloudActions from './PCloudActions' import { getAllPCloudFolders } from './PCloudCommonFunc' -export default function PCloudIntegLayout({ flowID, formFields, pCloudConf, setPCloudConf }) { +export default function PCloudIntegLayout({ formFields, pCloudConf, setPCloudConf }) { return ( <>
      diff --git a/frontend/src/components/AllIntegrations/PaidMembershipPro/PaidMembershipProAuthorization.jsx b/frontend/src/components/AllIntegrations/PaidMembershipPro/PaidMembershipProAuthorization.jsx index 886fd770b..07de48799 100644 --- a/frontend/src/components/AllIntegrations/PaidMembershipPro/PaidMembershipProAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/PaidMembershipPro/PaidMembershipProAuthorization.jsx @@ -1,103 +1,38 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function PaidMembershipProAuthorization({ - formID, paidMembershipProConf, setPaidMembershipProConf, step, setStep, - isLoading, - setIsLoading, - setSnackbar + isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'paid_membership_pro_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Paid Membership Pro Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(paidMembershipProConf) - newConf[e.target.name] = e.target.value - setPaidMembershipProConf(newConf) - } - return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - - {isLoading === 'auth' && ( -
      - - {__('Checking if Paid Membership Pro is active!!!', 'bit-integrations')} -
      - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
      - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'Paid Membership Pro' - )} -
      - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/PeepSo/PeepSoAuthorization.jsx b/frontend/src/components/AllIntegrations/PeepSo/PeepSoAuthorization.jsx index 117963e47..b0d35db11 100644 --- a/frontend/src/components/AllIntegrations/PeepSo/PeepSoAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/PeepSo/PeepSoAuthorization.jsx @@ -1,118 +1,30 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' -export default function PeepSoAuthorization({ - peepSoConf, - setPeepSoConf, - step, - nextPage, - isLoading, - setIsLoading, - setSnackbar, - info -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'peep_so_authorize').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with PeepSo Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = { ...peepSoConf } - newConf[e.target.name] = e.target.value - setPeepSoConf(newConf) - } - +export default function PeepSoAuthorization({ peepSoConf, setPeepSoConf, step, nextPage, isInfo }) { + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - - {isLoading === 'auth' && ( -
      - - {__('Checking if PeepSo is authorized!!!', 'bit-integrations')} -
      - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
      -
      -
      - -
      -
      - {__('PeepSo is not activated or not installed', 'bit-integrations')} -
      -
      -
      - )} - - {showAuthMsg && isAuthorized && !isLoading && ( -
      -
      - -
      -
      {__('PeepSo is activated', 'bit-integrations')}
      -
      - )} - - {!info && ( - <> - -
      - - - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/PerfexCRM/PerfexCRMAuthorization.jsx b/frontend/src/components/AllIntegrations/PerfexCRM/PerfexCRMAuthorization.jsx index 2499ff3bb..855f3b829 100644 --- a/frontend/src/components/AllIntegrations/PerfexCRM/PerfexCRMAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/PerfexCRM/PerfexCRMAuthorization.jsx @@ -1,156 +1,62 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' -import { toast } from 'react-hot-toast' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { perfexCRMAuthentication } from './PerfexCRMCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + export default function PerfexCRMAuthorization({ perfexCRMConf, setPerfexCRMConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_token: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !perfexCRMConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...perfexCRMConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setPerfexCRMConf(newConf) - } - - const handleApiTokenLink = () => { - perfexCRMConf.domain - ? window.open(`${perfexCRMConf.domain}/admin/api/api_management`, '_blank', 'noreferrer') - : toast.error(__('Access API URL is required!', 'bit-integrations')) - } - - const ActiveInstructions = ` -

      ${__('Get API Token', 'bit-integrations')}

      -
        -
      • ${__( - "Go to your Perfex CRM's Admin area and select the following menu item: SETUP → MODULES.", - 'bit-integrations' - )}
      • -
      • ${__( - 'Select the extracted upload.zip at Module installation selection prompt and press INSTALL.', - 'bit-integrations' - )}
      • -
      • ${__( - 'Find the newly installed module in the list, press ACTIVATE and enter your license key.', - 'bit-integrations' - )}
      • -
      • ${__( - "Go to your Perfex's CRM backend as an admin, go to API → API Management, and create a new token.", - 'bit-integrations' - )}
      • -
      ` + const note = ` +

      ${__('Get API Token', 'bit-integrations')}

      +
        +
      • ${__( + "Go to your Perfex CRM's Admin area and select the following menu item: SETUP → MODULES.", + 'bit-integrations' + )}
      • +
      • ${__( + 'Select the extracted upload.zip at Module installation selection prompt and press INSTALL.', + 'bit-integrations' + )}
      • +
      • ${__( + 'Find the newly installed module in the list, press ACTIVATE and enter your license key.', + 'bit-integrations' + )}
      • +
      • ${__( + "Go to your Perfex's CRM backend as an admin, go to API → API Management, and create a new token.", + 'bit-integrations' + )}
      • +
      ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Access API URL:', 'bit-integrations')} -
      - -
      {error.domain}
      - -
      - {__('API Token:', 'bit-integrations')} -
      - -
      {error.api_token}
      - - - {__('To Get API Token, Please Visit', 'bit-integrations')} -   - - {__('PerfexCRM API Token', 'bit-integrations')} - - -
      -
      - - {!isInfo && ( -
      - -
      - -
      - )} - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/PerfexCRM/PerfexCRMCommonFunc.js b/frontend/src/components/AllIntegrations/PerfexCRM/PerfexCRMCommonFunc.js index c509ed6a3..53bfc261d 100644 --- a/frontend/src/components/AllIntegrations/PerfexCRM/PerfexCRMCommonFunc.js +++ b/frontend/src/components/AllIntegrations/PerfexCRM/PerfexCRMCommonFunc.js @@ -16,12 +16,19 @@ export const handleInput = (e, perfexCRMConf, setPerfexCRMConf) => { setPerfexCRMConf({ ...newConf }) } +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + api_token: confTmp.api_token, + domain: confTmp.domain + } + // refreshMappedFields export const refreshCustomFields = (perfexCRMConf, setPerfexCRMConf, setIsLoading, setSnackbar) => { setIsLoading(true) const requestParams = { - api_token: perfexCRMConf.api_token, - domain: perfexCRMConf.domain, + ...buildAuthRequestParams(perfexCRMConf), action_name: perfexCRMConf.actionName } @@ -97,53 +104,10 @@ export const checkMappedFields = perfexCRMConf => { return true } -export const perfexCRMAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_token || !confTmp.domain) { - setError({ - api_token: !confTmp.api_token ? __("API Token can't be empty", 'bit-integrations') : '', - domain: !confTmp.domain ? __("API URL can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - api_token: confTmp.api_token, - domain: confTmp.domain - } - - bitsFetch(requestParams, 'perfexcrm_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error( - String(result?.data) - ? result?.data - : __('Authorized failed, Please enter valid API Token or Access API URL', 'bit-integrations') - ) - }) -} - export const getAllCustomer = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, customers: true }) - const requestParams = { - api_token: confTmp.api_token, - domain: confTmp.domain - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'perfexcrm_fetch_all_customers').then(result => { if (result && result.success) { @@ -166,10 +130,7 @@ export const getAllCustomer = (confTmp, setConf, loading, setLoading) => { export const getAllLeads = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, leads: true }) - const requestParams = { - api_token: confTmp.api_token, - domain: confTmp.domain - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'perfexcrm_fetch_all_leads').then(result => { if (result && result.success) { @@ -192,10 +153,7 @@ export const getAllLeads = (confTmp, setConf, loading, setLoading) => { export const getAllStaffs = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, staffs: true }) - const requestParams = { - api_token: confTmp.api_token, - domain: confTmp.domain - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'perfexcrm_fetch_all_staffs').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/PipeDrive/PipeDriveAuthorization.jsx b/frontend/src/components/AllIntegrations/PipeDrive/PipeDriveAuthorization.jsx index ba3a15416..40a4b20ef 100644 --- a/frontend/src/components/AllIntegrations/PipeDrive/PipeDriveAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/PipeDrive/PipeDriveAuthorization.jsx @@ -1,37 +1,15 @@ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { handleAuthorize } from './PipeDriveCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import Authorization from '../../Connections/Authorization' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' export default function PipeDriveAuthorization({ pipeDriveConf, setPipeDriveConf, step, setstep, - isLoading, - setIsLoading, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - } - - const handleInput = e => { - const newConf = { ...pipeDriveConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setPipeDriveConf(newConf) - } const note = `

      ${__('Step of generate API token:', 'bit-integrations')}

        @@ -48,77 +26,22 @@ export default function PipeDriveAuthorization({ ` return ( -
        - - -
        - {__('Integration Name:', 'bit-integrations')} -
        - - -
        - {__('API Token:', 'bit-integrations')} -
        - -
        {error.api_key}
        - - - {__('To Get API Token, Please Visit', 'bit-integrations')} -   - - {__('PipeDrive API Token', 'bit-integrations')} - - -
        -
        - - {!isInfo && ( -
        - -
        - -
        - )} - -
        + ) } diff --git a/frontend/src/components/AllIntegrations/PipeDrive/PipeDriveCommonFunc.js b/frontend/src/components/AllIntegrations/PipeDrive/PipeDriveCommonFunc.js index f695fab25..09bab7780 100644 --- a/frontend/src/components/AllIntegrations/PipeDrive/PipeDriveCommonFunc.js +++ b/frontend/src/components/AllIntegrations/PipeDrive/PipeDriveCommonFunc.js @@ -4,6 +4,9 @@ import toast from 'react-hot-toast' import { __, sprintf } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' +const buildAuthRequestParams = confTmp => + confTmp?.connection_id ? { connection_id: confTmp.connection_id } : { api_key: confTmp?.api_key } + export const handleInput = ( e, recordTab, @@ -105,7 +108,7 @@ const moduleChange = (recordTab, formID, pipeDriveConf, setPipeDriveConf, setIsL } const refreshFields = (module, pipeDriveConf, setPipeDriveConf, recordTab) => { - const requestParams = { api_key: pipeDriveConf.api_key, module } + const requestParams = { ...buildAuthRequestParams(pipeDriveConf), module } bitsFetch(requestParams, 'PipeDrive_refresh_fields').then(result => { if (result && result.success) { @@ -132,7 +135,7 @@ const refreshFields = (module, pipeDriveConf, setPipeDriveConf, recordTab) => { export const refreshOrganizations = (pipeDriveConf, setPipeDriveConf, setIsLoading, setSnackbar) => { setIsLoading(true) const requestParams = { - api_key: pipeDriveConf.api_key, + ...buildAuthRequestParams(pipeDriveConf), type: 'organizations' } @@ -173,7 +176,7 @@ export const refreshOrganizations = (pipeDriveConf, setPipeDriveConf, setIsLoadi export const refreshPersons = (pipeDriveConf, setPipeDriveConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { api_key: pipeDriveConf.api_key, type: 'persons' } + const requestParams = { ...buildAuthRequestParams(pipeDriveConf), type: 'persons' } bitsFetch(requestParams, 'PipeDrive_fetch_meta_data') .then(result => { @@ -212,7 +215,7 @@ export const refreshPersons = (pipeDriveConf, setPipeDriveConf, setIsLoading, se export const getAllOwners = (pipeDriveConf, setPipeDriveConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { api_key: pipeDriveConf.api_key, type: 'users' } + const requestParams = { ...buildAuthRequestParams(pipeDriveConf), type: 'users' } bitsFetch(requestParams, 'PipeDrive_fetch_meta_data') .then(result => { @@ -251,7 +254,7 @@ export const getAllOwners = (pipeDriveConf, setPipeDriveConf, setIsLoading, setS export const getAllLeadLabels = (pipeDriveConf, setPipeDriveConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { api_key: pipeDriveConf.api_key, type: 'leadLabels' } + const requestParams = { ...buildAuthRequestParams(pipeDriveConf), type: 'leadLabels' } bitsFetch(requestParams, 'PipeDrive_fetch_meta_data') .then(result => { @@ -289,7 +292,7 @@ export const getAllLeadLabels = (pipeDriveConf, setPipeDriveConf, setIsLoading, } export const getAllCurrencies = (pipeDriveConf, setPipeDriveConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { api_key: pipeDriveConf.api_key, type: 'currencies' } + const requestParams = { ...buildAuthRequestParams(pipeDriveConf), type: 'currencies' } bitsFetch(requestParams, 'PipeDrive_fetch_meta_data') .then(result => { @@ -328,7 +331,7 @@ export const getAllCurrencies = (pipeDriveConf, setPipeDriveConf, setIsLoading, export const getDealStages = (pipeDriveConf, setPipeDriveConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { api_key: pipeDriveConf.api_key, type: 'stages' } + const requestParams = { ...buildAuthRequestParams(pipeDriveConf), type: 'stages' } bitsFetch(requestParams, 'PipeDrive_fetch_meta_data') .then(result => { @@ -422,7 +425,7 @@ export const checkRequired = pipeDriveConf => { } export const handleAuthorize = (confTmp, setError, setisAuthorized, setIsLoading) => { - if (!confTmp.api_key) { + if (!confTmp.connection_id && !confTmp.api_key) { setError({ api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' }) @@ -430,7 +433,7 @@ export const handleAuthorize = (confTmp, setError, setisAuthorized, setIsLoading } setError({}) setIsLoading(true) - const requestParams = { api_key: confTmp.api_key, type: 'persons' } + const requestParams = { ...buildAuthRequestParams(confTmp), type: 'persons' } bitsFetch(requestParams, 'PipeDrive_fetch_meta_data').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/PropovoiceCRM/PropovoiceCrmAuthorization.jsx b/frontend/src/components/AllIntegrations/PropovoiceCRM/PropovoiceCrmAuthorization.jsx index d1c35d729..255e4778c 100644 --- a/frontend/src/components/AllIntegrations/PropovoiceCRM/PropovoiceCrmAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/PropovoiceCRM/PropovoiceCrmAuthorization.jsx @@ -1,103 +1,35 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function PropovoiceCrmAuthorization({ - formID, propovoiceCrmConf, setPropovoiceCrmConf, step, setStep, - isLoading, - setIsLoading, - setSnackbar + isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'propovoice_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Propovoice CRM Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(propovoiceCrmConf) - newConf[e.target.name] = e.target.value - setPropovoiceCrmConf(newConf) - } - return ( -
        - - -
        - {__('Integration Name:', 'bit-integrations')} -
        - - - {isLoading === 'auth' && ( -
        - - {__('Checking if Propovoice CRM is active!!!', 'bit-integrations')} -
        - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
        - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'Propovoice CRM' - )} -
        - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
        + ) } diff --git a/frontend/src/components/AllIntegrations/Rapidmail/Rapidmail.jsx b/frontend/src/components/AllIntegrations/Rapidmail/Rapidmail.jsx index 5a050708d..8867986f9 100644 --- a/frontend/src/components/AllIntegrations/Rapidmail/Rapidmail.jsx +++ b/frontend/src/components/AllIntegrations/Rapidmail/Rapidmail.jsx @@ -100,7 +100,6 @@ function Rapidmail({ formFields, setFlow, flow, allIntegURL }) { setRapidmailConf={setRapidmailConf} step={step} setstep={setstep} - isLoading={isLoading} setIsLoading={setIsLoading} setSnackbar={setSnackbar} /> diff --git a/frontend/src/components/AllIntegrations/Rapidmail/RapidmailAuthorization.jsx b/frontend/src/components/AllIntegrations/Rapidmail/RapidmailAuthorization.jsx index 32eb0fe0b..eb07a2bed 100644 --- a/frontend/src/components/AllIntegrations/Rapidmail/RapidmailAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Rapidmail/RapidmailAuthorization.jsx @@ -1,40 +1,30 @@ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +/* eslint-disable jsx-a11y/anchor-is-valid */ +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { getAllRecipient, handleAuthorize } from './RapidmailCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllRecipient } from './RapidmailCommonFunc' export default function RapidmailAuthorization({ rapidmailConf, setRapidmailConf, step, setstep, - isLoading, setIsLoading, setSnackbar, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ username: '', password: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const handleSetStep = useCallback( + value => { + if (value === 2 && !rapidmailConf?.default) { + getAllRecipient(rapidmailConf, setRapidmailConf, setIsLoading, setSnackbar) + } + setstep(value) + }, + [rapidmailConf, setRapidmailConf, setIsLoading, setSnackbar, setstep] + ) - !rapidmailConf?.default && - getAllRecipient(rapidmailConf, setRapidmailConf, setIsLoading, setSnackbar) - setstep(2) - } - const handleInput = e => { - const newConf = { ...rapidmailConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setRapidmailConf(newConf) - } const note = `

        ${__('Step of creating username and password:', 'bit-integrations')}

          @@ -55,83 +45,21 @@ export default function RapidmailAuthorization({ ` return ( -
          - - -
          - {__('Integration Name:', 'bit-integrations')} -
          - - -
          - {__('User Name:', 'bit-integrations')} -
          - -
          {error.username}
          - -
          - {__('Password:', 'bit-integrations')} -
          - -
          {error.password}
          - - {!isInfo && ( -
          - -
          - -
          - )} - -
          + ) } diff --git a/frontend/src/components/AllIntegrations/Rapidmail/RapidmailCommonFunc.js b/frontend/src/components/AllIntegrations/Rapidmail/RapidmailCommonFunc.js index ef4c66014..84cda171d 100644 --- a/frontend/src/components/AllIntegrations/Rapidmail/RapidmailCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Rapidmail/RapidmailCommonFunc.js @@ -25,10 +25,12 @@ export const handleInput = ( export const getAllRecipient = (rapidmailConf, setRapidmailConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const queryParams = { - username: rapidmailConf.username, - password: rapidmailConf.password - } + const queryParams = rapidmailConf?.connection_id + ? { connection_id: rapidmailConf.connection_id } + : { + username: rapidmailConf.username, + password: rapidmailConf.password + } const loadPostTypes = bitsFetch(null, 'rapidmail_get_all_recipients', queryParams, 'GET').then( result => { if (result && result.success) { @@ -75,55 +77,3 @@ export const checkMappedFields = rapidmailConf => { } return true } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.username || !confTmp.password) { - setError({ - username: !confTmp.username ? __("Username can't be empty", 'bit-integrations') : '', - password: !confTmp.password ? __("Password can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setIsLoading(true) - - const tokenRequestParams = { - username: confTmp.username, - password: confTmp.password - } - - bitsFetch(tokenRequestParams, 'rapidmail_authorization') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/RestrictContent/RestrictContentAuthorization.jsx b/frontend/src/components/AllIntegrations/RestrictContent/RestrictContentAuthorization.jsx index 77e62c95d..188fc0d2f 100644 --- a/frontend/src/components/AllIntegrations/RestrictContent/RestrictContentAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/RestrictContent/RestrictContentAuthorization.jsx @@ -1,110 +1,59 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' import { getAllLevels } from './RestrictContentCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' + export default function RestrictContentAuthorization({ - formID, restrictConf, setRestrictConf, step, setStep, - setSnackbar + isInfo, + setIsLoading }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'restrict_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Restrict Content Successfully', 'bit-integrations') - }) + const handleSetStep = useCallback( + value => { + if (value === 2) { + getAllLevels(restrictConf, setRestrictConf, setIsLoading) } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(restrictConf) - newConf[e.target.name] = e.target.value - setRestrictConf(newConf) - } - - const nextPage = () => { - // setTimeout(() => { - // document.getElementById('btcd-settings-wrp').scrollTop = 0 - // }, 300) - getAllLevels(restrictConf, setRestrictConf, setIsLoading) - setStep(2) - } + setStep(value) + }, + [setStep, restrictConf, setRestrictConf, setIsLoading] + ) return ( -
          - - -
          - {__('Integration Name:', 'bit-integrations')} -
          - - - {isLoading === 'auth' && ( -
          - - Checking if restrict content is active!!! -
          - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
          - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'RestrictContent' - )} -
          - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
          + ) } diff --git a/frontend/src/components/AllIntegrations/Salesflare/SalesflareAuthorization.jsx b/frontend/src/components/AllIntegrations/Salesflare/SalesflareAuthorization.jsx index 3711e557e..836380a40 100644 --- a/frontend/src/components/AllIntegrations/Salesflare/SalesflareAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Salesflare/SalesflareAuthorization.jsx @@ -1,116 +1,44 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { salesflareAuthentication } from './SalesflareCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function SalesflareAuthorization({ salesflareConf, setSalesflareConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_token: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !salesflareConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...salesflareConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSalesflareConf(newConf) - } - - const ActiveInstructions = ` -

          ${__('Get API Key', 'bit-integrations')}

          -
            -
          • ${__('Go to your Salesflare user dashboard', 'bit-integrations')}
          • -
          • ${__('Then click "Settings"', 'bit-integrations')}
          • -
          • ${__('Then click "API Keys → Generates Keys"', 'bit-integrations')}
          • -
          ` + const note = ` +

          ${__('Get API Key', 'bit-integrations')}

          +
            +
          • ${__('Go to your Salesflare user dashboard.', 'bit-integrations')}
          • +
          • ${__('Open Settings.', 'bit-integrations')}
          • +
          • ${__('Open API Keys, then generate/copy your key.', 'bit-integrations')}
          • +
          + + ${__('To get API key, please visit', 'bit-integrations')} + + ${__('Salesflare API Key', 'bit-integrations')} + + ` return ( -
          - - -
          - {__('Integration Name:', 'bit-integrations')} -
          - - -
          - {__('API Key:', 'bit-integrations')} -
          - -
          {error.api_key}
          - - - {__('To get API key, please visit', 'bit-integrations')} -   - - {__('Salesflare API Key', 'bit-integrations')} - - -
          -
          - - {!isInfo && ( -
          - -
          - -
          - )} - -
          + ) } diff --git a/frontend/src/components/AllIntegrations/Salesflare/SalesflareCommonFunc.js b/frontend/src/components/AllIntegrations/Salesflare/SalesflareCommonFunc.js index 6b5ec127a..63b6e2307 100644 --- a/frontend/src/components/AllIntegrations/Salesflare/SalesflareCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Salesflare/SalesflareCommonFunc.js @@ -44,37 +44,17 @@ export const checkMappedFields = salesflareConf => { return true } -export const salesflareAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - api_key: confTmp.api_key - } - - bitsFetch(requestParams, 'salesflare_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid API Key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + api_key: confTmp.api_key + } export const salesflareFields = (salesflareConf, setSalesflareConf, setIsLoading, setSnackbar) => { setIsLoading(true) const requestParams = { - api_key: salesflareConf.api_key, + ...buildAuthRequestParams(salesflareConf), action_name: salesflareConf.actionName } @@ -108,9 +88,7 @@ export const salesflareFields = (salesflareConf, setSalesflareConf, setIsLoading export const getAllTags = (salesflareConf, setSalesflareConf, setLoading) => { setLoading({ ...setLoading, tags: true }) - const requestParams = { - api_key: salesflareConf.api_key - } + const requestParams = buildAuthRequestParams(salesflareConf) bitsFetch(requestParams, 'Salesflare_fetch_all_tags').then(result => { if (result && result.success) { @@ -131,9 +109,7 @@ export const getAllTags = (salesflareConf, setSalesflareConf, setLoading) => { export const getallAccounts = (salesflareConf, setSalesflareConf, loading, setLoading) => { setLoading({ ...loading, account: true }) - const requestParams = { - api_key: salesflareConf.api_key - } + const requestParams = buildAuthRequestParams(salesflareConf) bitsFetch(requestParams, 'Salesflare_fetch_all_account').then(result => { if (result && result.success) { @@ -153,9 +129,7 @@ export const getallAccounts = (salesflareConf, setSalesflareConf, loading, setLo } export const getallPipelines = (salesflareConf, setSalesflareConf, loading, setLoading) => { setLoading({ ...loading, pipeline: true }) - const requestParams = { - api_key: salesflareConf.api_key - } + const requestParams = buildAuthRequestParams(salesflareConf) bitsFetch(requestParams, 'Salesflare_fetch_all_pipelines').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/Salesforce/Salesforce.jsx b/frontend/src/components/AllIntegrations/Salesforce/Salesforce.jsx index b6ed2c74d..a2681fd8f 100644 --- a/frontend/src/components/AllIntegrations/Salesforce/Salesforce.jsx +++ b/frontend/src/components/AllIntegrations/Salesforce/Salesforce.jsx @@ -1,5 +1,5 @@ /* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import toast from 'react-hot-toast' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' @@ -7,7 +7,7 @@ import BackIcn from '../../../Icons/BackIcn' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { saveActionConf, setGrantTokenResponse } from '../IntegrationHelpers/IntegrationHelpers' +import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import { checkMappedFields, handleInput } from './SalesforceCommonFunc' import SelesforceIntegLayout from './SalesforceIntegLayout' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' @@ -42,9 +42,6 @@ function Salesforce({ formFields, setFlow, flow, allIntegURL }) { action_modules, actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('salesforce') - }, []) const checkedActionFieldsMapType = [ 'contact-create', diff --git a/frontend/src/components/AllIntegrations/Salesforce/SalesforceAuthorization.jsx b/frontend/src/components/AllIntegrations/Salesforce/SalesforceAuthorization.jsx index c9419be88..aee8c65dc 100644 --- a/frontend/src/components/AllIntegrations/Salesforce/SalesforceAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Salesforce/SalesforceAuthorization.jsx @@ -1,150 +1,49 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { handleAuthorize } from './SalesforceCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' -import { $appConfigState } from '../../../GlobalStates' -import { useRecoilValue } from 'recoil' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function SalesforceAuthorization({ - formID, salesforceConf, setSalesforceConf, step, setStep, - isLoading, - setIsLoading, - setSnackbar, - redirectLocation, isInfo }) { - const btcbi = useRecoilValue($appConfigState) - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ - dataCenter: '', - clientId: '', - clientSecret: '' - }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setStep(2) - } - - const handleInput = e => { - const newConf = { ...salesforceConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSalesforceConf(newConf) - } + const note = `

          ${__('Salesforce OAuth2 Setup', 'bit-integrations')}

          +
            +
          1. ${__('Create a Connected App in Salesforce.', 'bit-integrations')}
          2. +
          3. ${__('Set the callback URL exactly as shown below.', 'bit-integrations')}
          4. +
          5. ${__('Use Consumer Key as Client ID and Consumer Secret as Client Secret.', 'bit-integrations')}
          6. +
          ` return ( -
          - - -
          - {__('Integration Name:', 'bit-integrations')} -
          -
          - - -
          - {__('Homepage URL:', 'bit-integrations')} -
          - - -
          - {__('Authorized Redirect URIs:', 'bit-integrations')} -
          - - -
          - {__('Client id:', 'bit-integrations')} -
          - -
          {error.clientId}
          - -
          - {__('Client secret:', 'bit-integrations')} -
          - -
          {error.clientSecret}
          - - {!isInfo && ( - <> - -
          - - - )} -
          + ) } diff --git a/frontend/src/components/AllIntegrations/Salesforce/SalesforceCommonFunc.js b/frontend/src/components/AllIntegrations/Salesforce/SalesforceCommonFunc.js index 423e9bd72..64f3b4d21 100644 --- a/frontend/src/components/AllIntegrations/Salesforce/SalesforceCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Salesforce/SalesforceCommonFunc.js @@ -1,8 +1,17 @@ import toast from 'react-hot-toast' -import { __, sprintf } from '../../../Utils/i18nwrap' +import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' import { create } from 'mutative' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const handleInput = ( e, salesforceConf, @@ -68,12 +77,7 @@ export const getAllCampaignList = ( setSnackbar ) => { setIsLoading(true) - const campaignRequestParams = { - formID, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails - } + const campaignRequestParams = { formID, ...buildAuthRequestParams(salesforceConf) } const loadPostTypes = bitsFetch(campaignRequestParams, 'selesforce_campaign_list').then(result => { if (result && result.success) { setSalesforceConf(oldConf => { @@ -105,12 +109,7 @@ export const getAllCampaignList = ( export const getAllOrigin = (formID, salesforceConf, setSalesforceConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const campaignRequestParams = { - formID, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails - } + const campaignRequestParams = { formID, ...buildAuthRequestParams(salesforceConf) } const loadPostTypes = bitsFetch(campaignRequestParams, 'selesforce_case_origin').then(result => { if (result && result.success) { setSalesforceConf(prevConf => @@ -133,12 +132,7 @@ export const getAllOrigin = (formID, salesforceConf, setSalesforceConf, setIsLoa export const getAllType = (formID, salesforceConf, setSalesforceConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const campaignRequestParams = { - formID, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails - } + const campaignRequestParams = { formID, ...buildAuthRequestParams(salesforceConf) } const loadPostTypes = bitsFetch(campaignRequestParams, 'selesforce_case_type').then(result => { if (result && result.success) { setSalesforceConf(prevConf => @@ -161,12 +155,7 @@ export const getAllType = (formID, salesforceConf, setSalesforceConf, setIsLoadi export const getAllReason = (formID, salesforceConf, setSalesforceConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const campaignRequestParams = { - formID, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails - } + const campaignRequestParams = { formID, ...buildAuthRequestParams(salesforceConf) } const loadPostReasons = bitsFetch(campaignRequestParams, 'selesforce_case_reason').then(result => { if (result && result.success) { setSalesforceConf(prevConf => @@ -189,12 +178,7 @@ export const getAllReason = (formID, salesforceConf, setSalesforceConf, setIsLoa export const getAllStatus = (formID, salesforceConf, setSalesforceConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const campaignRequestParams = { - formID, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails - } + const campaignRequestParams = { formID, ...buildAuthRequestParams(salesforceConf) } const loadPostReasons = bitsFetch(campaignRequestParams, 'selesforce_case_status').then(result => { if (result && result.success) { setSalesforceConf(prevConf => @@ -217,12 +201,7 @@ export const getAllStatus = (formID, salesforceConf, setSalesforceConf, setIsLoa export const getAllPriority = (formID, salesforceConf, setSalesforceConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const campaignRequestParams = { - formID, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails - } + const campaignRequestParams = { formID, ...buildAuthRequestParams(salesforceConf) } const loadPostReasons = bitsFetch(campaignRequestParams, 'selesforce_case_priority').then(result => { if (result && result.success) { setSalesforceConf(prevConf => @@ -251,12 +230,7 @@ export const getAllPotentialLiability = ( setSnackbar ) => { setIsLoading(true) - const campaignRequestParams = { - formID, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails - } + const campaignRequestParams = { formID, ...buildAuthRequestParams(salesforceConf) } const loadPostReasons = bitsFetch(campaignRequestParams, 'selesforce_case_potential_liability').then( result => { if (result && result.success) { @@ -287,12 +261,7 @@ export const getAllSLAViolation = ( setSnackbar ) => { setIsLoading(true) - const campaignRequestParams = { - formID, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails - } + const campaignRequestParams = { formID, ...buildAuthRequestParams(salesforceConf) } const loadPostReasons = bitsFetch(campaignRequestParams, 'selesforce_case_sla_violation').then( result => { if (result && result.success) { @@ -326,9 +295,7 @@ export const getAllLeadSource = ( const campaignRequestParams = { formID, actionName: salesforceConf.actionName, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails + ...buildAuthRequestParams(salesforceConf) } const loadPostReasons = bitsFetch(campaignRequestParams, 'selesforce_get_lead_sources').then( result => { @@ -365,9 +332,7 @@ export const getAllLeadStatus = ( const campaignRequestParams = { formID, actionName: salesforceConf.actionName, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails + ...buildAuthRequestParams(salesforceConf) } const loadPostReasons = bitsFetch(campaignRequestParams, 'selesforce_get_lead_status').then(result => { if (result && result.success) { @@ -402,9 +367,7 @@ export const getAllLeadRatings = ( const campaignRequestParams = { formID, actionName: salesforceConf.actionName, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails + ...buildAuthRequestParams(salesforceConf) } const loadPostReasons = bitsFetch(campaignRequestParams, 'selesforce_get_lead_ratings').then( result => { @@ -471,12 +434,7 @@ export const getAllLeadIndustries = ( export const getAllLeadList = (formID, salesforceConf, setSalesforceConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const campaignRequestParams = { - formID, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails - } + const campaignRequestParams = { formID, ...buildAuthRequestParams(salesforceConf) } bitsFetch(campaignRequestParams, 'selesforce_lead_list') .then(result => { if (result && result.success) { @@ -511,12 +469,7 @@ export const getAllContactList = ( setSnackbar ) => { setIsLoading(true) - const campaignRequestParams = { - formID, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails - } + const campaignRequestParams = { formID, ...buildAuthRequestParams(salesforceConf) } const loadPostTypes = bitsFetch(campaignRequestParams, 'selesforce_contact_list').then(result => { if (result && result.success) { setSalesforceConf(oldConf => { @@ -558,9 +511,7 @@ export const getAllCustomFields = ( const customFieldRequestParams = { formID, actionName, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails + ...buildAuthRequestParams(salesforceConf) } const loadPostTypes = bitsFetch(customFieldRequestParams, 'selesforce_custom_field').then(result => { @@ -597,9 +548,7 @@ export const getAllCustomActionModules = ( setSnackbar ) => { const customFieldRequestParams = { - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails + ...buildAuthRequestParams(salesforceConf) } setIsLoading(true) bitsFetch(customFieldRequestParams, 'selesforce_custom_action').then(result => { @@ -666,12 +615,7 @@ export const getAllAccountList = ( setSnackbar ) => { setIsLoading(true) - const campaignRequestParams = { - formID, - clientId: salesforceConf.clientId, - clientSecret: salesforceConf.clientSecret, - tokenDetails: salesforceConf.tokenDetails - } + const campaignRequestParams = { formID, ...buildAuthRequestParams(salesforceConf) } const loadPostTypes = bitsFetch(campaignRequestParams, 'selesforce_account_list').then(result => { if (result && result.success) { setSalesforceConf(oldConf => { @@ -742,116 +686,3 @@ export const generateMappedField = (salesforceConf, actionName) => { return fieldMap } - -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty", 'bit-integrations') : '' - }) - return - } - - setIsLoading(true) - const apiEndpoint = `https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id=${ - confTmp.clientId - }&prompt=login%20consent&state=${encodeURIComponent( - window.location.href - )}/redirect&redirect_uri=${encodeURIComponent(btcbi.api)}/redirect` - - const authWindow = window.open(apiEndpoint, 'salesforce', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitformsZoho = localStorage.getItem('__salesforce') - if (bitformsZoho) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitformsZoho) - localStorage.removeItem('__salesforce') - } - - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper( - grantTokenResponse, - newConf, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi - ) - } - } - }, 500) -} - -const tokenHelper = ( - grantToken, - confTmp, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - - bitsFetch(tokenRequestParams, 'selesforce_generate_token').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Authorized Successfully', 'bit-integrations') - }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/Salesmate/Salesmate.jsx b/frontend/src/components/AllIntegrations/Salesmate/Salesmate.jsx index b3444a4b8..b0f4639de 100644 --- a/frontend/src/components/AllIntegrations/Salesmate/Salesmate.jsx +++ b/frontend/src/components/AllIntegrations/Salesmate/Salesmate.jsx @@ -101,9 +101,6 @@ function Salesmate({ formFields, setFlow, flow, allIntegURL }) { setSalesmateConf={setSalesmateConf} step={step} setStep={setStep} - loading={loading} - setLoading={setLoading} - setSnackbar={setSnackbar} /> {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/Salesmate/SalesmateAuthorization.jsx b/frontend/src/components/AllIntegrations/Salesmate/SalesmateAuthorization.jsx index a7a2e6673..ef74c99bb 100644 --- a/frontend/src/components/AllIntegrations/Salesmate/SalesmateAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Salesmate/SalesmateAuthorization.jsx @@ -1,54 +1,17 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { salesmateAuthentication } from './SalesmateCommonFunc' -import Note from '../../Utilities/Note' -import { toast } from 'react-hot-toast' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function SalesmateAuthorization({ salesmateConf, setSalesmateConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ session_token: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !salesmateConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...salesmateConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSalesmateConf(newConf) - } - - const handleSessionTokenLink = () => { - salesmateConf.link_name - ? window.open( - `https://${salesmateConf.link_name}.salesmate.io/#/app/user/accesskey`, - '_blank', - 'noreferrer' - ) - : toast.error(__('Link Name is required!', 'bit-integrations')) - } - - const ActiveInstructions = ` -

          ${__('Get Session Token', 'bit-integrations')}

          + const ActiveInstructions = `

          ${__('Get Session Token', 'bit-integrations')}

          • ${__('First go to your Salesmate dashboard.', 'bit-integrations')}
          • ${__('Click go to your "Profile" from Right top corner', 'bit-integrations')}
          • @@ -60,100 +23,34 @@ export default function SalesmateAuthorization({
          ` return ( -
          - - -
          - {__('Integration Name:', 'bit-integrations')} -
          - - -
          - {__('Session Token:', 'bit-integrations')} -
          - -
          {error.session_token}
          - -
          - {__('Link Name:', 'bit-integrations')} -
          - -
          -
          https://
          -
          - -
          -
          .salesmate.io
          -
          -
          {error.link_name}
          - - - {__('To Get Session Token, Please Visit', 'bit-integrations')} -   - - {__('Salesmate Session Token', 'bit-integrations')} - - -
          -
          - - {!isInfo && ( -
          - -
          - -
          - )} - -
          + ) } diff --git a/frontend/src/components/AllIntegrations/Salesmate/SalesmateCommonFunc.js b/frontend/src/components/AllIntegrations/Salesmate/SalesmateCommonFunc.js index 160ba7c5c..ba5e43268 100644 --- a/frontend/src/components/AllIntegrations/Salesmate/SalesmateCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Salesmate/SalesmateCommonFunc.js @@ -15,13 +15,14 @@ export const handleInput = (e, salesmateConf, setSalesmateConf) => { setSalesmateConf({ ...newConf }) } +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { session_token: confTmp.session_token, link_name: confTmp.link_name } + // refreshMappedFields export const refreshSalesmateFields = (salesmateConf, setSalesmateConf, setIsLoading, setSnackbar) => { - const requestParams = { - session_token: salesmateConf.session_token, - link_name: salesmateConf.link_name, - action_id: salesmateConf.actionId - } + const requestParams = { ...buildAuthRequestParams(salesmateConf), action_id: salesmateConf.actionId } bitsFetch(requestParams, 'Salesmate_fields') .then(result => { @@ -81,55 +82,10 @@ export const checkMappedFields = salesmateConf => { return true } -export const salesmateAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.session_token || !confTmp.link_name) { - setError({ - session_token: !confTmp.session_token - ? __("Session Token can't be empty", 'bit-integrations') - : '', - link_name: !confTmp.link_name ? __("Link Name can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - session_token: confTmp.session_token, - link_name: confTmp.link_name - } - - bitsFetch(requestParams, 'salesmate_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error( - __('Authorized failed, Please enter valid Session Token or Link Name', 'bit-integrations') - ) - }) -} - export const getAllTags = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, tags: true }) - const requestParams = { - session_token: confTmp.session_token, - link_name: confTmp.link_name - } - - bitsFetch(requestParams, 'salesmate_fetch_all_tags').then(result => { + bitsFetch(buildAuthRequestParams(confTmp), 'salesmate_fetch_all_tags').then(result => { if (result && result.success) { const newConf = { ...confTmp } if (result.data) { @@ -217,12 +173,8 @@ export const getAllCRMPriority = setConf => { export const getAllCRMCurrency = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMCurrency: true }) - const requestParams = { - session_token: confTmp.session_token, - link_name: confTmp.link_name - } - bitsFetch(requestParams, 'salesmate_fetch_all_currencies').then(result => { + bitsFetch(buildAuthRequestParams(confTmp), 'salesmate_fetch_all_currencies').then(result => { if (result && result.success) { const newConf = { ...confTmp } if (result.data) { @@ -241,12 +193,8 @@ export const getAllCRMCurrency = (confTmp, setConf, setLoading) => { export const getAllCRMCompany = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMCompany: true }) - const requestParams = { - session_token: confTmp.session_token, - link_name: confTmp.link_name - } - bitsFetch(requestParams, 'salesmate_fetch_all_CRMCompanies').then(result => { + bitsFetch(buildAuthRequestParams(confTmp), 'salesmate_fetch_all_CRMCompanies').then(result => { if (result && result.success) { if (!result.data) { setLoading({ ...setLoading, CRMCompany: false }) @@ -273,12 +221,8 @@ export const getAllCRMCompany = (confTmp, setConf, setLoading) => { export const getAllCRMPipelines = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, CRMPipelines: true }) - const requestParams = { - session_token: confTmp.session_token, - link_name: confTmp.link_name - } - bitsFetch(requestParams, 'salesmate_fetch_all_CRMPipelines').then(result => { + bitsFetch(buildAuthRequestParams(confTmp), 'salesmate_fetch_all_CRMPipelines').then(result => { setLoading({ ...loading, CRMPipelines: false }) if (result && result.success) { setConf(prevConf => { @@ -297,12 +241,8 @@ export const getAllCRMPipelines = (confTmp, setConf, loading, setLoading) => { } export const getAllCRMPrimaryContact = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, CRMContacts: true }) - const requestParams = { - session_token: confTmp.session_token, - link_name: confTmp.link_name - } - bitsFetch(requestParams, 'salesmate_fetch_all_CRMContacts').then(result => { + bitsFetch(buildAuthRequestParams(confTmp), 'salesmate_fetch_all_CRMContacts').then(result => { setLoading({ ...loading, CRMContacts: false }) if (result && result.success) { if (!result.data) { @@ -327,12 +267,8 @@ export const getAllCRMPrimaryContact = (confTmp, setConf, loading, setLoading) = export const getAllCRMOwner = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, CRMOwners: true }) - const requestParams = { - session_token: confTmp.session_token, - link_name: confTmp.link_name - } - bitsFetch(requestParams, 'salesmate_fetch_all_CRMOwners').then(result => { + bitsFetch(buildAuthRequestParams(confTmp), 'salesmate_fetch_all_CRMOwners').then(result => { setLoading({ ...loading, CRMOwners: false }) if (result && result.success) { setConf(prevConf => { diff --git a/frontend/src/components/AllIntegrations/Selzy/Selzy.jsx b/frontend/src/components/AllIntegrations/Selzy/Selzy.jsx index e62571bf3..092848033 100644 --- a/frontend/src/components/AllIntegrations/Selzy/Selzy.jsx +++ b/frontend/src/components/AllIntegrations/Selzy/Selzy.jsx @@ -21,7 +21,7 @@ function Selzy({ formFields, setFlow, flow, allIntegURL }) { const [selzyConf, setSelzyConf] = useState({ name: 'Selzy', type: 'Selzy', - authKey: '', + api_key: '', field_map: [{ formFields: '', selzyFormField: '' }], listIds: '', tags: '', diff --git a/frontend/src/components/AllIntegrations/Selzy/SelzyAuthorization.jsx b/frontend/src/components/AllIntegrations/Selzy/SelzyAuthorization.jsx index 7d4e6ad2a..09bf866c2 100644 --- a/frontend/src/components/AllIntegrations/Selzy/SelzyAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Selzy/SelzyAuthorization.jsx @@ -1,28 +1,32 @@ -import { useState } from 'react' -import AuthorizeButton from '../../Utilities/AuthorizeButton' -import ErrorField from '../../Utilities/ErrorField' -import GetInfo from '../../Utilities/GetInfo' -import Input from '../../Utilities/Input' -import Note from '../../Utilities/Note' -import StepPage from '../../Utilities/StepPage' -import { getAllTags, handleAuthorize, handleInput } from './SelzyCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllCustomFields, getAllLists, getAllTags } from './SelzyCommonFunc' function SelzyAuthorization({ selzyConf, setSelzyConf, step, setStep, loading, setLoading, isInfo }) { - const [authorized, setAuthorized] = useState(false) - const [error, setError] = useState({ name: '', authKey: '' }) - const nextPage = async () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setStep(2) - setLoading({ ...loading, page: true }) - const data = await getAllTags(selzyConf, setSelzyConf) - if (data) { - setLoading({ ...loading, page: false }) - } - } + const loadLists = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...selzyConf, connection_id: connectionId } : selzyConf + await getAllLists(nextConf, setSelzyConf, loading, setLoading) + }, + [loading, selzyConf, setLoading, setSelzyConf] + ) + + const handleSetStep = useCallback( + async value => { + if (value === 2) { + setLoading({ ...loading, page: true }) + const nextConf = selzyConf?.connection_id ? selzyConf : { ...selzyConf } + await getAllTags(nextConf, setSelzyConf) + await getAllCustomFields(nextConf, setSelzyConf) + setLoading({ ...loading, page: false }) + } + setStep(value) + }, + [loading, selzyConf, setLoading, setSelzyConf, setStep] + ) const note = `

          ${__('Step of get API Key:', 'bit-integrations')}

          @@ -45,44 +49,24 @@ function SelzyAuthorization({ selzyConf, setSelzyConf, step, setStep, loading, s ` return ( - - - -
          - {/* SelzyAuthorization */} - handleInput(e, selzyConf, setSelzyConf, error, setError)} - /> - handleInput(e, selzyConf, setSelzyConf, error, setError)} - /> - - - {!isInfo && ( - - handleAuthorize(selzyConf, setSelzyConf, setError, setAuthorized, loading, setLoading) - } - nextPage={nextPage} - auth={authorized} - loading={loading.auth} - /> - )} -
          - - -
          + ) } diff --git a/frontend/src/components/AllIntegrations/Selzy/SelzyCommonFunc.js b/frontend/src/components/AllIntegrations/Selzy/SelzyCommonFunc.js index 19ac93597..a95dcb8ba 100644 --- a/frontend/src/components/AllIntegrations/Selzy/SelzyCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Selzy/SelzyCommonFunc.js @@ -6,6 +6,11 @@ import bitsFetch from '../../../Utils/bitsFetch' import { __ } from '../../../Utils/i18nwrap' import { saveActionConf, saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers' +const getApiKeyFromConf = conf => conf?.api_key || conf?.authKey || '' + +const buildAuthRequestParams = conf => + conf?.connection_id ? { connection_id: conf.connection_id } : { api_key: getApiKeyFromConf(conf) } + export const handleInput = (e, conf, setConf, error, setError) => { const newConf = { ...conf } const inputError = { ...error } @@ -15,45 +20,14 @@ export const handleInput = (e, conf, setConf, error, setError) => { setConf(newConf) } -export const handleAuthorize = (conf, setConf, setError, setAuthorized, loading, setLoading) => { - if (!conf.authKey) { - setError({ authKey: !conf.authKey ? __("API Key can't be empty") : '' }) - return +export const getAllLists = async (conf, setConf, loading, setLoading) => { + if (setLoading) { + setLoading({ ...loading, list: true }) } - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { authKey: conf.authKey } - - bitsFetch(requestParams, 'selzy_handle_authorize').then(result => { - if (result.success && result.data) { - const newConf = { ...conf } - if (result.data) { - if (!newConf.default) { - newConf.default = {} - } - const data = result.data.result?.map(v => ({ - ...v, - id: String(v.id) - })) - newConf.default.lists = data - } - getAllCustomFields(newConf, setConf, loading, setLoading) - setConf(newConf) - setAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed')) - }) -} -export const getAllLists = async (conf, setConf, loading, setLoading) => { - setLoading({ ...loading, list: true }) - const requestParams = { authKey: conf.authKey } - const result = await bitsFetch(requestParams, 'selzy_handle_authorize') + const requestParams = buildAuthRequestParams(conf) + const result = await bitsFetch(requestParams, 'selzy_get_all_lists') + if (result.success) { const data = result.data.result?.map(v => ({ ...v, id: String(v.id) })) const newConf = { ...conf } @@ -63,18 +37,25 @@ export const getAllLists = async (conf, setConf, loading, setLoading) => { } newConf.default.lists = data setConf(newConf) - setLoading({ ...loading, list: false }) - toast.success(__('List refresh successfully')) - return + if (setLoading) { + setLoading({ ...loading, list: false }) + toast.success(__('List refresh successfully')) + } + return true } } - setLoading({ ...loading, list: false }) - toast.success(__('List refresh failed')) + + if (setLoading) { + setLoading({ ...loading, list: false }) + toast.success(__('List refresh failed')) + } + + return false } export const getAllTags = async (conf, setConf, loading, setLoading) => { setLoading && setLoading({ ...loading, tag: true }) - const requestParams = { authKey: conf.authKey } + const requestParams = buildAuthRequestParams(conf) const result = await bitsFetch(requestParams, 'selzy_get_all_tags') if (result.success) { const data = result.data.result @@ -101,7 +82,7 @@ export const getAllTags = async (conf, setConf, loading, setLoading) => { export const getAllCustomFields = async (conf, setConf, loading, setLoading) => { setLoading && setLoading({ ...loading, customFields: true }) - const requestParams = { authKey: conf.authKey } + const requestParams = buildAuthRequestParams(conf) const result = await bitsFetch(requestParams, 'selzy_get_all_custom_fields') if (result.success) { const newConf = { ...conf } diff --git a/frontend/src/components/AllIntegrations/SendFox/SendFoxAuthorization.jsx b/frontend/src/components/AllIntegrations/SendFox/SendFoxAuthorization.jsx index 1f8aca507..1e82b613e 100644 --- a/frontend/src/components/AllIntegrations/SendFox/SendFoxAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/SendFox/SendFoxAuthorization.jsx @@ -1,128 +1,32 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import TutorialLink from '../../Utilities/TutorialLink' -import { handleAuthorize } from './SendFoxCommonFunc' - -export default function SendFoxAuthorization({ - formID, - sendFoxConf, - setSendFoxConf, - step, - setstep, - isLoading, - setIsLoading, - setSnackbar, - redirectLocation, - isInfo -}) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - setstep(2) - // fetchAllList(sendFoxConf, setSendFoxConf, setIsLoading, setSnackbar) - } - - const handleInput = e => { - const newConf = { ...sendFoxConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSendFoxConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function SendFoxAuthorization({ sendFoxConf, setSendFoxConf, step, setstep, isInfo }) { + const note = ` + + ${__('To generate an access token, please visit', 'bit-integrations')} + + ${__(' SendFox OAuth settings', 'bit-integrations')} + + ` return ( -
          - - -
          - {__('Integration Name:', 'bit-integrations')} -
          - - -
          - {__('Authorized Redirect URIs:', 'bit-integrations')} -
          - - -
          - {__('Access Token:', 'bit-integrations')} -
          - -
          {error.access_token}
          - - - {__('To Get Client Auth token, Please Visit', 'bit-integrations')} -   - - {__('SendFox Access Token', 'bit-integrations')} - - - - {!isInfo && ( - <> - -
          - - - )} -
          + ) } diff --git a/frontend/src/components/AllIntegrations/SendFox/SendFoxCommonFunc.js b/frontend/src/components/AllIntegrations/SendFox/SendFoxCommonFunc.js index 96a8769f2..f4b2f5049 100644 --- a/frontend/src/components/AllIntegrations/SendFox/SendFoxCommonFunc.js +++ b/frontend/src/components/AllIntegrations/SendFox/SendFoxCommonFunc.js @@ -5,6 +5,13 @@ import { contactFields } from './SendFoxFieldMap' import { listFields } from './SendFoxListFieldMap' import { unsubscribeFields } from './SendFoxUnsubscribeFieldMap' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + access_token: conf.access_token + } + export const handleInput = (e, sendFoxConf, setSendFoxConf, setIsLoading, setSnackbar, formID) => { const newConf = { ...sendFoxConf } const { name } = e.target @@ -21,7 +28,7 @@ export const handleInput = (e, sendFoxConf, setSendFoxConf, setIsLoading, setSna export const fetchAllList = (sendFoxConf, setSendFoxConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const requestParams = { access_token: sendFoxConf.access_token } + const requestParams = buildAuthRequestParams(sendFoxConf) bitsFetch(requestParams, 'sendfox_fetch_all_list') .then(result => { @@ -45,39 +52,6 @@ export const fetchAllList = (sendFoxConf, setSendFoxConf, setIsLoading, setSnack .catch(() => setIsLoading(false)) } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.access_token) { - setError({ - access_token: !confTmp.access_token ? __("Access Token can't be empty", 'bit-integrations') : '' - }) - return - } - setError({}) - setIsLoading(true) - - const requestParams = { access_token: confTmp.access_token } - - bitsFetch(requestParams, 'sendFox_authorize').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - setConf(newConf) - setisAuthorized(true) - setIsLoading(false) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setIsLoading(false) - toast.error(__('Authorized failed', 'bit-integrations')) - }) -} - export const generateMappedField = sendFoxConf => { const requiredFlds = contactFields.filter(fld => fld.required === true) return requiredFlds.length > 0 diff --git a/frontend/src/components/AllIntegrations/SendGrid/SendGridAuthorization.jsx b/frontend/src/components/AllIntegrations/SendGrid/SendGridAuthorization.jsx index 393830784..e1547310b 100644 --- a/frontend/src/components/AllIntegrations/SendGrid/SendGridAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/SendGrid/SendGridAuthorization.jsx @@ -1,10 +1,9 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { sendGridAuthentication } from './SendGridCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { fetchSendGridCustomFields } from './SendGridCommonFunc' export default function SendGridAuthorization({ sendGridConf, @@ -15,105 +14,50 @@ export default function SendGridAuthorization({ setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '', secretKey: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const refreshCustomFields = useCallback( + connectionId => { + const nextConf = connectionId ? { ...sendGridConf, connection_id: connectionId } : sendGridConf - !sendGridConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...sendGridConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSendGridConf(newConf) - } + fetchSendGridCustomFields(nextConf, setSendGridConf, loading, setLoading) + }, + [loading, sendGridConf, setLoading, setSendGridConf] + ) - return ( -
          - + const handleSetStep = useCallback( + value => { + if (value === 2 && !sendGridConf?.customFields?.length) { + refreshCustomFields() + } -
          - {__('Integration Name:', 'bit-integrations')} -
          - + setStep(value) + }, + [refreshCustomFields, sendGridConf?.customFields?.length, setStep] + ) -
          - {__('API Key:', 'bit-integrations')} -
          - -
          - {error.apiKey} -
          - - {__('To Get API key & Secret Key, Please Visit', 'bit-integrations')} -   - - {__('SendGrid API Token', 'bit-integrations')} - - -
          -
          + const note = ` + + ${__('To Get API key, Please Visit', 'bit-integrations')} + + ${__('SendGrid API Token', 'bit-integrations')} + + ` - {!isInfo && ( -
          - -
          - -
          - )} -
          + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/SendGrid/SendGridCommonFunc.js b/frontend/src/components/AllIntegrations/SendGrid/SendGridCommonFunc.js index 70c580750..0aac6f1cd 100644 --- a/frontend/src/components/AllIntegrations/SendGrid/SendGridCommonFunc.js +++ b/frontend/src/components/AllIntegrations/SendGrid/SendGridCommonFunc.js @@ -37,60 +37,38 @@ export const checkMappedFields = sendGridConf => { return true } -export const sendGridAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading, - type -) => { - if (!confTmp.apiKey) { - setError({ apiKey: !confTmp.apiKey ? __("API key can't be empty", 'bit-integrations') : '' }) - return - } +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + apiKey: confTmp.apiKey + } - setError({}) +export const fetchSendGridCustomFields = (confTmp, setConf, loading, setLoading) => { + const requestParams = buildAuthRequestParams(confTmp) + setLoading({ ...loading, customFields: true }) - if (type === 'authentication') { - setLoading({ ...loading, auth: true }) - } - if (type === 'refreshLists') { - setLoading({ ...loading, customFields: true }) - } - const requestParams = { apiKey: confTmp.apiKey } - - bitsFetch(requestParams, 'sendGrid_authentication').then(result => { + bitsFetch(requestParams, 'sendGrid_fetch_custom_fields').then(result => { if (result && result.success) { const newConf = { ...confTmp } - setIsAuthorized(true) - if (type === 'authentication') { - if (result.data) { - newConf.customFields = result.data - } + + if (result.data) { + newConf.customFields = result.data setConf(newConf) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - } else if (type === 'refreshLists') { - if (result.data) { - newConf.customFields = result.data - setConf(newConf) - } - setLoading({ ...loading, customFields: false }) - toast.success(__('Custom fields fetched successfully', 'bit-integrations')) } + setLoading({ ...loading, customFields: false }) + toast.success(__('Custom fields fetched successfully', 'bit-integrations')) return } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorization Failed', 'bit-integrations')) + setLoading({ ...loading, customFields: false }) + toast.error(__('Custom fields fetching failed', 'bit-integrations')) }) } export const getLists = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, lists: true }) - const requestParams = { apiKey: confTmp.apiKey } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'sendGrid_fetch_all_lists').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/SendGrid/SendGridIntegLayout.jsx b/frontend/src/components/AllIntegrations/SendGrid/SendGridIntegLayout.jsx index 35791fe9a..dae31d337 100644 --- a/frontend/src/components/AllIntegrations/SendGrid/SendGridIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/SendGrid/SendGridIntegLayout.jsx @@ -1,12 +1,11 @@ /* eslint-disable no-unused-vars */ /* eslint-disable react-hooks/exhaustive-deps */ -import { useState } from 'react' import MultiSelect from 'react-multiple-select-dropdown-lite' import { __ } from '../../../Utils/i18nwrap' import Loader from '../../Loaders/Loader' import SendGridActions from './SendGridActions' import 'react-multiple-select-dropdown-lite/dist/index.css' -import { sendGridAuthentication } from './SendGridCommonFunc' +import { fetchSendGridCustomFields } from './SendGridCommonFunc' import SendGridFieldMap from './SendGridFieldMap' import { addFieldMap } from './IntegrationHelpers' @@ -18,9 +17,6 @@ export default function SendGridIntegLayout({ setLoading, setSnackbar }) { - const [error, setError] = useState({ name: '', auth_token: '' }) - const [isAuthorized, setIsAuthorized] = useState(false) - const setChanges = val => { const newConf = { ...sendGridConf } newConf.selectedLists = val @@ -33,17 +29,7 @@ export default function SendGridIntegLayout({ {__('Field Map', 'bit-integrations')} -
          - - - )} - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/SendPulse/SendPulseCommonFunc.js b/frontend/src/components/AllIntegrations/SendPulse/SendPulseCommonFunc.js index 8cccabb71..c15a63214 100644 --- a/frontend/src/components/AllIntegrations/SendPulse/SendPulseCommonFunc.js +++ b/frontend/src/components/AllIntegrations/SendPulse/SendPulseCommonFunc.js @@ -8,13 +8,18 @@ export const handleInput = (e, sendPulseConf, setSendPulseConf) => { setSendPulseConf({ ...newConf }) } +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + client_id: confTmp.client_id, + client_secret: confTmp.client_secret, + tokenDetails: confTmp.tokenDetails + } + // refreshMappedLists export const refreshSendPulseList = (sendPulseConf, setSendPulseConf, setIsLoading, setSnackbar) => { - const refreshListsRequestParams = { - client_id: sendPulseConf.client_id, - client_secret: sendPulseConf.client_secret, - tokenDetails: sendPulseConf.tokenDetails - } + const refreshListsRequestParams = buildAuthRequestParams(sendPulseConf) bitsFetch(refreshListsRequestParams, 'sendPulse_lists') .then(result => { if (result && result.success) { @@ -53,10 +58,8 @@ export const refreshSendPulseList = (sendPulseConf, setSendPulseConf, setIsLoadi // refreshMappedFields export const refreshSendPulseHeader = (sendPulseConf, setSendPulseConf, setIsLoading, setSnackbar) => { const refreshListsRequestParams = { - client_id: sendPulseConf.client_id, - client_secret: sendPulseConf.client_secret, - list_id: sendPulseConf.listId, - tokenDetails: sendPulseConf.tokenDetails + ...buildAuthRequestParams(sendPulseConf), + list_id: sendPulseConf.listId } bitsFetch(refreshListsRequestParams, 'sendPulse_headers') diff --git a/frontend/src/components/AllIntegrations/SendinBlue/SendinBlueAuthorization.jsx b/frontend/src/components/AllIntegrations/SendinBlue/SendinBlueAuthorization.jsx index 3c39eb6d5..073732e8e 100644 --- a/frontend/src/components/AllIntegrations/SendinBlue/SendinBlueAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/SendinBlue/SendinBlueAuthorization.jsx @@ -1,13 +1,11 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' +import Authorization from '../../Connections/Authorization' import { refreshLists } from './SendinBlueCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' export default function SendinBlueAuthorization({ - formID, sendinBlueConf, setSendinBlueConf, step, @@ -15,125 +13,55 @@ export default function SendinBlueAuthorization({ setSnackbar, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_key: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const handleAuthorize = () => { - const newConf = { ...sendinBlueConf } - if (!newConf.name || !newConf.api_key) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - api_key: !newConf.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading('auth') - const data = { api_key: newConf.api_key } - bitsFetch(data, 'sblue_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...sendinBlueConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSendinBlueConf(newConf) - } + const loadLists = useCallback( + connectionId => { + const nextConf = connectionId ? { ...sendinBlueConf, connection_id: connectionId } : sendinBlueConf - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - refreshLists(sendinBlueConf, setSendinBlueConf, setIsLoading, setSnackbar) - } + refreshLists(nextConf, setSendinBlueConf, () => {}, setSnackbar) + }, + [sendinBlueConf, setSendinBlueConf, setSnackbar] + ) - return ( -
      - + const handleSetStep = useCallback( + value => { + if (value === 2 && !sendinBlueConf?.default?.sblueList) { + loadLists() + } + setstep(value) + }, + [loadLists, sendinBlueConf, setstep] + ) -
      - {__('Integration Name:', 'bit-integrations')} -
      - -
      {error.name}
      -
      - {__('API Key:', 'bit-integrations')} -
      - -
      {error.api_key}
      - - {__('To get API , Please Visit', 'bit-integrations')}{' '} + const note = ` + + ${__('To get API, please visit', 'bit-integrations')} - {__('Brevo(Sendinblue) API Console', 'bit-integrations')} + ${__(' Brevo(Sendinblue) API Console', 'bit-integrations')} - - {isLoading === 'auth' && ( -
      - - {__('Checking API Key!!!', 'bit-integrations')} -
      - )} +
      ` - {showAuthMsg && !isAuthorized && !isLoading && ( -
      - - × - - {__('Sorry, Api key is invalid', 'bit-integrations')} -
      - )} - {!isInfo && ( - <> - -
      - - - )} -
      + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/SendinBlue/SendinBlueCommonFunc.js b/frontend/src/components/AllIntegrations/SendinBlue/SendinBlueCommonFunc.js index b77c04172..91e5f8459 100644 --- a/frontend/src/components/AllIntegrations/SendinBlue/SendinBlueCommonFunc.js +++ b/frontend/src/components/AllIntegrations/SendinBlue/SendinBlueCommonFunc.js @@ -7,9 +7,13 @@ export const handleInput = (e, sendinBlueConf, setSendinBlueConf) => { newConf.name = e.target.value setSendinBlueConf({ ...newConf }) } + +const buildAuthRequestParams = conf => + conf.connection_id ? { connection_id: conf.connection_id } : { api_key: conf.api_key } + export const refreshLists = (sendinBlueConf, setSendinBlueConf, setIsLoading, setSnackbar) => { - setIsLoading(true) - const refreshListsRequestParams = { api_key: sendinBlueConf.api_key } + if (typeof setIsLoading === 'function') setIsLoading(true) + const refreshListsRequestParams = buildAuthRequestParams(sendinBlueConf) bitsFetch(refreshListsRequestParams, 'sblue_refresh_lists') .then(result => { if (result && result.success) { @@ -20,13 +24,13 @@ export const refreshLists = (sendinBlueConf, setSendinBlueConf, setIsLoading, se if (result.data.sblueList) { newConf.default.sblueList = result.data.sblueList } - setSnackbar({ show: true, msg: __('List refreshed', 'bit-integrations') }) + setSnackbar?.({ show: true, msg: __('List refreshed', 'bit-integrations') }) setSendinBlueConf({ ...newConf }) } else if ( (result && result.data && result.data.data) || (!result.success && typeof result.data === 'string') ) { - setSnackbar({ + setSnackbar?.({ show: true, msg: sprintf( __('List refresh failed Cause: %s. please try again', 'bit-integrations'), @@ -34,16 +38,18 @@ export const refreshLists = (sendinBlueConf, setSendinBlueConf, setIsLoading, se ) }) } else { - setSnackbar({ show: true, msg: __('List failed. please try again', 'bit-integrations') }) + setSnackbar?.({ show: true, msg: __('List failed. please try again', 'bit-integrations') }) } - setIsLoading(false) + if (typeof setIsLoading === 'function') setIsLoading(false) + }) + .catch(() => { + if (typeof setIsLoading === 'function') setIsLoading(false) }) - .catch(() => setIsLoading(false)) } export const refreshTemplate = (sendinBlueConf, setSendinBlueConf, setSnackbar) => { // setIsLoading(true) - const refreshListsRequestParams = { api_key: sendinBlueConf.api_key } + const refreshListsRequestParams = buildAuthRequestParams(sendinBlueConf) bitsFetch(refreshListsRequestParams, 'sblue_refresh_template').then(result => { if (result && result.success) { const newConf = { ...sendinBlueConf } @@ -53,13 +59,13 @@ export const refreshTemplate = (sendinBlueConf, setSendinBlueConf, setSnackbar) if (result.data.sblueTemplates) { newConf.default.sblueTemplates = result.data.sblueTemplates } - setSnackbar({ show: true, msg: __('Templates refreshed', 'bit-integrations') }) + setSnackbar?.({ show: true, msg: __('Templates refreshed', 'bit-integrations') }) setSendinBlueConf({ ...newConf }) } else if ( (result && result.data && result.data.data) || (!result.success && typeof result.data === 'string') ) { - setSnackbar({ + setSnackbar?.({ show: true, msg: sprintf( __('Templates refresh failed Cause: %s. please try again', 'bit-integrations'), @@ -67,7 +73,7 @@ export const refreshTemplate = (sendinBlueConf, setSendinBlueConf, setSnackbar) ) }) } else { - setSnackbar({ show: true, msg: __('Templates failed. please try again', 'bit-integrations') }) + setSnackbar?.({ show: true, msg: __('Templates failed. please try again', 'bit-integrations') }) } // setIsLoading(false) }) @@ -80,7 +86,7 @@ export const refreshSendinBlueHeader = ( setIsLoading, setSnackbar ) => { - const refreshListsRequestParams = { api_key: sendinBlueConf.api_key } + const refreshListsRequestParams = buildAuthRequestParams(sendinBlueConf) bitsFetch(refreshListsRequestParams, 'sblue_headers') .then(result => { if (result && result.success) { @@ -91,9 +97,9 @@ export const refreshSendinBlueHeader = ( newConf.field_map = Object.values(fields) .filter(f => f.required) .map(f => ({ formField: '', sendinBlueField: f.fieldId, required: true })) - setSnackbar({ show: true, msg: __('Sendinblue fields refreshed', 'bit-integrations') }) + setSnackbar?.({ show: true, msg: __('Sendinblue fields refreshed', 'bit-integrations') }) } else { - setSnackbar({ + setSnackbar?.({ show: true, msg: __( 'No Sendinblue fields found. Try changing the header row number or try again', @@ -104,14 +110,16 @@ export const refreshSendinBlueHeader = ( setSendinBlueConf({ ...newConf }) } else { - setSnackbar({ + setSnackbar?.({ show: true, msg: __('Sendinblue fields refresh failed. please try again', 'bit-integrations') }) } - setIsLoading(false) + if (typeof setIsLoading === 'function') setIsLoading(false) + }) + .catch(() => { + if (typeof setIsLoading === 'function') setIsLoading(false) }) - .catch(() => setIsLoading(false)) } export const checkMappedFields = sendinBlueConf => { diff --git a/frontend/src/components/AllIntegrations/Sendy/SendyAuthorization.jsx b/frontend/src/components/AllIntegrations/Sendy/SendyAuthorization.jsx index 5bb227d29..ee476f61a 100644 --- a/frontend/src/components/AllIntegrations/Sendy/SendyAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Sendy/SendyAuthorization.jsx @@ -1,143 +1,73 @@ -/* eslint-disable no-unused-expressions */ -/* eslint-disable no-undef */ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' -// import { getAllList } from './ElasticEmailCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllBrand } from './SendyCommonFunc' -export default function SendyAuthorization({ sendyConf, setSendyConf, step, setstep, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_key: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const handleAuthorize = () => { - const newConf = { ...sendyConf } - if (!newConf.name || !newConf.api_key) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - api_key: !newConf.api_key ? __("API Key can't be empty", 'bit-integrations') : '', - sendy_url: !newConf.sendy_url ? __("Sendy URL can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading('auth') - const values = { api_key: newConf.api_key, sendy_url: newConf.sendy_url } - bitsFetch(values, 'sendy_authorize').then(result => { - if (result.success) { - setisAuthorized(true) +export default function SendyAuthorization({ + sendyConf, + setSendyConf, + step, + setstep, + setIsLoading, + setSnackbar, + isInfo +}) { + const loadBrands = useCallback( + connectionId => { + const nextConf = connectionId ? { ...sendyConf, connection_id: connectionId } : sendyConf + getAllBrand(nextConf, setSendyConf, setIsLoading, setSnackbar) + }, + [sendyConf, setSendyConf, setIsLoading, setSnackbar] + ) + + const handleSetStep = useCallback( + value => { + if (value === 2 && !sendyConf?.default?.brandList) { + loadBrands() } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...sendyConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSendyConf(newConf) - } + setstep(value) + }, + [loadBrands, sendyConf?.default?.brandList, setstep] + ) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - // !sendyConf?.default && getAllList(sendyConf, setSendyConf, setIsLoading) - setstep(2) - } + const note = ` + + ${__('To get API , Please Visit', 'bit-integrations')} + + ${__('Sendy API Console', 'bit-integrations')} + + + ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - -
      {error.name}
      -
      - {__('API Key:', 'bit-integrations')} -
      - -
      {error.api_key}
      - - {__('To get API , Please Visit', 'bit-integrations')}{' '} - - {__('Sendy API Console', 'bit-integrations')} - - -
      - {__('Sendy URL:', 'bit-integrations')} -
      - -
      {error.sendy_url}
      - {isLoading === 'auth' && ( -
      - - {__('Checking API Key!!!', 'bit-integrations')} -
      - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
      - - × - - {__('Sorry, Api key is invalid', 'bit-integrations')} -
      - )} - {!isInfo && ( - <> - -
      - - - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Sendy/SendyCommonFunc.jsx b/frontend/src/components/AllIntegrations/Sendy/SendyCommonFunc.jsx index 4741fe9cb..00cf7585a 100644 --- a/frontend/src/components/AllIntegrations/Sendy/SendyCommonFunc.jsx +++ b/frontend/src/components/AllIntegrations/Sendy/SendyCommonFunc.jsx @@ -1,8 +1,16 @@ /* eslint-disable no-else-return */ import toast from 'react-hot-toast' -import { __, sprintf } from '../../../Utils/i18nwrap' +import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + api_key: conf.api_key, + sendy_url: conf.sendy_url + } + export const handleInput = ( e, sendyConf, @@ -25,10 +33,8 @@ export const handleInput = ( export const getAllBrand = (sendyConf, setSendyConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const queryParams = { - api_key: sendyConf.api_key, - sendy_url: sendyConf.sendy_url - } + const queryParams = buildAuthRequestParams(sendyConf) + const loadPostTypes = bitsFetch(queryParams, 'get_all_brands').then(result => { if (result && result.success) { const newConf = { ...sendyConf } @@ -49,16 +55,15 @@ export const getAllBrand = (sendyConf, setSendyConf, setIsLoading, setSnackbar) error: __('Error Occurred', 'bit-integrations'), loading: __('Loading Brand...') }) - // .catch(() => setIsLoading(false)) } export const getAllList = (sendyConf, setSendyConf, setIsLoading, setSnackbar) => { setIsLoading(true) const queryParams = { - api_key: sendyConf.api_key, - sendy_url: sendyConf.sendy_url, + ...buildAuthRequestParams(sendyConf), brand_id: sendyConf.brand_id } + const loadPostTypes = bitsFetch(queryParams, 'get_all_lists_from_sendy').then(result => { if (result && result.success) { const newConf = { ...sendyConf } @@ -79,7 +84,6 @@ export const getAllList = (sendyConf, setSendyConf, setIsLoading, setSnackbar) = error: __('Error Occurred', 'bit-integrations'), loading: __('Loading Lists...') }) - // .catch(() => setIsLoading(false)) } export const generateMappedField = sendyConf => { diff --git a/frontend/src/components/AllIntegrations/SeoPress/SeoPressAuthorization.jsx b/frontend/src/components/AllIntegrations/SeoPress/SeoPressAuthorization.jsx index a8f0a28fa..38f4922a5 100644 --- a/frontend/src/components/AllIntegrations/SeoPress/SeoPressAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/SeoPress/SeoPressAuthorization.jsx @@ -1,112 +1,40 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function SeoPressAuthorization({ - formID, seoPressConf, setSeoPressConf, step, nextPage, - isLoading, - setIsLoading, - setSnackbar, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'seopress_authorize').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with SEOPress Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = { ...seoPressConf } - newConf[e.target.name] = e.target.value - setSeoPressConf(newConf) - } + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - - {isLoading === 'auth' && ( -
      - - {__('Checking if SEOPress is active!!!', 'bit-integrations')} -
      - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
      -
      -
      - -
      -
      - {__('SEOPress is not activated or not installed', 'bit-integrations')} -
      -
      -
      - )} - - {showAuthMsg && isAuthorized && !isLoading && ( -
      -
      - -
      -
      {__('SEOPress is activated', 'bit-integrations')}
      -
      - )} - - -
      - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Slack/Slack.jsx b/frontend/src/components/AllIntegrations/Slack/Slack.jsx index 1fc7389a7..7eaeff586 100644 --- a/frontend/src/components/AllIntegrations/Slack/Slack.jsx +++ b/frontend/src/components/AllIntegrations/Slack/Slack.jsx @@ -1,20 +1,18 @@ /* eslint-disable no-unused-expressions */ import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' -import { useNavigate, useParams } from 'react-router' +import { useNavigate } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import SlackAuthorization from './SlackAuthorization' -import { handleInput } from './SlackCommonFunc' import SlackIntegLayout from './SlackIntegLayout' import BackIcn from '../../../Icons/BackIcn' function Slack({ formFields, setFlow, flow, allIntegURL }) { const navigate = useNavigate() - const { formID } = useParams() const [isLoading, setIsLoading] = useState(false) const [step, setstep] = useState(1) const [snack, setSnackbar] = useState({ show: false }) @@ -25,6 +23,7 @@ function Slack({ formFields, setFlow, flow, allIntegURL }) { parse_mode: 'HTML', field_map: [{ formField: '', slackFormField: '' }], channel_id: '', + channels: [], body: '', actions: {} }) @@ -49,14 +48,11 @@ function Slack({ formFields, setFlow, flow, allIntegURL }) { {/* STEP 1 */} {/* STEP 2 */} @@ -72,7 +68,6 @@ function Slack({ formFields, setFlow, flow, allIntegURL }) { }}> handleInput(e, slackConf, setSlackConf, setIsLoading, setSnackbar)} slackConf={slackConf} setSlackConf={setSlackConf} isLoading={isLoading} diff --git a/frontend/src/components/AllIntegrations/Slack/SlackAuthorization.jsx b/frontend/src/components/AllIntegrations/Slack/SlackAuthorization.jsx index 0bb71fc3f..e900ff017 100644 --- a/frontend/src/components/AllIntegrations/Slack/SlackAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Slack/SlackAuthorization.jsx @@ -1,42 +1,47 @@ -import { useState } from 'react' +/* eslint-disable jsx-a11y/anchor-is-valid */ +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { handleAuthorize } from './SlackCommonFunc' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { fetchChannels } from './SlackCommonFunc' export default function SlackAuthorization({ - formID, slackConf, setSlackConf, step, setstep, - isLoading, setIsLoading, - setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ accessToken: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const loadChannels = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...slackConf, connection_id: connectionId } : slackConf - setstep(2) - } - const handleInput = e => { - const newConf = { ...slackConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSlackConf(newConf) - } + await fetchChannels(nextConf, setSlackConf, setIsLoading) + }, + [slackConf, setSlackConf, setIsLoading] + ) + + const handleConnectionSelected = useCallback( + async connectionId => { + await loadChannels(connectionId) + }, + [loadChannels] + ) + + const handleSetStep = useCallback( + value => { + if (value === 2 && !slackConf?.default) { + loadChannels() + } + + setstep(value) + }, + [slackConf, loadChannels, setstep] + ) - const slackInstructions = ` -

      ${__('Get Access Token few step', 'bit-integrations')}

      + const slackInstructions = `

      ${__('Get Access Token few step', 'bit-integrations')}

      • ${__('First create app.', 'bit-integrations')}
      • ${__( @@ -50,84 +55,22 @@ export default function SlackAuthorization({
      ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - - - {__('To get access Token , Please Visit', 'bit-integrations')}{' '} - - {__('Slack Console', 'bit-integrations')} - - - -
      - {__('Access Token:', 'bit-integrations')} -
      - -
      {error.accessToken}
      - - {!isInfo && ( - <> - -
      - - - )} - - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Slack/SlackCommonFunc.js b/frontend/src/components/AllIntegrations/Slack/SlackCommonFunc.js index 9ab8ab241..11a46b36a 100644 --- a/frontend/src/components/AllIntegrations/Slack/SlackCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Slack/SlackCommonFunc.js @@ -1,6 +1,6 @@ -/* eslint-disable no-else-return */ import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' +import toast from 'react-hot-toast' export const handleInput = (e, slackConf, setSlackConf) => { const newConf = { ...slackConf } @@ -13,51 +13,33 @@ export const handleInput = (e, slackConf, setSlackConf) => { setSlackConf({ ...newConf }) } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.accessToken) { - setError({ - accessToken: !confTmp.accessToken ? __("Access Token can't be empty", 'bit-integrations') : '' - }) +export const fetchChannels = async (confTmp, setConf, setIsLoading, type = 'fetch') => { + if (!confTmp.connection_id && !confTmp.accessToken) { + toast.error(__("Access Token can't be empty", 'bit-integrations')) return } - setError({}) setIsLoading(true) - const tokenRequestParams = { accessToken: confTmp.accessToken } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { accessToken: confTmp.accessToken } - bitsFetch(tokenRequestParams, 'slack_authorization_and_fetch_channels') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) + const result = await bitsFetch(requestParams, 'slack_fetch_channels') + + if (result && result.success) { + const newConf = { ...confTmp } + newConf.channels = result.data?.channels || [] + setConf(newConf) + setIsLoading(false) + toast.success( + type === 'refresh' + ? __('Channels fetched successfully', 'bit-integrations') + : __('Channels loaded successfully', 'bit-integrations') + ) + return + } + + setIsLoading(false) + toast.error(__('Channels fetching failed', 'bit-integrations')) } diff --git a/frontend/src/components/AllIntegrations/Slack/SlackIntegLayout.jsx b/frontend/src/components/AllIntegrations/Slack/SlackIntegLayout.jsx index 0da29c2bd..91c8a3548 100644 --- a/frontend/src/components/AllIntegrations/Slack/SlackIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/Slack/SlackIntegLayout.jsx @@ -1,12 +1,18 @@ import { useRef } from 'react' import MultiSelect from 'react-multiple-select-dropdown-lite' -import { useParams } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import Loader from '../../Loaders/Loader' import { setFieldInputOnMsgBody } from '../IntegrationHelpers/IntegrationHelpers' import SlackActions from './SlackActions' +import { fetchChannels } from './SlackCommonFunc' -export default function SlackIntegLayout({ formFields, slackConf, setSlackConf, isLoading }) { +export default function SlackIntegLayout({ + formFields, + slackConf, + setSlackConf, + isLoading, + setIsLoading +}) { const textAreaRef = useRef(null) const handleInput = e => { @@ -15,20 +21,6 @@ export default function SlackIntegLayout({ formFields, slackConf, setSlackConf, setSlackConf(newConf) } - const setMessageBody = val => { - const newConf = { ...slackConf } - newConf.body = val - setSlackConf(newConf) - } - const changeActionRun = e => { - const newConf = { ...slackConf } - if (newConf?.body) { - newConf.body = '' - } - newConf.parse_mode = e.target.value - setSlackConf(newConf) - } - return ( <>
      @@ -40,13 +32,20 @@ export default function SlackIntegLayout({ formFields, slackConf, setSlackConf, value={slackConf.channel_id} className="btcd-paper-inp w-5"> - {slackConf?.tokenDetails?.channels && - slackConf?.tokenDetails?.channels.map(({ id, name }) => ( - - ))} + {(slackConf?.channels || slackConf?.tokenDetails?.channels || []).map(({ id, name }) => ( + + ))} +
      {isLoading && ( { - setIsLoading('auth') - bitsFetch({}, 'slicewp_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with SliceWp affiliate Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(sliceWpConf) - newConf[e.target.name] = e.target.value - setSliceWpConf(newConf) - } +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function SliceWpAuthorization({ sliceWpConf, setSliceWpConf, step, setStep, isInfo }) { return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - - {isLoading === 'auth' && ( -
      - - Checking if SliceWp affiliate is active!!! -
      - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
      - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'SliceWp affiliate' - )} -
      - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Smaily/Smaily.jsx b/frontend/src/components/AllIntegrations/Smaily/Smaily.jsx index 68e67543d..48ede5a47 100644 --- a/frontend/src/components/AllIntegrations/Smaily/Smaily.jsx +++ b/frontend/src/components/AllIntegrations/Smaily/Smaily.jsx @@ -74,9 +74,6 @@ function Smaily({ formFields, setFlow, flow, allIntegURL }) { setSmailyConf={setSmailyConf} step={step} setStep={setStep} - loading={loading} - setLoading={setLoading} - setSnackbar={setSnackbar} /> {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/Smaily/SmailyAuthorization.jsx b/frontend/src/components/AllIntegrations/Smaily/SmailyAuthorization.jsx index d7ce56ff4..007e4b4bc 100644 --- a/frontend/src/components/AllIntegrations/Smaily/SmailyAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Smaily/SmailyAuthorization.jsx @@ -1,46 +1,10 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { smailyAuthentication } from './SmailyCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function SmailyAuthorization({ - smailyConf, - setSmailyConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ - name: '', - subdomain: '', - api_user_name: '', - api_user_password: '' - }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !smailyConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...smailyConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSmailyConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function SmailyAuthorization({ smailyConf, setSmailyConf, step, setStep, isInfo }) { const note = `

      ${__( 'To create API username and password, do the following.', 'bit-integrations' @@ -59,111 +23,28 @@ export default function SmailyAuthorization({ ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - -
      {error.name}
      -
      - {__('Subdomain Name:', 'bit-integrations')} -
      - -
      {error.subdomain}
      -
      - {__('API User Name:', 'bit-integrations')} -
      - -
      {error.api_user_name}
      -
      - {__('API User Password:', 'bit-integrations')} -
      - -
      {error.api_user_password}
      - {smailyConf.subdomain && ( - - {__('To Get subdomain, API user name and password Please Visit', 'bit-integrations')} -   - - {__('Smaily API Token', 'bit-integrations')} - - - )} -
      -
      - - {!isInfo && ( -
      - -
      - -
      - )} - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Smaily/SmailyCommonFunc.js b/frontend/src/components/AllIntegrations/Smaily/SmailyCommonFunc.js index 18779d2fd..3ad4cda5a 100644 --- a/frontend/src/components/AllIntegrations/Smaily/SmailyCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Smaily/SmailyCommonFunc.js @@ -1,9 +1,3 @@ -/* eslint-disable no-console */ -/* eslint-disable no-else-return */ -import toast from 'react-hot-toast' -import bitsFetch from '../../../Utils/bitsFetch' -import { __ } from '../../../Utils/i18nwrap' - export const handleInput = (e, smailyConf, setSmailyConf) => { const newConf = { ...smailyConf } const { name } = e.target @@ -37,48 +31,3 @@ export const checkMappedFields = smailyConf => { } return true } - -export const smailyAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.name || !confTmp.subdomain || !confTmp.api_user_name || !confTmp.api_user_password) { - setError({ - name: !confTmp.name ? __("Integration Name can't be empty", 'bit-integrations') : '', - subdomain: !confTmp.subdomain ? __("Subdomain can't be empty", 'bit-integrations') : '', - api_user_name: !confTmp.api_user_name - ? __("Api user name can't be empty", 'bit-integrations') - : '', - api_user_password: !confTmp.api_user_password - ? __("Api user password can't be empty", 'bit-integrations') - : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - subdomain: confTmp.subdomain, - api_user_name: confTmp.api_user_name, - api_user_password: confTmp.api_user_password - } - - bitsFetch(requestParams, 'smaily_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error( - __('Authorized failed, Please enter valid subdomain name & API credentials', 'bit-integrations') - ) - }) -} diff --git a/frontend/src/components/AllIntegrations/SmartSuite/SmartSuite.jsx b/frontend/src/components/AllIntegrations/SmartSuite/SmartSuite.jsx index 5ce16ecbc..315984532 100644 --- a/frontend/src/components/AllIntegrations/SmartSuite/SmartSuite.jsx +++ b/frontend/src/components/AllIntegrations/SmartSuite/SmartSuite.jsx @@ -94,9 +94,7 @@ function SmartSuite({ formFields, setFlow, flow, allIntegURL }) { setSmartSuiteConf={setSmartSuiteConf} step={step} setStep={setStep} - loading={loading} setLoading={setLoading} - setSnackbar={setSnackbar} /> {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/SmartSuite/SmartSuiteAuthorization.jsx b/frontend/src/components/AllIntegrations/SmartSuite/SmartSuiteAuthorization.jsx index 54b2de78d..2a8e60fa0 100644 --- a/frontend/src/components/AllIntegrations/SmartSuite/SmartSuiteAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/SmartSuite/SmartSuiteAuthorization.jsx @@ -1,150 +1,89 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { smartSuiteAuthentication, getAllSolutions } from './SmartSuiteCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' -import { create } from 'mutative' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllSolutions } from './SmartSuiteCommonFunc' export default function SmartSuiteAuthorization({ smartSuiteConf, setSmartSuiteConf, step, setStep, - loading, setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ workspaceId: '', apiToken: '' }) + const loadSolutions = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...smartSuiteConf, connection_id: connectionId } : smartSuiteConf - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + await getAllSolutions(nextConf, setSmartSuiteConf, setLoading) + }, + [smartSuiteConf, setSmartSuiteConf, setLoading] + ) - !smartSuiteConf?.default - setStep(2) - } + const handleConnectionSelected = useCallback( + async connectionId => { + await loadSolutions(connectionId) + }, + [loadSolutions] + ) - const handleInput = e => { - const { name, value } = e.target - setError(error => - create(error, draftError => { - draftError[name] = '' - }) - ) - setSmartSuiteConf(smartSuiteConf => - create(smartSuiteConf, draftConf => { - draftConf[name] = value - }) - ) - } + const handleSetStep = useCallback( + value => { + if (value === 2 && !smartSuiteConf?.solutions?.length) { + loadSolutions() + } - const ActiveInstructions = ` -

      ${__('To Get Workspace ID & API Token', 'bit-integrations')}

      + setStep(value) + }, + [smartSuiteConf, loadSolutions, setStep] + ) + + const ActiveInstructions = `

      ${__('To Get Workspace ID & API Token', 'bit-integrations')}

      • ${__('First go to your SmartSuite dashboard.', 'bit-integrations')}
      • ${__('Click go to Profile Icon from Right Top corner.', 'bit-integrations')}
      • ${__('Then Click "API Key" from the "My Profile Menu".', 'bit-integrations')}
      • ${__('Then Click and Copy the "Hidden Api Token".', 'bit-integrations')}
      • ${__( - 'Your Workspace Id is the 8 characters that follow https://app.smartsuite.com/ in the SmartSuite URL when you’re logged in.', + 'Your Workspace Id is the 8 characters that follow https://app.smartsuite.com/ in the SmartSuite URL when you are logged in.', 'bit-integrations' )}
      • -
      ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Workspace ID:', 'bit-integrations')} -
      - -
      {error.workspaceId}
      - -
      - {__('API Token:', 'bit-integrations')} -
      - -
      {error.apiToken}
      - - - {__('To Get API Token & Workspace ID, Please Visit', 'bit-integrations')} -   - - {__('SmartSuite API Token & Workspace ID', 'bit-integrations')} - - -
      -
      - - {!isInfo && ( -
      - -
      - -
      - )} - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/SmartSuite/SmartSuiteCommonFunc.js b/frontend/src/components/AllIntegrations/SmartSuite/SmartSuiteCommonFunc.js index 6ce7bea89..52cee63b6 100644 --- a/frontend/src/components/AllIntegrations/SmartSuite/SmartSuiteCommonFunc.js +++ b/frontend/src/components/AllIntegrations/SmartSuite/SmartSuiteCommonFunc.js @@ -45,53 +45,17 @@ export const checkMappedFields = smartSuiteConf => { return true } -export const smartSuiteAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.workspaceId || !confTmp.apiToken) { - setError({ - workspaceId: !confTmp.workspaceId ? __("Workspace ID can't be empty", 'bit-integrations') : '', - apiToken: !confTmp.apiToken ? __("API Token can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - workspaceId: confTmp.workspaceId, - apiToken: confTmp.apiToken - } - - bitsFetch(requestParams, 'smartSuite_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, ' + result.data, 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { workspaceId: confTmp.workspaceId, apiToken: confTmp.apiToken } export const getAllSolutions = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, solution: true }) if (confTmp?.selectedSolution) delete confTmp?.selectedSolution - const requestParams = { - workspaceId: confTmp.workspaceId, - apiToken: confTmp.apiToken - } - - bitsFetch(requestParams, 'smartSuite_fetch_all_solutions').then(result => { + bitsFetch(buildAuthRequestParams(confTmp), 'smartSuite_fetch_all_solutions').then(result => { if (result && result.success) { if (result.data) { setConf(prevConf => { @@ -116,13 +80,10 @@ export const getAllTables = (confTmp, setConf, solution_id, setLoading) => { if (confTmp?.selectedTable) delete confTmp?.selectedTable setLoading({ ...setLoading, table: true }) - const requestParams = { - workspaceId: confTmp.workspaceId, - apiToken: confTmp.apiToken, - solution_id: solution_id - } - - bitsFetch(requestParams, 'smartSuite_fetch_all_tables').then(result => { + bitsFetch( + { ...buildAuthRequestParams(confTmp), solution_id: solution_id }, + 'smartSuite_fetch_all_tables' + ).then(result => { if (result && result.success) { if (result.data) { setConf(prevConf => { @@ -146,12 +107,7 @@ export const getAllTables = (confTmp, setConf, solution_id, setLoading) => { export const getAllUser = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, assignedUser: true }) - const requestParams = { - workspaceId: confTmp.workspaceId, - apiToken: confTmp.apiToken - } - - bitsFetch(requestParams, 'smartSuite_fetch_all_user').then(result => { + bitsFetch(buildAuthRequestParams(confTmp), 'smartSuite_fetch_all_user').then(result => { if (result && result.success) { if (result.data) { setConf(prevConf => { diff --git a/frontend/src/components/AllIntegrations/SuiteDash/SuiteDashAuthorization.jsx b/frontend/src/components/AllIntegrations/SuiteDash/SuiteDashAuthorization.jsx index 33965aa4c..250a860bd 100644 --- a/frontend/src/components/AllIntegrations/SuiteDash/SuiteDashAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/SuiteDash/SuiteDashAuthorization.jsx @@ -1,143 +1,51 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { suiteDashAuthentication } from './SuiteDashCommonFunc' -import Note from '../../Utilities/Note' -import { toast } from 'react-hot-toast' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function SuiteDashAuthorization({ suiteDashConf, setSuiteDashConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ session_token: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !suiteDashConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...suiteDashConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSuiteDashConf(newConf) - } - - const ActiveInstructions = ` -

      ${__('To Get Public Id & Secret Key', 'bit-integrations')}

      -
        -
      • ${__('First go to your SuiteDash dashboard.', 'bit-integrations')}
      • -
      • ${__('Click go to your "Profile" from Right top corner', 'bit-integrations')}
      • -
      • ${__('Then Click "Integrations"', 'bit-integrations')}
      • -
      • ${__('Then Click "Secure Api"', 'bit-integrations')}
      • -
      • ${__('Then copy "API Authorization Credentials"', 'bit-integrations')}
      • -
      ` + const note = ` +

      ${__('To get Public ID and Secret Key', 'bit-integrations')}

      +
        +
      • ${__('Open your SuiteDash dashboard.', 'bit-integrations')}
      • +
      • ${__('Go to Profile, then Integrations.', 'bit-integrations')}
      • +
      • ${__('Open Secure API and copy credentials.', 'bit-integrations')}
      • +
      ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Public Id:', 'bit-integrations')} -
      - -
      {error.public_id}
      - -
      - {__('Secret Key:', 'bit-integrations')} -
      - -
      {error.secret_key}
      - - - {__('To Get Public Id & Secret Key, Please Visit', 'bit-integrations')} -   - - {__('SuiteDash Public Id & Secret Key', 'bit-integrations')} - - -
      -
      - - {!isInfo && ( -
      - -
      - -
      - )} - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/SuiteDash/SuiteDashCommonFunc.js b/frontend/src/components/AllIntegrations/SuiteDash/SuiteDashCommonFunc.js index 898b3b868..1c2f11c0c 100644 --- a/frontend/src/components/AllIntegrations/SuiteDash/SuiteDashCommonFunc.js +++ b/frontend/src/components/AllIntegrations/SuiteDash/SuiteDashCommonFunc.js @@ -18,8 +18,7 @@ export const handleInput = (e, salesmateConf, setSalesmateConf) => { export const refreshSuiteDashFields = (suiteDashConf, setSuiteDashConf, setIsLoading, setSnackbar) => { setIsLoading(true) const requestParams = { - public_id: suiteDashConf.public_id, - secret_key: suiteDashConf.secret_key, + ...buildAuthRequestParams(suiteDashConf), action_name: suiteDashConf.actionName } @@ -81,49 +80,15 @@ export const checkMappedFields = suiteDashConf => { return true } -export const suiteDashAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.public_id || !confTmp.secret_key) { - setError({ - public_id: !confTmp.public_id ? __("Public Id can't be empty", 'bit-integrations') : '', - secret_key: !confTmp.secret_key ? __("Secret key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - public_id: confTmp.public_id, - secret_key: confTmp.secret_key - } - - bitsFetch(requestParams, 'suite_dash_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid Public Id & Secret Key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { public_id: confTmp.public_id, secret_key: confTmp.secret_key } export const getAllCompanies = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, companies: true }) - const requestParams = { - public_id: confTmp.public_id, - secret_key: confTmp.secret_key - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'suite_dash_fetch_all_companies').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/SureCart/SureCart.jsx b/frontend/src/components/AllIntegrations/SureCart/SureCart.jsx index c13cddd3b..d3709bfb6 100644 --- a/frontend/src/components/AllIntegrations/SureCart/SureCart.jsx +++ b/frontend/src/components/AllIntegrations/SureCart/SureCart.jsx @@ -1,7 +1,7 @@ /* eslint-disable no-unused-expressions */ import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' -import { useNavigate, useParams } from 'react-router' +import { useNavigate } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' @@ -14,7 +14,6 @@ import SureCartIntegLayout from './SureCartIntegLayout' function SureCart({ formFields, setFlow, flow, allIntegURL }) { const navigate = useNavigate() - const { formID } = useParams() const [isLoading, setIsLoading] = useState(false) const [step, setStep] = useState(1) const [snack, setSnackbar] = useState({ show: false }) @@ -40,7 +39,6 @@ function SureCart({ formFields, setFlow, flow, allIntegURL }) { name: 'SureCart', type: 'SureCart', mainAction: '', - api_key: '', field_map: [{ formField: '', SureCartFormField: '' }], customerFields, allActions, @@ -67,14 +65,10 @@ function SureCart({ formFields, setFlow, flow, allIntegURL }) { {/* STEP 1 */} {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/SureCart/SureCartAuthorization.jsx b/frontend/src/components/AllIntegrations/SureCart/SureCartAuthorization.jsx index 8a423cb94..f778b482c 100644 --- a/frontend/src/components/AllIntegrations/SureCart/SureCartAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/SureCart/SureCartAuthorization.jsx @@ -1,117 +1,32 @@ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { handleAuthorize } from './SureCartCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' -export default function SureCartAuthorization({ - formID, - sureCartConf, - setSureCartConf, - step, - setStep, - isLoading, - setIsLoading, - setSnackbar, - redirectLocation, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - setStep(2) - } - const handleInput = e => { - const newConf = { ...sureCartConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSureCartConf(newConf) - } +export default function SureCartAuthorization({ sureCartConf, setSureCartConf, step, setStep, isInfo }) { + const note = ` + + ${__('To get bearer token, please visit', 'bit-integrations')} + + ${__(' SureCart developer settings', 'bit-integrations')} + + ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('App api key:', 'bit-integrations')} -
      - -
      {error.api_key}
      - - {__('To get Api key , Please Visit ', 'bit-integrations')} - - {__('SureCart', 'bit-integrations')} - - -
      -
      - - {!isInfo && ( - <> - -
      - - - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/SureCart/SureCartCommonFunc.js b/frontend/src/components/AllIntegrations/SureCart/SureCartCommonFunc.js index ef31df6b1..bb0670b4d 100644 --- a/frontend/src/components/AllIntegrations/SureCart/SureCartCommonFunc.js +++ b/frontend/src/components/AllIntegrations/SureCart/SureCartCommonFunc.js @@ -1,8 +1,3 @@ -/* eslint-disable no-else-return */ -import toast from 'react-hot-toast' -import { __ } from '../../../Utils/i18nwrap' -import bitsFetch from '../../../Utils/bitsFetch' - export const handleInput = (e, slackConf, setSlackConf) => { const newConf = { ...slackConf } const { name } = e.target @@ -14,37 +9,6 @@ export const handleInput = (e, slackConf, setSlackConf) => { setSlackConf({ ...newConf }) } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setIsAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.api_key) { - setError({ api_key: !confTmp.api_key ? __("Api Key can't be empty", 'bit-integrations') : '' }) - return - } - setError({}) - setIsLoading(true) - const auth_url = window.location.origin - const tokenRequestParams = { api_key: confTmp.api_key, auth_url } - - bitsFetch(tokenRequestParams, 'sureCart_authorization').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - setConf(newConf) - setIsAuthorized(true) - setIsLoading(false) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - toast.error(__('Authorization Failed', 'bit-integrations')) - setIsLoading(false) - }) -} - export const checkMappedFields = fieldsMapped => { const checkedField = fieldsMapped ? fieldsMapped?.filter(item => !item.formField || !item.SureCartFormField) diff --git a/frontend/src/components/AllIntegrations/SureMembers/SureMembersAuthorization.jsx b/frontend/src/components/AllIntegrations/SureMembers/SureMembersAuthorization.jsx index e788028af..827502f7b 100644 --- a/frontend/src/components/AllIntegrations/SureMembers/SureMembersAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/SureMembers/SureMembersAuthorization.jsx @@ -1,86 +1,38 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { sureMembersAuthentication } from './SureMembersCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function SureMembersAuthorization({ sureMembersConf, setSureMembersConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !sureMembersConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...sureMembersConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSureMembersConf(newConf) - } - return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - {error.name &&
      {error.name}
      } -
      - - {!isInfo && ( -
      - -
      - -
      - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/SureMembers/SureMembersCommonFunc.js b/frontend/src/components/AllIntegrations/SureMembers/SureMembersCommonFunc.js index 562988478..cafeede06 100644 --- a/frontend/src/components/AllIntegrations/SureMembers/SureMembersCommonFunc.js +++ b/frontend/src/components/AllIntegrations/SureMembers/SureMembersCommonFunc.js @@ -33,27 +33,6 @@ export const checkMappedFields = sureMembersConf => { return true } -export const sureMembersAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.name) { - setError({ name: !confTmp.name ? __("Name can't be empty", 'bit-integrations') : '' }) - return - } - - setLoading({ ...loading, auth: true }) - bitsFetch({}, 'sureMembers_authentication').then(result => { - if (result.success) { - setIsAuthorized(true) - toast.success(__('Connected Successfully', 'bit-integrations')) - setLoading({ ...loading, auth: false }) - return - } - setLoading({ ...loading, auth: false }) - toast.error( - __('Connection failed: install and active SureMembers plugin first!', 'bit-integrations') - ) - }) -} - export const getSureMembersGroups = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, groups: true }) diff --git a/frontend/src/components/AllIntegrations/SystemeIO/SystemeIOAuthorization.jsx b/frontend/src/components/AllIntegrations/SystemeIO/SystemeIOAuthorization.jsx index 6c7ae7cfb..df34a6277 100644 --- a/frontend/src/components/AllIntegrations/SystemeIO/SystemeIOAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/SystemeIO/SystemeIOAuthorization.jsx @@ -1,45 +1,17 @@ /* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { systemeIOAuthentication, getAllTags, getAllFields } from './SystemeIOCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import Authorization from '../../Connections/Authorization' export default function SystemeIOAuthorization({ systemeIOConf, setSystemeIOConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '' }) - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - getAllFields(systemeIOConf, setSystemeIOConf, setLoading) - getAllTags(systemeIOConf, setSystemeIOConf, setLoading) - - !systemeIOConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...systemeIOConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setSystemeIOConf(newConf) - } - - const ActiveInstructions = ` + const activeInstructions = `

      ${__('To Get API Key & API Secret', 'bit-integrations')}

      • ${__('First go to your SystemeIO dashboard.', 'bit-integrations')}
      • @@ -53,82 +25,23 @@ export default function SystemeIOAuthorization({
      ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('API Key:', 'bit-integrations')} -
      - -
      {error.api_key}
      - - - {__('To Get API Key & API Secret, Please Visit', 'bit-integrations')} -   - - {__('SystemeIO API Key & Secret', 'bit-integrations')} - - -
      -
      - - {!isInfo && ( -
      - -
      - -
      - )} - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/SystemeIO/SystemeIOCommonFunc.js b/frontend/src/components/AllIntegrations/SystemeIO/SystemeIOCommonFunc.js index 526d1c1e3..1cc1c723c 100644 --- a/frontend/src/components/AllIntegrations/SystemeIO/SystemeIOCommonFunc.js +++ b/frontend/src/components/AllIntegrations/SystemeIO/SystemeIOCommonFunc.js @@ -4,6 +4,9 @@ import toast from 'react-hot-toast' import bitsFetch from '../../../Utils/bitsFetch' import { __ } from '../../../Utils/i18nwrap' +const buildAuthRequestParams = confTmp => + confTmp.connection_id ? { connection_id: confTmp.connection_id } : { api_key: confTmp.api_key } + export const handleInput = (e, salesmateConf, setSalesmateConf) => { const newConf = { ...salesmateConf } const { name } = e.target @@ -41,46 +44,10 @@ export const checkMappedFields = systemeIOConf => { return true } -export const systemeIOAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - api_key: confTmp.api_key - } - - bitsFetch(requestParams, 'systemeIO_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid Sub Domain & API Key', 'bit-integrations')) - }) -} - export const getAllTags = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, tag: true }) - const requestParams = { - api_key: confTmp.api_key - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'systemeIO_fetch_all_tags').then(result => { if (result && result.success) { @@ -106,9 +73,7 @@ export const getAllTags = (confTmp, setConf, setLoading) => { export const getAllFields = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, fields: true }) - const requestParams = { - api_key: confTmp.api_key - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'systemeIO_fetch_all_fields').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/TeamsForWooCommerceMemberships/TeamsForWooCommerceMembershipsAuthorization.jsx b/frontend/src/components/AllIntegrations/TeamsForWooCommerceMemberships/TeamsForWooCommerceMembershipsAuthorization.jsx index 3945a75b1..4b6868c18 100644 --- a/frontend/src/components/AllIntegrations/TeamsForWooCommerceMemberships/TeamsForWooCommerceMembershipsAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/TeamsForWooCommerceMemberships/TeamsForWooCommerceMembershipsAuthorization.jsx @@ -1,115 +1,36 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function TeamsForWooCommerceMembershipsAuthorization({ - formID, teamsForWcConf, setTeamsForWcConf, step, nextPage, - isLoading, - setIsLoading, - setSnackbar + isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'teams_for_wc_memberships_authorize').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Teams for WooCommerce Memberships Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = { ...teamsForWcConf } - newConf[e.target.name] = e.target.value - setTeamsForWcConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - - {isLoading === 'auth' && ( -
      - - {__('Checking if Teams for WooCommerce Memberships is authorized!!!', 'bit-integrations')} -
      - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
      -
      -
      - -
      -
      - {__( - 'Teams for WooCommerce Memberships is not activated or not installed', - 'bit-integrations' - )} -
      -
      -
      - )} - - {showAuthMsg && isAuthorized && !isLoading && ( -
      -
      - -
      -
      - {__('Teams for WooCommerce Memberships is activated', 'bit-integrations')} -
      -
      - )} - - -
      - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Telegram/TelegramAuthorization.jsx b/frontend/src/components/AllIntegrations/Telegram/TelegramAuthorization.jsx index 8c2e94b9a..cbb51ddf6 100644 --- a/frontend/src/components/AllIntegrations/Telegram/TelegramAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Telegram/TelegramAuthorization.jsx @@ -1,13 +1,11 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' +import Authorization from '../../Connections/Authorization' import { refreshGetUpdates } from './TelegramCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' export default function TelegramAuthorization({ - formID, telegramConf, setTelegramConf, step, @@ -15,120 +13,46 @@ export default function TelegramAuthorization({ setSnackbar, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', bot_api_key: '', apiError: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const handleAuthorize = () => { - const newConf = { ...telegramConf } + const loadUpdates = useCallback( + connectionId => { + const nextConf = connectionId ? { ...telegramConf, connection_id: connectionId } : telegramConf - if (!newConf.name || !newConf.bot_api_key) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - bot_api_key: !newConf.bot_api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading('auth') - const requestParams = { bot_api_key: newConf.bot_api_key } - bitsFetch(requestParams, 'telegram_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else { - setisAuthorized(false) - setError({ apiError: result?.data.description }) - setSnackbar({ show: true, msg: __('Authorized Filled', 'bit-integrations') }) + refreshGetUpdates(nextConf, setTelegramConf, () => {}, setSnackbar) + }, + [setSnackbar, setTelegramConf, telegramConf] + ) + + const handleSetStep = useCallback( + value => { + if (value === 2 && !telegramConf?.default?.telegramChatLists) { + loadUpdates() } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...telegramConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setTelegramConf(newConf) - } + setstep(value) + }, + [loadUpdates, setstep, telegramConf] + ) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - refreshGetUpdates(telegramConf, setTelegramConf, setIsLoading, setSnackbar) - setstep(2) - } + const note = ` + + ${__('Create a Telegram bot with BotFather and copy the bot token.', 'bit-integrations')} + ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - -
      {error.name}
      -
      - {__('Bot API Key:', 'bit-integrations')} -
      - -
      {error.bot_api_key}
      - {isLoading === 'auth' && ( -
      - - {__('Checking API Key!!!', 'bit-integrations')} -
      - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
      - - × - - {error.apiError} -
      - )} - {!isInfo && ( - <> - -
      - - - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Telegram/TelegramCommonFunc.js b/frontend/src/components/AllIntegrations/Telegram/TelegramCommonFunc.js index 10b4a6953..e1e6fae43 100644 --- a/frontend/src/components/AllIntegrations/Telegram/TelegramCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Telegram/TelegramCommonFunc.js @@ -2,10 +2,13 @@ import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' import { create } from 'mutative' +const buildAuthRequestParams = conf => + conf.connection_id ? { connection_id: conf.connection_id } : { bot_api_key: conf.bot_api_key } + export const refreshGetUpdates = (telegramConf, setTelegramConf, setIsLoading, setSnackbar) => { const newConf = { ...telegramConf } - const requestParams = { bot_api_key: newConf.bot_api_key } - setIsLoading(true) + const requestParams = buildAuthRequestParams(newConf) + if (typeof setIsLoading === 'function') setIsLoading(true) bitsFetch(requestParams, 'refresh_get_updates') .then(result => { if (result && result.success) { @@ -16,27 +19,29 @@ export const refreshGetUpdates = (telegramConf, setTelegramConf, setIsLoading, s if (result.data.telegramChatLists) { newConf.default.telegramChatLists = result.data.telegramChatLists } - setSnackbar({ show: true, msg: __('Chat list refreshed', 'bit-integrations') }) + setSnackbar?.({ show: true, msg: __('Chat list refreshed', 'bit-integrations') }) setTelegramConf({ ...newConf }) } else if ( (result && result.data && result.data.data) || (!result.success && typeof result.data === 'string') ) { - setSnackbar({ + setSnackbar?.({ show: true, msg: `${__('Chat list refresh failed Cause:', 'bit-integrations')}${ result.data.data || result.data }. ${__('please try again', 'bit-integrations')}` }) } else { - setSnackbar({ + setSnackbar?.({ show: true, msg: __('Chat list refresh failed. please try again', 'bit-integrations') }) } - setIsLoading(false) + if (typeof setIsLoading === 'function') setIsLoading(false) + }) + .catch(() => { + if (typeof setIsLoading === 'function') setIsLoading(false) }) - .catch(() => setIsLoading(false)) } export const handleInput = (e, telegramConf, setTelegramConf) => { diff --git a/frontend/src/components/AllIntegrations/TheEventsCalendar/TheEventsCalendarAuthorization.jsx b/frontend/src/components/AllIntegrations/TheEventsCalendar/TheEventsCalendarAuthorization.jsx index d6aea31bd..641bd317f 100644 --- a/frontend/src/components/AllIntegrations/TheEventsCalendar/TheEventsCalendarAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/TheEventsCalendar/TheEventsCalendarAuthorization.jsx @@ -1,92 +1,41 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { theEventsCalendarAuthentication } from './theEventsCalendarCommonFunctions' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function TheEventsCalendarAuthorization({ theEventsCalendarConf, setTheEventsCalendarConf, step, setStep, - loading, - setLoading, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !theEventsCalendarConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...theEventsCalendarConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setTheEventsCalendarConf(newConf) - } - return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - {error.name &&
      {error.name}
      } -
      - - {!isInfo && ( -
      - -
      - -
      - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/TheEventsCalendar/theEventsCalendarCommonFunctions.js b/frontend/src/components/AllIntegrations/TheEventsCalendar/theEventsCalendarCommonFunctions.js index 7b1972b6c..ceb58a522 100644 --- a/frontend/src/components/AllIntegrations/TheEventsCalendar/theEventsCalendarCommonFunctions.js +++ b/frontend/src/components/AllIntegrations/TheEventsCalendar/theEventsCalendarCommonFunctions.js @@ -34,31 +34,6 @@ export const checkMappedFields = theEventsCalendarConf => { return true } -export const theEventsCalendarAuthentication = ( - confTmp, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.name) { - setError({ name: !confTmp.name ? __("Name can't be empty", 'bit-integrations') : '' }) - return - } - - setLoading({ ...loading, auth: true }) - bitsFetch({}, 'the_events_calendar_authentication').then(result => { - if (result.success) { - setIsAuthorized(true) - toast.success(__('Connected Successfully', 'bit-integrations')) - setLoading({ ...loading, auth: false }) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__(result?.data ? result.data : 'Something went wrong!', 'bit-integrations')) - }) -} - export const getAllEvents = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, events: true }) diff --git a/frontend/src/components/AllIntegrations/Trello/Trello.jsx b/frontend/src/components/AllIntegrations/Trello/Trello.jsx index 09bed071d..4c49c0c67 100644 --- a/frontend/src/components/AllIntegrations/Trello/Trello.jsx +++ b/frontend/src/components/AllIntegrations/Trello/Trello.jsx @@ -1,24 +1,17 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' -import { useNavigate, useParams } from 'react-router' -import BackIcn from '../../../Icons/BackIcn' +import { useNavigate } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import TrelloAuthorization from './TrelloAuthorization' -import { - handleInput, - setGrantTokenResponse, - checkMappedFields, - generateMappedField -} from './TrelloCommonFunc' +import { handleInput, checkMappedFields, generateMappedField } from './TrelloCommonFunc' import TrelloIntegLayout from './TrelloIntegLayout' function Trello({ formFields, setFlow, flow, allIntegURL }) { const navigate = useNavigate() - const { formID } = useParams() const [isLoading, setIsLoading] = useState(false) const [step, setstep] = useState(1) const [snack, setSnackbar] = useState({ show: false }) @@ -42,10 +35,6 @@ function Trello({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - // eslint-disable-next-line no-unused-expressions - window.opener && setGrantTokenResponse('trello') - }, []) const nextPage = () => { setTimeout(() => { document.getElementById('btcd-settings-wrp').scrollTop = 0 @@ -69,12 +58,10 @@ function Trello({ formFields, setFlow, flow, allIntegURL }) { {/* STEP 1 */} diff --git a/frontend/src/components/AllIntegrations/Trello/TrelloAuthorization.jsx b/frontend/src/components/AllIntegrations/Trello/TrelloAuthorization.jsx index ebc87a7d2..43ee4c08a 100644 --- a/frontend/src/components/AllIntegrations/Trello/TrelloAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Trello/TrelloAuthorization.jsx @@ -1,126 +1,78 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { handleTrelloAuthorize, fetchAllBoard } from './TrelloCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { fetchAllBoard } from './TrelloCommonFunc' export default function TrelloAuthorization({ - formID, trelloConf, setTrelloConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - setstep(2) - fetchAllBoard(formID, trelloConf, setTrelloConf, setIsLoading, setSnackbar) - } - - const handleInput = e => { - const newConf = { ...trelloConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setTrelloConf(newConf) - } - - return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - + const loadBoards = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...trelloConf, connection_id: connectionId } : trelloConf + fetchAllBoard(nextConf, setTrelloConf, setIsLoading, setSnackbar) + }, + [setIsLoading, setSnackbar, setTrelloConf, trelloConf] + ) -
      - {__('Authorized Redirect URIs:', 'bit-integrations')} -
      - + const handleSetStep = useCallback( + value => { + if (value === 2 && !trelloConf?.default?.allBoardlist?.length) { + loadBoards() + } - - {__('To get Client ID , Please Visit', 'bit-integrations')}{' '} - - {__('Trello API Console', 'bit-integrations')} - - + setstep(value) + }, + [loadBoards, setstep, trelloConf?.default?.allBoardlist?.length] + ) -
      - {__('Client id:', 'bit-integrations')} -
      - -
      {error.clientId}
      + const note = `

      ${__('Get Trello OAuth details', 'bit-integrations')}

      +
        +
      • ${__('Open Trello API key page and copy your API key.', 'bit-integrations')}
      • +
      • ${__('Use the callback/return URL from this form when generating your user token.', 'bit-integrations')}
      • +
      • ${__('Authorize and save the connection, then continue to map fields.', 'bit-integrations')}
      • +
      ` -
      {error.clientSecret}
      - {!isInfo && ( - <> - -
      - - - )} -
      + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/Trello/TrelloCommonFunc.js b/frontend/src/components/AllIntegrations/Trello/TrelloCommonFunc.js index f60f4d774..c1526a2cd 100644 --- a/frontend/src/components/AllIntegrations/Trello/TrelloCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Trello/TrelloCommonFunc.js @@ -1,7 +1,14 @@ import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' import { sprintf, __ } from '../../../Utils/i18nwrap' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + accessToken: conf.accessToken + } + export const handleInput = (e, trelloConf, setTrelloConf, setIsLoading, setSnackbar) => { const newConf = { ...trelloConf } const { name } = e.target @@ -18,13 +25,9 @@ export const handleInput = (e, trelloConf, setTrelloConf, setIsLoading, setSnack setTrelloConf({ ...newConf }) } -export const fetchAllBoard = (formID, trelloConf, setTrelloConf, setIsLoading, setSnackbar) => { +export const fetchAllBoard = (trelloConf, setTrelloConf, setIsLoading, setSnackbar) => { setIsLoading(true) - const fetchBoardModulesRequestParams = { - formID, - clientId: trelloConf.clientId, - accessToken: trelloConf.accessToken - } + const fetchBoardModulesRequestParams = buildAuthRequestParams(trelloConf) bitsFetch(fetchBoardModulesRequestParams, 'trello_fetch_all_board') .then(result => { if (result && result.success) { @@ -65,9 +68,8 @@ export const fetchAllBoard = (formID, trelloConf, setTrelloConf, setIsLoading, s export const fetchAllList = (trelloConf, setTrelloConf, setIsLoading, setSnackbar) => { setIsLoading(true) const fetchListModulesRequestParams = { - clientId: trelloConf.clientId, - boardId: trelloConf.boardId, - accessToken: trelloConf.accessToken + ...buildAuthRequestParams(trelloConf), + boardId: trelloConf.boardId } bitsFetch(fetchListModulesRequestParams, 'trello_fetch_all_list_Individual_board') @@ -107,9 +109,8 @@ export const fetchAllList = (trelloConf, setTrelloConf, setIsLoading, setSnackba export const fetchAllCustomFields = (trelloConf, setTrelloConf, setIsLoading, setSnackbar) => { setIsLoading(true) const requestParams = { - clientId: trelloConf.clientId, - boardId: trelloConf.boardId, - accessToken: trelloConf.accessToken + ...buildAuthRequestParams(trelloConf), + boardId: trelloConf.boardId } bitsFetch(requestParams, 'trello_fetch_all_custom_fields') @@ -143,88 +144,6 @@ export const fetchAllCustomFields = (trelloConf, setTrelloConf, setIsLoading, se .catch(() => setIsLoading(false)) } -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation.replace(`${window.opener.location.href}`, '').split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] - } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} - -export const handleTrelloAuthorize = ( - integ, - ajaxInteg, - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.clientId) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - - const apiEndpoint = `https://trello.com/1/authorize?expiration=never&name=MyPersonalToken&scope=read,write&response_type=token&key=${ - confTmp.clientId - }&return_url=${encodeURIComponent(window.location.href)}/&response_type=token` - - const authWindow = window.open(apiEndpoint, integ, 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitsTrello = localStorage.getItem(`__${integ}`) - - if (bitsTrello) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsTrello) - localStorage.removeItem(`__${integ}`) - - if (grantTokenResponse.token) { - grantTokenResponse.code = grantTokenResponse.token - } - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accessToken = grantTokenResponse.code - setConf(newConf) - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } - } - setIsLoading(false) - }, 500) -} export const generateMappedField = cardFields => { const requiredFlds = cardFields.filter(fld => fld.required === true) diff --git a/frontend/src/components/AllIntegrations/TutorLms/TutorLmsAuthorization.jsx b/frontend/src/components/AllIntegrations/TutorLms/TutorLmsAuthorization.jsx index 6e22e24ca..00379de51 100644 --- a/frontend/src/components/AllIntegrations/TutorLms/TutorLmsAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/TutorLms/TutorLmsAuthorization.jsx @@ -1,101 +1,29 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function TutorLmsAuthorization({ - tutorlmsConf, - setTutorlmsConf, - step, - setStep, - setSnackbar -}) { - const [isAuthorized, setisAuthorized] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'tutor_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Tutor LMS Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(tutorlmsConf) - newConf[e.target.name] = e.target.value - setTutorlmsConf(newConf) - } +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function TutorLmsAuthorization({ tutorlmsConf, setTutorlmsConf, step, setStep, isInfo }) { return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - - {isLoading === 'auth' && ( -
      - - Checking if Tutor LMS is active!!! -
      - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
      - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'Tutor LMS' - )} -
      - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Twilio/Twilio.jsx b/frontend/src/components/AllIntegrations/Twilio/Twilio.jsx index 16dd8b18b..951ce2acb 100644 --- a/frontend/src/components/AllIntegrations/Twilio/Twilio.jsx +++ b/frontend/src/components/AllIntegrations/Twilio/Twilio.jsx @@ -72,9 +72,6 @@ function Twilio({ formFields, setFlow, flow, allIntegURL }) { setTwilioConf={setTwilioConf} step={step} setstep={setstep} - isLoading={isLoading} - setIsLoading={setIsLoading} - setSnackbar={setSnackbar} /> {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/Twilio/TwilioAuthorization.jsx b/frontend/src/components/AllIntegrations/Twilio/TwilioAuthorization.jsx index 60953d20d..8a7a53df1 100644 --- a/frontend/src/components/AllIntegrations/Twilio/TwilioAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Twilio/TwilioAuthorization.jsx @@ -1,136 +1,45 @@ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +/* eslint-disable jsx-a11y/anchor-is-valid */ +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { handleAuthorize } from './TwilioCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function TwilioAuthorization({ - twilioConf, - setTwilioConf, - step, - setstep, - isLoading, - setIsLoading, - setSnackbar, - isInfo -}) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ username: '', password: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - setstep(2) - } - const handleInput = e => { - const newConf = { ...twilioConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setTwilioConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function TwilioAuthorization({ twilioConf, setTwilioConf, step, setstep, isInfo }) { + const note = `

      ${__('To get Account SID and Auth Token:', 'bit-integrations')}

      +
        +
      • ${__( + 'Visit your', + 'bit-integrations' + )} Twilio Console.
      • +
      • ${__('Copy your Account SID and use it as Username.', 'bit-integrations')}
      • +
      • ${__('Copy your Auth Token and use it as Password.', 'bit-integrations')}
      • +
      • ${__('Use your Twilio sender number in the From Number field.', 'bit-integrations')}
      • +
      ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Account SID:', 'bit-integrations')} -
      - -
      {error.sid}
      - - - {__('To get Account SID and Auth Token , Please Visit', 'bit-integrations')}{' '} - - {__('Twilio Console', 'bit-integrations')} - - - -
      - {__('Auth Token:', 'bit-integrations')} -
      - -
      {error.token}
      - -
      - {__('From:', 'bit-integrations')} -
      - -
      {error.from_num}
      - - {!isInfo && ( -
      - -
      - -
      - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Twilio/TwilioCommonFunc.js b/frontend/src/components/AllIntegrations/Twilio/TwilioCommonFunc.js index 82ad9280b..161261b6e 100644 --- a/frontend/src/components/AllIntegrations/Twilio/TwilioCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Twilio/TwilioCommonFunc.js @@ -1,8 +1,3 @@ -/* eslint-disable no-else-return */ -import toast from 'react-hot-toast' -import { __ } from '../../../Utils/i18nwrap' -import bitsFetch from '../../../Utils/bitsFetch' - export const handleInput = ( e, twilioConf, @@ -37,57 +32,3 @@ export const checkMappedFields = twilioConf => { } return true } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.sid || !confTmp.token || !confTmp.from_num) { - setError({ - sid: !confTmp.sid ? __("Account SID can't be empty", 'bit-integrations') : '', - token: !confTmp.token ? __("Auth Token can't be empty", 'bit-integrations') : '', - from_num: !confTmp.from_num ? __("Phone number can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setIsLoading(true) - - const tokenRequestParams = { - sid: confTmp.sid, - token: confTmp.token, - from_num: confTmp.from_num - } - - bitsFetch(tokenRequestParams, 'twilio_authorization') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/UltimateAffiliatePro/UltimateAffiliateProAuthorization.jsx b/frontend/src/components/AllIntegrations/UltimateAffiliatePro/UltimateAffiliateProAuthorization.jsx index df3caf441..3c1aba142 100644 --- a/frontend/src/components/AllIntegrations/UltimateAffiliatePro/UltimateAffiliateProAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/UltimateAffiliatePro/UltimateAffiliateProAuthorization.jsx @@ -1,124 +1,54 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + export default function UltimateAffiliateProAuthorization({ ultimateAffiliateProConf, setUltimateAffiliateProConf, step, nextPage, - isLoading, - setIsLoading, - setSnackbar, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - - const authorizeHandler = () => { - if (isInfo || typeof setIsLoading !== 'function' || typeof setSnackbar !== 'function') { - return - } - - setIsLoading('auth') - bitsFetch({}, 'ultimate_affiliate_pro_authorize').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with Ultimate Affiliate Pro Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - if (isInfo || typeof setUltimateAffiliateProConf !== 'function') { - return - } - - const newConf = { ...ultimateAffiliateProConf } - newConf[e.target.name] = e.target.value - setUltimateAffiliateProConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
      - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - - {isLoading === 'auth' && ( -
      - - {__('Checking if Ultimate Affiliate Pro is authorized...', 'bit-integrations')} -
      - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
      -
      -
      - -
      -
      - {__('Ultimate Affiliate Pro is not activated or not installed', 'bit-integrations')} -
      -
      -
      - )} - - {showAuthMsg && isAuthorized && !isLoading && ( -
      -
      - -
      -
      {__('Ultimate Affiliate Pro is activated', 'bit-integrations')}
      -
      - )} - - {!isInfo && ( - <> - -
      - - - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/UserRegistrationMembership/UserRegistrationMembershipAuthorization.jsx b/frontend/src/components/AllIntegrations/UserRegistrationMembership/UserRegistrationMembershipAuthorization.jsx index 0cd3a52b7..39e365125 100644 --- a/frontend/src/components/AllIntegrations/UserRegistrationMembership/UserRegistrationMembershipAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/UserRegistrationMembership/UserRegistrationMembershipAuthorization.jsx @@ -1,71 +1,36 @@ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' -import { userRegistrationAuthorize } from './UserRegistrationMembershipCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function UserRegistrationMembershipAuthorization({ userRegistrationConf, setUserRegistrationConf, step, nextPage, - isLoading, - setIsLoading, - setSnackbar + isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - - const handleAuthorize = () => { - userRegistrationAuthorize(setIsAuthorized, setShowAuthMsg, setIsLoading, setSnackbar, nextPage) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - setUserRegistrationConf({ ...userRegistrationConf, name: e.target.value })} - name="name" - value={userRegistrationConf.name} - type="text" - placeholder={__('Integration Name...', 'bit-integrations')} - disabled={isAuthorized} - /> - - -
      - {showAuthMsg && isAuthorized && ( -
      - -
      - )} - -
      + ) + }} + /> ) } diff --git a/frontend/src/components/AllIntegrations/UserRegistrationMembership/UserRegistrationMembershipCommonFunc.js b/frontend/src/components/AllIntegrations/UserRegistrationMembership/UserRegistrationMembershipCommonFunc.js index b5e075301..13719120f 100644 --- a/frontend/src/components/AllIntegrations/UserRegistrationMembership/UserRegistrationMembershipCommonFunc.js +++ b/frontend/src/components/AllIntegrations/UserRegistrationMembership/UserRegistrationMembershipCommonFunc.js @@ -8,45 +8,6 @@ export const handleInput = (e, userRegistrationConf, setUserRegistrationConf) => setUserRegistrationConf(newConf) } -export const userRegistrationAuthorize = ( - setIsAuthorized, - setShowAuthMsg, - setIsLoading, - setSnackbar, - nextPage -) => { - setIsLoading(true) - bitsFetch({}, 'user_registration_authorize') - .then(result => { - if (result?.success) { - setIsAuthorized(true) - setShowAuthMsg(true) - setSnackbar({ - show: true, - msg: __('Connected Successfully', 'bit-integrations') - }) - nextPage(2) - } else { - setSnackbar({ - show: true, - msg: __( - result?.data || - 'Connection failed. Please make sure User Registration and Membership plugin is installed and activated.', - 'bit-integrations' - ) - }) - } - setIsLoading(false) - }) - .catch(error => { - setSnackbar({ - show: true, - msg: __('Connection failed', 'bit-integrations') - }) - setIsLoading(false) - }) -} - export const refreshForms = ( userRegistrationConf, setUserRegistrationConf, diff --git a/frontend/src/components/AllIntegrations/Vbout/Vbout.jsx b/frontend/src/components/AllIntegrations/Vbout/Vbout.jsx index e5ae345ee..d1f359c50 100644 --- a/frontend/src/components/AllIntegrations/Vbout/Vbout.jsx +++ b/frontend/src/components/AllIntegrations/Vbout/Vbout.jsx @@ -73,7 +73,6 @@ function Vbout({ formFields, setFlow, flow, allIntegURL }) { setstep={setstep} loading={loading} setLoading={setLoading} - setSnackbar={setSnackbar} /> {/* STEP 2 */} diff --git a/frontend/src/components/AllIntegrations/Vbout/VboutAuthorization.jsx b/frontend/src/components/AllIntegrations/Vbout/VboutAuthorization.jsx index f18097dc0..da09cc2cf 100644 --- a/frontend/src/components/AllIntegrations/Vbout/VboutAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Vbout/VboutAuthorization.jsx @@ -1,11 +1,10 @@ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +/* eslint-disable jsx-a11y/anchor-is-valid */ +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { handleAuthorize } from './VboutCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' import { getAllLists } from './VboutCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' export default function VboutAuthorization({ vboutConf, @@ -14,118 +13,66 @@ export default function VboutAuthorization({ setstep, loading, setLoading, - setSnackbar, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', auth_token: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !vboutConf?.default - setstep(2) - if (!vboutConf.list_id) { - getAllLists(vboutConf, setVboutConf, loading, setLoading) - } - } - const handleInput = e => { - const newConf = { ...vboutConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setVboutConf(newConf) - } - const note = ` -

      ${__('Step of get API Key:', 'bit-integrations')}

      -
        -
      • ${__( - 'Goto Settings and click on', - 'bit-integrations' - )} ${__('API Integrations', 'bit-integrations')}
      • -
      • ${__( - 'Copy the Key and paste into API Key field of your authorization form.', - 'bit-integrations' - )}
      • -
      • ${__('Finally, click Authorize button.', 'bit-integrations')}
      • -
      - ` - - return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - + const loadLists = useCallback( + connectionId => { + const nextConf = connectionId ? { ...vboutConf, connection_id: connectionId } : vboutConf + getAllLists(nextConf, setVboutConf, loading, setLoading) + }, + [vboutConf, setVboutConf, loading, setLoading] + ) - - {__('To get API key, please visit', 'bit-integrations')} -   - - {__('Vbout API Key', 'bit-integrations')} - - + const handleConnectionSelected = useCallback( + async connectionId => { + loadLists(connectionId) + }, + [loadLists] + ) -
      - {__('API Key:', 'bit-integrations')} -
      - -
      {error.auth_token}
      + const handleSetStep = useCallback( + value => { + if (value === 2 && !vboutConf?.default && !vboutConf?.list_id) { + loadLists() + } + setstep(value) + }, + [vboutConf, loadLists, setstep] + ) - {!isInfo && ( -
      - -
      - -
      - )} - -
      + return ( + ) } + +const note = ` +

      ${__('Step of get API Key:', 'bit-integrations')}

      +
        +
      • ${__( + 'Goto Settings and click on', + 'bit-integrations' + )} ${__('API Integrations', 'bit-integrations')}
      • +
      • ${__( + 'Copy the Key and paste into API Key field of your authorization form.', + 'bit-integrations' + )}
      • +
      • ${__('Finally, click Authorize button.', 'bit-integrations')}
      • +
      +` diff --git a/frontend/src/components/AllIntegrations/Vbout/VboutCommonFunc.js b/frontend/src/components/AllIntegrations/Vbout/VboutCommonFunc.js index 58fb19faa..5ded395b8 100644 --- a/frontend/src/components/AllIntegrations/Vbout/VboutCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Vbout/VboutCommonFunc.js @@ -38,35 +38,13 @@ export const checkMappedFields = vboutConf => { } return true } -export const handleAuthorize = (confTmp, setConf, setError, setisAuthorized, loading, setLoading) => { - if (!confTmp.auth_token) { - setError({ - auth_token: !confTmp.auth_token ? __("Api Key can't be empty", 'bit-integrations') : '' - }) - return - } - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { auth_token: confTmp.auth_token } - bitsFetch(requestParams, 'vbout_handle_authorize').then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - setConf(newConf) - setisAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed', 'bit-integrations')) - }) -} export const VboutRefreshFields = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, field: true }) - const requestParams = { auth_token: confTmp.auth_token, list_id: confTmp.list_id } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id, list_id: confTmp.list_id } + : { auth_token: confTmp.auth_token, list_id: confTmp.list_id } bitsFetch(requestParams, 'vbout_refresh_fields').then(result => { if (result && result.success) { @@ -88,7 +66,9 @@ export const VboutRefreshFields = (confTmp, setConf, loading, setLoading) => { export const getAllLists = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, list: true }) - const requestParams = { auth_token: confTmp.auth_token } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { auth_token: confTmp.auth_token } bitsFetch(requestParams, 'vbout_fetch_all_lists').then(result => { if (result && result.success) { diff --git a/frontend/src/components/AllIntegrations/Voxel/VoxelAuthorization.jsx b/frontend/src/components/AllIntegrations/Voxel/VoxelAuthorization.jsx index 60e880f0f..27eecfc1d 100644 --- a/frontend/src/components/AllIntegrations/Voxel/VoxelAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Voxel/VoxelAuthorization.jsx @@ -1,86 +1,29 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { voxelAuthentication } from './VoxelCommonFunctions' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function VoxelAuthorization({ - voxelConf, - setVoxelConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !voxelConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...voxelConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setVoxelConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function VoxelAuthorization({ voxelConf, setVoxelConf, step, setStep, isInfo }) { return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - {error.name &&
      {error.name}
      } -
      - - {!isInfo && ( -
      - -
      - -
      - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Voxel/VoxelCommonFunctions.js b/frontend/src/components/AllIntegrations/Voxel/VoxelCommonFunctions.js index f01e20f21..97bb0abec 100644 --- a/frontend/src/components/AllIntegrations/Voxel/VoxelCommonFunctions.js +++ b/frontend/src/components/AllIntegrations/Voxel/VoxelCommonFunctions.js @@ -34,25 +34,6 @@ export const checkMappedFields = voxelConf => { return true } -export const voxelAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.name) { - setError({ name: !confTmp.name ? __("Name can't be empty", 'bit-integrations') : '' }) - return - } - - setLoading({ ...loading, auth: true }) - bitsFetch({}, 'voxel_authentication').then(result => { - if (result.success) { - setIsAuthorized(true) - toast.success(__('Connected Successfully', 'bit-integrations')) - setLoading({ ...loading, auth: false }) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__(result?.data ? result.data : 'Something went wrong!', 'bit-integrations')) - }) -} - export const getPostTypes = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, postTypes: true }) diff --git a/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateAuthorization.jsx b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateAuthorization.jsx index 3e9794532..9768c9622 100644 --- a/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/WCAffiliate/WCAffiliateAuthorization.jsx @@ -1,120 +1,40 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' +import Authorization from '../../Connections/Authorization' export default function WCAffiliateAuthorization({ wcAffiliateConf, setWCAffiliateConf, step, nextPage, - isLoading, - setIsLoading, - setSnackbar, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - - const authorizeHandler = () => { - if (isInfo) { - return - } - - setIsLoading('auth') - bitsFetch({}, 'wc_affiliate_authorize').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with WC Affiliate successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - if (!setWCAffiliateConf) { - return - } - - const newConf = { ...wcAffiliateConf } - newConf[e.target.name] = e.target.value - setWCAffiliateConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
      -
      - {__('Integration Name:', 'bit-integrations')} -
      - - - {isLoading === 'auth' && ( -
      - - {__('Checking if WC Affiliate is authorized...', 'bit-integrations')} -
      - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
      -
      -
      - -
      -
      - {__('WC Affiliate is not activated or not installed', 'bit-integrations')} -
      -
      -
      - )} - - {showAuthMsg && isAuthorized && !isLoading && ( -
      -
      - -
      -
      {__('WC Affiliate is activated', 'bit-integrations')}
      -
      - )} - - -
      - {!isInfo && ( - - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/WPCafe/WPCafeAuthorization.jsx b/frontend/src/components/AllIntegrations/WPCafe/WPCafeAuthorization.jsx index d4e5dc4db..aaac22dde 100644 --- a/frontend/src/components/AllIntegrations/WPCafe/WPCafeAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/WPCafe/WPCafeAuthorization.jsx @@ -1,78 +1,30 @@ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' -import { wpcafeAuthentication } from './WPCafeCommonFunc' - -export default function WPCafeAuthorization({ - formID, - wpcafeConf, - setWpcafeConf, - step, - nextPage, - isLoading, - setIsLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - const handleInput = e => { - const newConf = { ...wpcafeConf } - newConf[e.target.name] = e.target.value - setWpcafeConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function WPCafeAuthorization({ wpcafeConf, setWpcafeConf, step, nextPage, isInfo }) { + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - -
      {error.name}
      - - - - {!isInfo && ( - <> - -
      - - - )} -
      + ) + }} + /> ) } diff --git a/frontend/src/components/AllIntegrations/WPCafe/WPCafeCommonFunc.js b/frontend/src/components/AllIntegrations/WPCafe/WPCafeCommonFunc.js index cce22fb53..bf2190131 100644 --- a/frontend/src/components/AllIntegrations/WPCafe/WPCafeCommonFunc.js +++ b/frontend/src/components/AllIntegrations/WPCafe/WPCafeCommonFunc.js @@ -1,6 +1,4 @@ import { create } from 'mutative' -import bitsFetch from '../../../Utils/bitsFetch' -import { __ } from '../../../Utils/i18nwrap' export const handleInput = (e, wpcafeConf, setWpcafeConf) => { const newConf = create(wpcafeConf, draftConf => { @@ -30,23 +28,3 @@ export const checkMappedFields = wpcafeConf => { } return true } - -export const wpcafeAuthentication = (confTmp, setConf, setError, setIsAuthorized, setIsLoading) => { - if (!confTmp.name) { - setError({ name: !confTmp.name ? __("Integration name cann't be empty", 'bit-integrations') : '' }) - return - } - - setError({}) - setIsLoading(true) - - const requestParams = { name: confTmp.name } - - bitsFetch(requestParams, 'wpcafe_authorize').then(result => { - if (result && result.success) { - setIsAuthorized(true) - } - - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/WPCourseware/WPCoursewareAuthorization.jsx b/frontend/src/components/AllIntegrations/WPCourseware/WPCoursewareAuthorization.jsx index 46bea75de..be9e8f4bc 100644 --- a/frontend/src/components/AllIntegrations/WPCourseware/WPCoursewareAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/WPCourseware/WPCoursewareAuthorization.jsx @@ -1,109 +1,39 @@ -/* eslint-disable react/jsx-no-useless-fragment */ -import { useEffect, useState } from 'react' -import { __, sprintf } from '../../../Utils/i18nwrap' -import bitsFetch from '../../../Utils/bitsFetch' -import LoaderSm from '../../Loaders/LoaderSm' -import BackIcn from '../../../Icons/BackIcn' -import TutorialLink from '../../Utilities/TutorialLink' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function WPCoursewareAuthorization({ - formID, wpCoursewareConf, setWPCoursewareConf, step, nextPage, - setSnackbar, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ integrationName: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const [isMounted, setIsMounted] = useState(true) - useEffect( - () => () => { - setIsMounted(false) - }, - [] - ) - - const handleAuthorize = () => { - setIsLoading('auth') - bitsFetch({}, 'wpCourseware_authorize').then(result => { - if (isMounted) { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Connect Successfully', 'bit-integrations') }) - } - setShowAuthMsg(true) - setIsLoading(false) - } - }) - } - const handleInput = e => { - const newConf = { ...wpCoursewareConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setWPCoursewareConf(newConf) - } - + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( - <> -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - - {showAuthMsg && !isAuthorized && !isLoading && ( -
      - - × - - {sprintf( - __('Please! First Install or Active %s Plugin', 'bit-integrations'), - 'WP Courseware' - )} -
      - )} - {!isInfo && ( - <> - -
      - - - )} -
      - + ) } diff --git a/frontend/src/components/AllIntegrations/WPForo/WPForoAuthorization.jsx b/frontend/src/components/AllIntegrations/WPForo/WPForoAuthorization.jsx index 98e5b2e9d..42555d1f5 100644 --- a/frontend/src/components/AllIntegrations/WPForo/WPForoAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/WPForo/WPForoAuthorization.jsx @@ -1,86 +1,29 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { wpforoAuthentication } from './WPForoCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function WPForoAuthorization({ - wpforoConf, - setWPForoConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !wpforoConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...wpforoConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setWPForoConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function WPForoAuthorization({ wpforoConf, setWPForoConf, step, setStep, isInfo }) { return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - {error.name &&
      {error.name}
      } -
      - - {!isInfo && ( -
      - -
      - -
      - )} -
      + ) } diff --git a/frontend/src/components/AllIntegrations/WPForo/WPForoCommonFunc.js b/frontend/src/components/AllIntegrations/WPForo/WPForoCommonFunc.js index c6a3d6820..38eb95243 100644 --- a/frontend/src/components/AllIntegrations/WPForo/WPForoCommonFunc.js +++ b/frontend/src/components/AllIntegrations/WPForo/WPForoCommonFunc.js @@ -34,25 +34,6 @@ export const checkMappedFields = wpforoConf => { return true } -export const wpforoAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.name) { - setError({ name: !confTmp.name ? __("Name can't be empty", 'bit-integrations') : '' }) - return - } - - setLoading({ ...loading, auth: true }) - bitsFetch({}, 'wpforo_authentication').then(result => { - if (result.success) { - setIsAuthorized(true) - toast.success(__('Connected Successfully', 'bit-integrations')) - setLoading({ ...loading, auth: false }) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Connection failed: install and active WPForo plugin first!', 'bit-integrations')) - }) -} - export const getWPForoReputations = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, reputation: true }) diff --git a/frontend/src/components/AllIntegrations/WeDocs/WeDocsAuthorization.jsx b/frontend/src/components/AllIntegrations/WeDocs/WeDocsAuthorization.jsx index 0b1907d7e..c340dd6de 100644 --- a/frontend/src/components/AllIntegrations/WeDocs/WeDocsAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/WeDocs/WeDocsAuthorization.jsx @@ -1,78 +1,34 @@ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import TutorialLink from '../../Utilities/TutorialLink' -import { weDocsAuthentication } from './WeDocsCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' -export default function WeDocsAuthorization({ - weDocsConf, - setWeDocsConf, - step, - nextPage, - isLoading, - setIsLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ name: '' }) - - const handleInput = e => { - const newConf = { ...weDocsConf } - newConf[e.target.name] = e.target.value - setWeDocsConf(newConf) - } +export default function WeDocsAuthorization({ weDocsConf, setWeDocsConf, step, nextPage, isInfo }) { + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - -
      {error.name}
      - - - - {!isInfo && ( - <> - -
      - - - )} -
      + ) + }} + /> ) } diff --git a/frontend/src/components/AllIntegrations/WeDocs/WeDocsCommonFunc.js b/frontend/src/components/AllIntegrations/WeDocs/WeDocsCommonFunc.js index 82ec6415c..ba9cc4e05 100644 --- a/frontend/src/components/AllIntegrations/WeDocs/WeDocsCommonFunc.js +++ b/frontend/src/components/AllIntegrations/WeDocs/WeDocsCommonFunc.js @@ -120,45 +120,3 @@ export const refreshSections = (documentationId, setWeDocsConf, setIsLoading, se setIsLoading(false) }) } - -export const weDocsAuthentication = ( - confTmp, - setWeDocsConf, - setError, - setIsAuthorized, - setIsLoading -) => { - if (!confTmp?.name) { - setError({ - name: __("Integration name can't be empty", 'bit-integrations') - }) - return - } - - setError({}) - setIsLoading(true) - - bitsFetch({ name: confTmp.name }, 'wedocs_authorize') - .then(result => { - if (result?.success) { - setIsAuthorized(true) - setWeDocsConf(prevConf => - create(prevConf, draftConf => { - draftConf.name = confTmp.name - }) - ) - } else { - setError({ - name: result?.data || __('Authorization failed', 'bit-integrations') - }) - } - }) - .catch(() => { - setError({ - name: __('Authorization failed', 'bit-integrations') - }) - }) - .finally(() => { - setIsLoading(false) - }) -} diff --git a/frontend/src/components/AllIntegrations/WhatsApp/WhatsApp.jsx b/frontend/src/components/AllIntegrations/WhatsApp/WhatsApp.jsx index d3b27baf7..2d23a3e84 100644 --- a/frontend/src/components/AllIntegrations/WhatsApp/WhatsApp.jsx +++ b/frontend/src/components/AllIntegrations/WhatsApp/WhatsApp.jsx @@ -1,6 +1,6 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' -import { useNavigate, useParams } from 'react-router' +import { useNavigate } from 'react-router' import BackIcn from '../../../Icons/BackIcn' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' @@ -10,14 +10,13 @@ import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' // import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' // import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import WhatsAppAuthorization from './WhatsAppAuthorization' -import { handleInput, generateMappedField, checkDisabledButton } from './WhatsAppCommonFunc' +import { generateMappedField, checkDisabledButton } from './WhatsAppCommonFunc' import WhatsAppIntegLayout from './WhatsAppIntegLayout' import { useRecoilValue } from 'recoil' import { $appConfigState } from '../../../GlobalStates' function WhatsApp({ formFields, setFlow, flow, allIntegURL }) { const navigate = useNavigate() - const { formID } = useParams() const [isLoading, setIsLoading] = useState(false) const [step, setstep] = useState(1) const [snack, setSnackbar] = useState({ show: false }) @@ -90,14 +89,10 @@ function WhatsApp({ formFields, setFlow, flow, allIntegURL }) {

    {/* STEP 2 */} @@ -106,7 +101,6 @@ function WhatsApp({ formFields, setFlow, flow, allIntegURL }) { style={{ ...(step === 2 && { width: 900, height: 'auto', overflow: 'visible' }) }}> handleInput(e, whatsAppConf, setWhatsAppConf, setIsLoading, setSnackbar)} whatsAppConf={whatsAppConf} setWhatsAppConf={setWhatsAppConf} isLoading={isLoading} diff --git a/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppAuthorization.jsx b/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppAuthorization.jsx index cb412baf0..6d77d39de 100644 --- a/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppAuthorization.jsx @@ -1,131 +1,46 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +/* eslint-disable jsx-a11y/anchor-is-valid */ +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { handleAuthorize } from './WhatsAppCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function WhatsAppAuthorization({ - formID, - whatsAppConf, - setWhatsAppConf, - step, - setstep, - isLoading, - setIsLoading, - setSnackbar, - redirectLocation, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - setstep(2) - } - - const handleInput = e => { - const newConf = { ...whatsAppConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setWhatsAppConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function WhatsAppAuthorization({ whatsAppConf, setWhatsAppConf, step, setstep, isInfo }) { return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Phone number ID:', 'bit-integrations')} -
    - - -
    - {__('WhatsApp Business Account ID:', 'bit-integrations')} -
    - - -
    - {__('Access Token:', 'bit-integrations')} -
    - -
    {error.clientId}
    - -
    {error.clientSecret}
    - {!isInfo && ( - <> - -
    - - - )} -
    + ) } + +const note = `

    ${__('WhatsApp Cloud API setup', 'bit-integrations')}

    +
      +
    • ${__('Provide your WhatsApp Business Account ID.', 'bit-integrations')}
    • +
    • ${__('Provide your Phone Number ID.', 'bit-integrations')}
    • +
    • ${__('Paste a valid long-lived access token.', 'bit-integrations')}
    • +
    ` diff --git a/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppCommonFunc.js b/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppCommonFunc.js index 94d4dbe91..5fe50200e 100644 --- a/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppCommonFunc.js +++ b/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppCommonFunc.js @@ -13,64 +13,22 @@ export const handleInput = (e, whatsAppConf, setWhatsAppConf) => { setWhatsAppConf({ ...newConf }) } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setIsAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.numberID || !confTmp.businessAccountID || !confTmp.token) { - setError({ - numberID: !confTmp.numberID - ? __("Phone number ID can't be empty or invalid", 'bit-integrations') - : '', - businessAccountID: !confTmp.businessAccountID - ? __("WhatsApp Business Account ID can't be empty or invalid", 'bit-integrations') - : '', - token: !confTmp.token ? __("Access Token can't be empty or invalid", 'bit-integrations') : '' - }) - return - } - - const requestParams = { - numberID: confTmp.numberID, - businessAccountID: confTmp.businessAccountID, - token: confTmp.token - } - - setIsLoading(true) - bitsFetch(requestParams, 'whats_app_authorization').then(result => { - setIsLoading(false) - if (result && result.success) { - setIsAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - return - } - setSnackbar({ show: true, msg: result?.data || __('Authorized failed', 'bit-integrations') }) - }) -} - export const getallTemplates = (confTmp, setConf, setIsLoading, setSnackbar) => { - if (!confTmp.numberID || !confTmp.businessAccountID || !confTmp.token) { - setError({ - numberID: !confTmp.numberID - ? __("Phone number ID can't be empty or invalid", 'bit-integrations') - : '', - businessAccountID: !confTmp.businessAccountID - ? __("WhatsApp Business Account ID can't be empty or invalid", 'bit-integrations') - : '', - token: !confTmp.token ? __("Access Token can't be empty or invalid", 'bit-integrations') : '' + if (!confTmp.connection_id && (!confTmp.numberID || !confTmp.businessAccountID || !confTmp.token)) { + setSnackbar({ + show: true, + msg: __('Phone number ID, Business Account ID and Access Token are required.', 'bit-integrations') }) return } - const requestParams = { - numberID: confTmp.numberID, - businessAccountID: confTmp.businessAccountID, - token: confTmp.token - } + const requestParams = confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + numberID: confTmp.numberID, + businessAccountID: confTmp.businessAccountID, + token: confTmp.token + } setIsLoading(true) bitsFetch(requestParams, 'whats_app_all_template').then(result => { diff --git a/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppIntegLayout.jsx b/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppIntegLayout.jsx index bfd9941be..3469f1a22 100644 --- a/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppIntegLayout.jsx +++ b/frontend/src/components/AllIntegrations/WhatsApp/WhatsAppIntegLayout.jsx @@ -13,7 +13,6 @@ import { checkIsPro, getProLabel } from '../../Utilities/ProUtilHelpers' export default function WhatsAppIntegLayout({ formFields, - handleInput, whatsAppConf, setWhatsAppConf, isLoading, diff --git a/frontend/src/components/AllIntegrations/WishlistMember/WishlistMemberAuthorization.jsx b/frontend/src/components/AllIntegrations/WishlistMember/WishlistMemberAuthorization.jsx index 0c6d411c1..7d03e1389 100644 --- a/frontend/src/components/AllIntegrations/WishlistMember/WishlistMemberAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/WishlistMember/WishlistMemberAuthorization.jsx @@ -1,57 +1,42 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' -import { handleAuthorize, setIntegrationName } from './WishlistMemberCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function WishlistMemberAuthorization({ wishlistMemberConf, setWishlistMemberConf, step, nextPage, - setSnackbar, isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [isLoading, setIsLoading] = useState(false) + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - setIntegrationName(e, setWishlistMemberConf)} - name="name" - value={wishlistMemberConf.name} - type="text" - placeholder={__('Integration Name...', 'bit-integrations')} - disabled={isInfo} - /> - - - -
    - -
    + ) } diff --git a/frontend/src/components/AllIntegrations/WishlistMember/WishlistMemberCommonFunc.js b/frontend/src/components/AllIntegrations/WishlistMember/WishlistMemberCommonFunc.js index 801ccc4e4..20942dad4 100644 --- a/frontend/src/components/AllIntegrations/WishlistMember/WishlistMemberCommonFunc.js +++ b/frontend/src/components/AllIntegrations/WishlistMember/WishlistMemberCommonFunc.js @@ -2,25 +2,6 @@ import { create } from 'mutative' import bitsFetch from '../../../Utils/bitsFetch' import { __ } from '../../../Utils/i18nwrap' -export const handleAuthorize = (setIsAuthorized, setIsLoading, setSnackbar) => { - setIsLoading(true) - - bitsFetch(null, 'wishlist_authorization').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else { - setIsAuthorized(false) - setSnackbar({ - show: true, - msg: result?.data || __('Authorization Failed, Please try again', 'bit-integrations') - }) - } - - setIsLoading(false) - }) -} - export const generateMappedField = wishlistFields => { const requiredFields = wishlistFields.filter(fld => fld?.required === true) diff --git a/frontend/src/components/AllIntegrations/WooCommerce/WooCommerceAuthorization.jsx b/frontend/src/components/AllIntegrations/WooCommerce/WooCommerceAuthorization.jsx index 2106e9c93..f2f0a3b0b 100644 --- a/frontend/src/components/AllIntegrations/WooCommerce/WooCommerceAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/WooCommerce/WooCommerceAuthorization.jsx @@ -1,102 +1,32 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' -import { deepCopy } from '../../../Utils/Helpers' -import { __, sprintf } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function WooCommerceAuthorization({ - formID, - wcConf, - setWcConf, - step, - setStep, - setSnackbar -}) { - const [isAuthorized, setisAuthorized] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'wc_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with WooCommerce Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = deepCopy(wcConf) - newConf[e.target.name] = e.target.value - setWcConf(newConf) - } +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' +import { __ } from '../../../Utils/i18nwrap' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +export default function WooCommerceAuthorization({ wcConf, setWcConf, step, setStep, isInfo }) { return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - {isLoading === 'auth' && ( -
    - - Checking if WooCommerce is active!!! -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {sprintf( - __('%s plugin must be activated to integrate with Bit Integrations', 'bit-integrations'), - 'WooCommerce' - )} -
    - )} - - {!isAuthorized && ( - - )} - - {isAuthorized && ( - - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Woodpecker/WoodpeckerAuthorization.jsx b/frontend/src/components/AllIntegrations/Woodpecker/WoodpeckerAuthorization.jsx index 40abf686d..6373a5461 100644 --- a/frontend/src/components/AllIntegrations/Woodpecker/WoodpeckerAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Woodpecker/WoodpeckerAuthorization.jsx @@ -1,11 +1,9 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' -import { woodpeckerAuthentication } from './WoodpeckerCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import Authorization from '../../Connections/Authorization' +import { getAllCampaign } from './WoodpeckerCommonFunc' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' export default function WoodpeckerAuthorization({ woodpeckerConf, @@ -14,27 +12,16 @@ export default function WoodpeckerAuthorization({ setStep, loading, setLoading, - isInfo + setSnackbar, + isInfo = false }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_token: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !woodpeckerConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...woodpeckerConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setWoodpeckerConf(newConf) - } + const loadCampaigns = useCallback( + connectionId => { + const nextConf = connectionId ? { ...woodpeckerConf, connection_id: connectionId } : woodpeckerConf + getAllCampaign(nextConf, setWoodpeckerConf, loading, setLoading, setSnackbar) + }, + [loading, setLoading, setSnackbar, setWoodpeckerConf, woodpeckerConf] + ) const ActiveInstructions = `

    ${__('Get API Key', 'bit-integrations')}

    @@ -54,75 +41,27 @@ export default function WoodpeckerAuthorization({ ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('API Key:', 'bit-integrations')} -
    - -
    {error.api_key}
    - - - {__('To get API key, please visit', 'bit-integrations')} -   - - {__('Woodpecker API Key', 'bit-integrations')} - - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} - -
    + ({ + Authorization: `Basic ${btoa(data?.api_key || '')}`, + 'Content-type': 'application/json' + }) + }} + noteDetails={{ note: ActiveInstructions }} + onConnectionSelected={loadCampaigns} + /> ) } diff --git a/frontend/src/components/AllIntegrations/Woodpecker/WoodpeckerCommonFunc.js b/frontend/src/components/AllIntegrations/Woodpecker/WoodpeckerCommonFunc.js index 3636df4dc..a829d8c63 100644 --- a/frontend/src/components/AllIntegrations/Woodpecker/WoodpeckerCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Woodpecker/WoodpeckerCommonFunc.js @@ -45,38 +45,18 @@ export const checkMappedFields = woodpeckerConf => { return true } -export const woodpeckerAuthentication = (confTmp, setError, setIsAuthorized, loading, setLoading) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { - api_key: confTmp.api_key - } - - bitsFetch(requestParams, 'woodpecker_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid API Key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = conf => + conf?.connection_id ? { connection_id: conf.connection_id } : { api_key: conf.api_key } -export const getAllCampaign = (woodpeckerConf, setWoodpeckerConf, loading, setLoading) => { +export const getAllCampaign = ( + woodpeckerConf, + setWoodpeckerConf, + loading, + setLoading, + setSnackbar = null +) => { setLoading({ ...loading, campaign: true }) - const requestParams = { - api_key: woodpeckerConf.api_key - } + const requestParams = buildAuthRequestParams(woodpeckerConf) bitsFetch(requestParams, 'woodpecker_fetch_all_campaigns').then(result => { if (result && result.success) { @@ -87,10 +67,16 @@ export const getAllCampaign = (woodpeckerConf, setWoodpeckerConf, loading, setLo }) toast.success(__('Campaigns fetched successfully', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Campaigns fetched successfully', 'bit-integrations') }) + } setLoading({ ...loading, campaign: false }) return } setLoading({ ...loading, campaign: false }) toast.error(__('Campaigns fetching failed', 'bit-integrations')) + if (typeof setSnackbar === 'function') { + setSnackbar({ show: true, msg: __('Campaigns fetching failed', 'bit-integrations') }) + } }) } diff --git a/frontend/src/components/AllIntegrations/WpErp/WpErpAuthorization.jsx b/frontend/src/components/AllIntegrations/WpErp/WpErpAuthorization.jsx index 16d8c914d..6595863f6 100644 --- a/frontend/src/components/AllIntegrations/WpErp/WpErpAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/WpErp/WpErpAuthorization.jsx @@ -1,110 +1,27 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' - -export default function WpErpAuthorization({ - wpErpConf, - setWpErpConf, - step, - nextPage, - isLoading, - setIsLoading, - setSnackbar, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [showAuthMsg, setShowAuthMsg] = useState(false) - - const authorizeHandler = () => { - setIsLoading('auth') - bitsFetch({}, 'wp_erp_authorize').then(result => { - if (result?.success) { - setIsAuthorized(true) - setSnackbar({ - show: true, - msg: __('Connected with WP ERP Successfully', 'bit-integrations') - }) - } - setIsLoading(false) - setShowAuthMsg(true) - }) - } - - const handleInput = e => { - const newConf = { ...wpErpConf } - newConf[e.target.name] = e.target.value - setWpErpConf(newConf) - } +import Authorization from '../../Connections/Authorization' +export default function WpErpAuthorization({ wpErpConf, setWpErpConf, step, nextPage, isInfo }) { + const setStep = useCallback(value => nextPage(value), [nextPage]) return ( -
    -
    - {__('Integration Name:', 'bit-integrations')} -
    - - - {isLoading === 'auth' && ( -
    - - {__('Checking if WP ERP is authorized!!!', 'bit-integrations')} -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    -
    -
    - -
    -
    - {__('WP ERP is not activated or not installed', 'bit-integrations')} -
    -
    -
    - )} - - {showAuthMsg && isAuthorized && !isLoading && ( -
    -
    - -
    -
    {__('WP ERP is activated', 'bit-integrations')}
    -
    - )} - - {!isInfo && ( - <> - -
    - - - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/ZagoMail/ZagoMailAuthorization.jsx b/frontend/src/components/AllIntegrations/ZagoMail/ZagoMailAuthorization.jsx index 8bbcdd41d..4f0c248e2 100644 --- a/frontend/src/components/AllIntegrations/ZagoMail/ZagoMailAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ZagoMail/ZagoMailAuthorization.jsx @@ -1,162 +1,71 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' -import bitsFetch from '../../../Utils/bitsFetch' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import Note from '../../Utilities/Note' +import Authorization from '../../Connections/Authorization' import { refreshZagoMailList } from './ZagoMailCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' export default function ZagoMailAuthorization({ - formID, zagoMailConf, setZagoMailConf, step, setstep, setSnackbar, - isInfo, - isLoading, - setIsLoading + isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ name: '', api_public_key: '' }) - const [showAuthMsg, setShowAuthMsg] = useState(false) + const loadLists = useCallback( + connectionId => { + const nextConf = connectionId ? { ...zagoMailConf, connection_id: connectionId } : zagoMailConf + refreshZagoMailList(nextConf, setZagoMailConf, () => {}, setSnackbar) + }, + [setSnackbar, setZagoMailConf, zagoMailConf] + ) - const handleAuthorize = () => { - const newConf = { ...zagoMailConf } - if (!newConf.name || !newConf.api_public_key) { - setError({ - name: !newConf.name ? __("Integration name can't be empty", 'bit-integrations') : '', - api_public_key: !newConf.api_public_key - ? __("API Public Key can't be empty", 'bit-integrations') - : '' - }) - return - } - setIsLoading('auth') - const data = { - api_public_key: newConf.api_public_key - } - bitsFetch(data, 'zagoMail_authorize').then(result => { - if (result?.success) { - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) + const handleSetStep = useCallback( + value => { + if (value === 2 && !zagoMailConf?.default?.zagoMailLists) { + loadLists() } - setShowAuthMsg(true) - setIsLoading(false) - }) - } - const handleInput = e => { - const newConf = { ...zagoMailConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setZagoMailConf(newConf) - } - - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - refreshZagoMailList(zagoMailConf, setZagoMailConf, setIsLoading, setSnackbar) - setstep(2) - } - - const ActiveInstructions = ` -

    ${__('Get API Public Key', 'bit-integrations')}

    -
      -
    • ${__('First go to your ZagoMail dashboard.', 'bit-integrations')}
    • -
    • ${__('Click on the top top right corner', 'bit-integrations')}
    • -
    • ${__('Then click on API', 'bit-integrations')}
    • -
    ` - - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - -
    {error.name}
    - -
    - {__('Access API Public Key Key:', 'bit-integrations')} -
    - -
    {error.api_public_key}
    + setstep(value) + }, + [loadLists, setstep, zagoMailConf] + ) - - {__('To Get API Public Key Key, Please Visit', 'bit-integrations')} -   + const note = ` +

    ${__('Get API Public Key', 'bit-integrations')}

    +
      +
    • ${__('First go to your ZagoMail dashboard.', 'bit-integrations')}
    • +
    • ${__('Click on the top top right corner', 'bit-integrations')}
    • +
    • ${__('Then click on API', 'bit-integrations')}
    • +
    + + ${__('To get API Public Key, please visit', 'bit-integrations')} - {__('ZagoMail API Token', 'bit-integrations')} + ${__(' ZagoMail API Token', 'bit-integrations')} - -
    -
    +
    ` - {isLoading === 'auth' && ( -
    - - {__('Checking API Public Key Key!!!', 'bit-integrations')} -
    - )} - - {showAuthMsg && !isAuthorized && !isLoading && ( -
    - - × - - {__('Sorry, API Public Key key is invalid', 'bit-integrations')} -
    - )} - {!isInfo && ( - <> - -
    - - - )} - -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/ZagoMail/ZagoMailCommonFunc.js b/frontend/src/components/AllIntegrations/ZagoMail/ZagoMailCommonFunc.js index 71433d2e9..f641489c0 100644 --- a/frontend/src/components/AllIntegrations/ZagoMail/ZagoMailCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ZagoMail/ZagoMailCommonFunc.js @@ -8,11 +8,12 @@ export const handleInput = (e, zagoMailConf, setZagoMailConf) => { setZagoMailConf({ ...newConf }) } +const buildAuthRequestParams = conf => + conf.connection_id ? { connection_id: conf.connection_id } : { api_public_key: conf.api_public_key } + export const refreshZagoMailList = (zagoMailConf, setZagoMailConf, setIsLoading, setSnackbar) => { - setIsLoading(true) - const refreshListsRequestParams = { - api_public_key: zagoMailConf.api_public_key - } + if (typeof setIsLoading === 'function') setIsLoading(true) + const refreshListsRequestParams = buildAuthRequestParams(zagoMailConf) bitsFetch(refreshListsRequestParams, 'zagoMail_lists') .then(result => { if (result && result.success) { @@ -22,12 +23,12 @@ export const refreshZagoMailList = (zagoMailConf, setZagoMailConf, setIsLoading, newConf.default = {} } newConf.default.zagoMailLists = result.data.zagoMailLists - setSnackbar({ + setSnackbar?.({ show: true, msg: __('ZagoMail lists refreshed', 'bit-integrations') }) } else { - setSnackbar({ + setSnackbar?.({ show: true, msg: __( 'No ZagoMail lists found. Try changing the header row number or try again', @@ -38,21 +39,21 @@ export const refreshZagoMailList = (zagoMailConf, setZagoMailConf, setIsLoading, setZagoMailConf({ ...newConf }) } else { - setSnackbar({ + setSnackbar?.({ show: true, msg: __('ZagoMail lists refresh failed. please try again', 'bit-integrations') }) } - setIsLoading(false) + if (typeof setIsLoading === 'function') setIsLoading(false) + }) + .catch(() => { + if (typeof setIsLoading === 'function') setIsLoading(false) }) - .catch(() => setIsLoading(false)) } // refreshZagoMailTags export const refreshZagoMailTags = (zagoMailConf, setZagoMailConf, setIsLoading, setSnackbar) => { - setIsLoading({ tags: true }) - const refreshListsRequestParams = { - api_public_key: zagoMailConf.api_public_key - } + if (typeof setIsLoading === 'function') setIsLoading({ tags: true }) + const refreshListsRequestParams = buildAuthRequestParams(zagoMailConf) bitsFetch(refreshListsRequestParams, 'zagoMail_tags') .then(result => { if (result && result.success) { @@ -63,12 +64,12 @@ export const refreshZagoMailTags = (zagoMailConf, setZagoMailConf, setIsLoading, } newConf.tags = result.data.zagoMailTags setZagoMailConf({ ...newConf }) - setSnackbar({ + setSnackbar?.({ show: true, msg: __('ZagoMail tags refreshed', 'bit-integrations') }) } else { - setSnackbar({ + setSnackbar?.({ show: true, msg: __( 'No ZagoMail tags found. Try changing the header row number or try again', @@ -79,20 +80,22 @@ export const refreshZagoMailTags = (zagoMailConf, setZagoMailConf, setIsLoading, // console.log('newConf', newConf) } else { - setSnackbar({ + setSnackbar?.({ show: true, msg: __('ZagoMail tags refresh failed. please try again', 'bit-integrations') }) } - setIsLoading(false) + if (typeof setIsLoading === 'function') setIsLoading(false) + }) + .catch(() => { + if (typeof setIsLoading === 'function') setIsLoading(false) }) - .catch(() => setIsLoading(false)) } // refreshMappedFields export const refreshZagoMailFields = (zagoMailConf, setZagoMailConf, setIsLoading, setSnackbar) => { - setIsLoading(true) + if (typeof setIsLoading === 'function') setIsLoading(true) const refreshListsRequestParams = { - api_public_key: zagoMailConf.api_public_key, + ...buildAuthRequestParams(zagoMailConf), listId: zagoMailConf.listId } // console.log('zagoMailConf', zagoMailConf) @@ -113,12 +116,12 @@ export const refreshZagoMailFields = (zagoMailConf, setZagoMailConf, setIsLoadin zagoMailField: f.fieldId, required: true })) - setSnackbar({ + setSnackbar?.({ show: true, msg: __('ZagoMail fields refreshed', 'bit-integrations') }) } else { - setSnackbar({ + setSnackbar?.({ show: true, msg: __( 'No ZagoMail fields found. Try changing the header row number or try again', @@ -129,14 +132,16 @@ export const refreshZagoMailFields = (zagoMailConf, setZagoMailConf, setIsLoadin setZagoMailConf({ ...newConf }) } else { - setSnackbar({ + setSnackbar?.({ show: true, msg: __('ZagoMail fields refresh failed. please try again', 'bit-integrations') }) } - setIsLoading(false) + if (typeof setIsLoading === 'function') setIsLoading(false) + }) + .catch(() => { + if (typeof setIsLoading === 'function') setIsLoading(false) }) - .catch(() => setIsLoading(false)) } export const checkMappedFields = zagoMailConf => { diff --git a/frontend/src/components/AllIntegrations/Zendesk/ZendeskAuthorization.jsx b/frontend/src/components/AllIntegrations/Zendesk/ZendeskAuthorization.jsx index 11b3c4ed5..697dadf4e 100644 --- a/frontend/src/components/AllIntegrations/Zendesk/ZendeskAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/Zendesk/ZendeskAuthorization.jsx @@ -1,117 +1,32 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import { zendeskAuthentication } from './ZendeskCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' - -export default function ZendeskAuthorization({ - zendeskConf, - setZendeskConf, - step, - setStep, - loading, - setLoading, - isInfo -}) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ api_key: '' }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - !zendeskConf?.default - setStep(2) - } - - const handleInput = e => { - const newConf = { ...zendeskConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setZendeskConf(newConf) - } +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' + +export default function ZendeskAuthorization({ zendeskConf, setZendeskConf, step, setStep, isInfo }) { + const note = ` + + ${__('To Get API Token, Please Visit', 'bit-integrations')} + + ${__('Zendesk API Token', 'bit-integrations')} + + ` return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('API Key:', 'bit-integrations')} -
    - -
    {error.api_key}
    - - - {__('To Get API Token, Please Visit', 'bit-integrations')} -   - - {__('Zendesk API Token', 'bit-integrations')} - - -
    -
    - - {!isInfo && ( -
    - -
    - -
    - )} -
    + ) } diff --git a/frontend/src/components/AllIntegrations/Zendesk/ZendeskCommonFunc.js b/frontend/src/components/AllIntegrations/Zendesk/ZendeskCommonFunc.js index 4f42c6a4d..58bd69406 100644 --- a/frontend/src/components/AllIntegrations/Zendesk/ZendeskCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Zendesk/ZendeskCommonFunc.js @@ -51,43 +51,18 @@ export const checkMappedFields = zendeskConf => { return true } -export const zendeskAuthentication = ( - confTmp, - setConf, - setError, - setIsAuthorized, - loading, - setLoading -) => { - if (!confTmp.api_key) { - setError({ - api_key: !confTmp.api_key ? __("API Key can't be empty", 'bit-integrations') : '' - }) - return - } - - setError({}) - setLoading({ ...loading, auth: true }) - - const requestParams = { api_key: confTmp.api_key } - - bitsFetch(requestParams, 'zendesk_authentication').then(result => { - if (result && result.success) { - setIsAuthorized(true) - setLoading({ ...loading, auth: false }) - toast.success(__('Authorized Successfully', 'bit-integrations')) - return - } - setLoading({ ...loading, auth: false }) - toast.error(__('Authorized failed, Please enter valid API key', 'bit-integrations')) - }) -} +const buildAuthRequestParams = confTmp => + confTmp.connection_id + ? { connection_id: confTmp.connection_id } + : { + api_key: confTmp.api_key + } export const getCustomFields = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, customFields: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action: confTmp.actionName } @@ -114,7 +89,7 @@ export const getCustomFields = (confTmp, setConf, setLoading) => { export const getAllLeads = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, leads: true }) - const requestParams = { api_key: confTmp.api_key } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'zendesk_fetch_all_leads').then(result => { if (result && result.success) { @@ -136,7 +111,7 @@ export const getAllLeads = (confTmp, setConf, setLoading) => { export const getAllParentOrganizations = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, parentOrganizations: true }) - const requestParams = { api_key: confTmp.api_key } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'zendesk_fetch_all_parentOrganizations').then(result => { if (result && result.success) { @@ -158,7 +133,7 @@ export const getAllParentOrganizations = (confTmp, setConf, setLoading) => { export const getAllTeams = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, teams: true }) - const requestParams = { api_key: confTmp.api_key } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'zendesk_fetch_all_teams').then(result => { if (result && result.success) { @@ -180,9 +155,7 @@ export const getAllTeams = (confTmp, setConf, setLoading) => { export const getAllCurrencies = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, currencies: true }) - const requestParams = { - api_key: confTmp.api_key - } + const requestParams = buildAuthRequestParams(confTmp) bitsFetch(requestParams, 'zendesk_fetch_all_currencies').then(result => { if (result && result.success) { @@ -205,7 +178,7 @@ export const getAllStages = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, stages: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -230,7 +203,7 @@ export const getAllCRMCompanies = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMCompanies: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -263,7 +236,7 @@ export const getAllCRMContacts = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMContacts: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } @@ -288,7 +261,7 @@ export const getAllCRMSources = (confTmp, setConf, setLoading) => { setLoading({ ...setLoading, CRMSources: true }) const requestParams = { - api_key: confTmp.api_key, + ...buildAuthRequestParams(confTmp), action_name: confTmp.actionName } diff --git a/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalytics.jsx b/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalytics.jsx index 93237a4e5..3d869ee52 100644 --- a/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalytics.jsx +++ b/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalytics.jsx @@ -7,7 +7,7 @@ import Steps from '../../Utilities/Steps' import { saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import ZohoAnalyticsAuthorization from './ZohoAnalyticsAuthorization' -import { handleInput, setGrantTokenResponse } from './ZohoAnalyticsCommonFunc' +import { handleInput } from './ZohoAnalyticsCommonFunc' import ZohoAnalyticsIntegLayout from './ZohoAnalyticsIntegLayout' export default function ZohoAnalytics({ formFields, setFlow, flow, allIntegURL }) { @@ -23,10 +23,6 @@ export default function ZohoAnalytics({ formFields, setFlow, flow, allIntegURL } actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse() - }, []) - const nextPage = () => { setTimeout(() => { document.getElementById('btcd-settings-wrp').scrollTop = 0 diff --git a/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalyticsAuthorization.jsx b/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalyticsAuthorization.jsx index 6e99a4615..10bd4c0b0 100644 --- a/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalyticsAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalyticsAuthorization.jsx @@ -1,11 +1,9 @@ -import { useState } from 'react' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import { checkValidEmail } from '../../../Utils/Helpers' -import CopyText from '../../Utilities/CopyText' -import LoaderSm from '../../Loaders/LoaderSm' -import { handleAuthorize, refreshWorkspaces } from './ZohoAnalyticsCommonFunc' -import BackIcn from '../../../Icons/BackIcn' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { refreshWorkspaces } from './ZohoAnalyticsCommonFunc' export default function ZohoAnalyticsAuthorization({ formID, @@ -13,181 +11,91 @@ export default function ZohoAnalyticsAuthorization({ setAnalyticsConf, step, setStep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ - dataCenter: '', - clientId: '', - clientSecret: '', - ownerEmail: '' - }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - if (!checkValidEmail(analyticsConf.ownerEmail)) { - setError({ - ownerEmail: !checkValidEmail(analyticsConf.ownerEmail) - ? __('Email is invalid', 'bit-integrations') - : '' - }) - return - } - setStep(2) - refreshWorkspaces(formID, analyticsConf, setAnalyticsConf, setIsLoading, setSnackbar) - } + const dataCenterOptions = [ + { value: 'com', label: 'zoho.com' }, + { value: 'eu', label: 'zoho.eu' }, + { value: 'com.cn', label: 'zoho.com.cn' }, + { value: 'in', label: 'zoho.in' }, + { value: 'com.au', label: 'zoho.com.au' } + ] - const handleInput = e => { - const newConf = { ...analyticsConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setAnalyticsConf(newConf) - } + const scopes = + 'ZohoAnalytics.metadata.read,ZohoAnalytics.data.read,ZohoAnalytics.data.create,ZohoAnalytics.data.update,ZohoAnalytics.usermanagement.read,ZohoAnalytics.share.create' - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Data Center:', 'bit-integrations')} -
    - -
    {error.dataCenter}
    - -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - - - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Zoho API Console', 'bit-integrations')} - - + const loadWorkspaces = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...analyticsConf, connection_id: connectionId } : analyticsConf + refreshWorkspaces(formID, nextConf, setAnalyticsConf, setIsLoading, setSnackbar) + }, + [analyticsConf, formID, setAnalyticsConf, setIsLoading, setSnackbar] + ) -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    + const handleSetStep = useCallback( + value => { + if (value === 2 && !analyticsConf?.default?.workspaces) { + loadWorkspaces() + } + setStep(value) + }, + [analyticsConf?.default?.workspaces, loadWorkspaces, setStep] + ) -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    -
    - {__('Zoho Analytics Owner Email:', 'bit-integrations')} -
    - -
    {error.ownerEmail}
    + const note = `

    ${__('Zoho Analytics OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create app in Zoho API Console.', 'bit-integrations')}
    • +
    • ${__('Add workspace owner email in connection form.', 'bit-integrations')}
    • +
    • ${__('Use callback URL shown in connection form.', 'bit-integrations')}
    • +
    ` - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalyticsCommonFunc.js b/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalyticsCommonFunc.js index 2249317fd..847d640df 100644 --- a/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalyticsCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ZohoAnalytics/ZohoAnalyticsCommonFunc.js @@ -1,127 +1,16 @@ import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' -import { checkValidEmail } from '../../../Utils/Helpers' -export const setGrantTokenResponse = () => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation - .replace(`${window.opener.location.href}/redirect`, '') - .split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails } - }) - } - localStorage.setItem('__zohoAnalytics', JSON.stringify(grantTokenResponse)) - window.close() -} -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.dataCenter || !confTmp.clientId || !confTmp.clientSecret) { - setError({ - dataCenter: !confTmp.dataCenter ? __("Data center can't be empty", 'bit-integrations') : '', - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty", 'bit-integrations') : '' - }) - return - } - if (!checkValidEmail(confTmp.ownerEmail)) { - setError({ - ownerEmail: !checkValidEmail(confTmp.ownerEmail) ? __('Email is invalid', 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - const scopes = - 'ZohoAnalytics.metadata.read,ZohoAnalytics.data.read,ZohoAnalytics.data.create,ZohoAnalytics.data.update,ZohoAnalytics.usermanagement.read,ZohoAnalytics.share.create' - const apiEndpoint = `https://accounts.zoho.${ - confTmp.dataCenter - }/oauth/v2/auth?scope=${scopes}&response_type=code&client_id=${ - confTmp.clientId - }&prompt=Consent&access_type=offline&redirect_uri=${encodeURIComponent(window.location.href)}/redirect` - const authWindow = window.open(apiEndpoint, 'zohoAnalytics', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitformsZoho = localStorage.getItem('__zohoAnalytics') - if (bitformsZoho) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitformsZoho) - localStorage.removeItem('__zohoAnalytics') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - grantTokenResponse['accounts-server'] = decodeURIComponent(grantTokenResponse['accounts-server']) - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper(grantTokenResponse, newConf, setConf, setisAuthorized, setIsLoading, setSnackbar) - } - } - }, 500) -} - -const tokenHelper = (grantToken, confTmp, setConf, setisAuthorized, setIsLoading, setSnackbar) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.dataCenter = confTmp.dataCenter - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - tokenRequestParams.redirectURI = `${encodeURIComponent(window.location.href)}/redirect` - bitsFetch(tokenRequestParams, 'zanalytics_generate_token') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} +import { checkValidEmail } from '../../../Utils/Helpers' export const handleInput = (e, analyticsConf, setAnalyticsConf, formID, setIsLoading, setSnackbar) => { let newConf = { ...analyticsConf } @@ -185,6 +74,7 @@ export const refreshWorkspaces = ( const refreshModulesRequestParams = { formID, id: analyticsConf.id, + ...buildAuthRequestParams(analyticsConf), dataCenter: analyticsConf.dataCenter, clientId: analyticsConf.clientId, clientSecret: analyticsConf.clientSecret, @@ -232,10 +122,8 @@ export const refreshUsers = (formID, analyticsConf, setAnalyticsConf, setIsLoadi const refreshUsersRequestParams = { formID, id: analyticsConf.id, + ...buildAuthRequestParams(analyticsConf), dataCenter: analyticsConf.dataCenter, - clientId: analyticsConf.clientId, - clientSecret: analyticsConf.clientSecret, - tokenDetails: analyticsConf.tokenDetails, ownerEmail: analyticsConf.ownerEmail } bitsFetch(refreshUsersRequestParams, 'zanalytics_refresh_users') @@ -284,10 +172,8 @@ export const refreshTables = (formID, analyticsConf, setAnalyticsConf, setIsLoad const refreshTablesRequestParams = { formID, workspace, + ...buildAuthRequestParams(analyticsConf), dataCenter: analyticsConf.dataCenter, - clientId: analyticsConf.clientId, - clientSecret: analyticsConf.clientSecret, - tokenDetails: analyticsConf.tokenDetails, ownerEmail: analyticsConf.ownerEmail } bitsFetch(refreshTablesRequestParams, 'zanalytics_refresh_tables') @@ -334,10 +220,8 @@ export const refreshTableHeaders = ( formID, workspace, table, + ...buildAuthRequestParams(analyticsConf), dataCenter: analyticsConf.dataCenter, - clientId: analyticsConf.clientId, - clientSecret: analyticsConf.clientSecret, - tokenDetails: analyticsConf.tokenDetails, ownerEmail: analyticsConf.ownerEmail } bitsFetch(refreshTableHeadersRequestParams, 'zanalytics_refresh_table_headers') diff --git a/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBigin.jsx b/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBigin.jsx index b62fd30db..873c2c76c 100644 --- a/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBigin.jsx +++ b/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBigin.jsx @@ -5,7 +5,7 @@ import { __ } from '../../../Utils/i18nwrap' import 'react-multiple-select-dropdown-lite/dist/index.css' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { saveActionConf, setGrantTokenResponse } from '../IntegrationHelpers/IntegrationHelpers' +import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import { checkMappedFields, handleInput } from './ZohoBiginCommonFunc' import ZohoBiginIntegLayout from './ZohoBiginIntegLayout' @@ -25,7 +25,6 @@ function ZohoBigin({ allIntegURL }) { const formFields = useRecoilValue($formFields) useEffect(() => { - window.opener && setGrantTokenResponse('zohoBigin') setBiginConf({ name: 'Zoho Bigin', type: 'Zoho Bigin', diff --git a/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBiginAuthorization.jsx b/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBiginAuthorization.jsx index 40e0849c2..26d95da68 100644 --- a/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBiginAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBiginAuthorization.jsx @@ -1,13 +1,9 @@ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' -import { $appConfigState } from '../../../GlobalStates' -import BackIcn from '../../../Icons/BackIcn' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { handleAuthorize } from '../IntegrationHelpers/IntegrationHelpers' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' import { refreshModules } from './ZohoBiginCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' export default function ZohoBiginAuthorization({ formID, @@ -15,164 +11,84 @@ export default function ZohoBiginAuthorization({ setBiginConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '', clientSecret: '' }) - const btcbi = useRecoilValue($appConfigState) + const dataCenterOptions = [ + { value: 'com', label: 'zoho.com' }, + { value: 'eu', label: 'zoho.eu' }, + { value: 'com.cn', label: 'zoho.com.cn' }, + { value: 'in', label: 'zoho.in' }, + { value: 'com.au', label: 'zoho.com.au' } + ] + const scopes = 'ZohoBigin.settings.modules.READ,ZohoBigin.settings.fields.READ,ZohoBigin.settings.tags.READ,ZohoBigin.users.READ,ZohoBigin.modules.ALL,ZohoBigin.settings.layouts.READ' - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - refreshModules(formID, biginConf, setBiginConf, setIsLoading, setSnackbar) - } - - const handleInput = e => { - const newConf = { ...biginConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setBiginConf(newConf) - } - - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Data Center:', 'bit-integrations')} -
    - -
    {error.dataCenter}
    -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - - - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Zoho API Console', 'bit-integrations')} - - + const loadModules = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...biginConf, connection_id: connectionId } : biginConf + refreshModules(formID, nextConf, setBiginConf, setIsLoading, setSnackbar) + }, + [biginConf, formID, setBiginConf, setIsLoading, setSnackbar] + ) -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    + const handleSetStep = useCallback( + value => { + if (value === 2) { + loadModules() + } + setstep(value) + }, + [loadModules, setstep] + ) -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    + const note = `

    ${__('Zoho Bigin OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create app in Zoho API Console.', 'bit-integrations')}
    • +
    • ${__('Choose right data center for your account.', 'bit-integrations')}
    • +
    • ${__('Use callback URL shown in connection form.', 'bit-integrations')}
    • +
    ` - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBiginCommonFunc.js b/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBiginCommonFunc.js index 4275adc4a..de6877259 100644 --- a/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBiginCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ZohoBigin/ZohoBiginCommonFunc.js @@ -1,6 +1,15 @@ import { __, sprintf } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const handleInput = ( e, recordTab, @@ -95,10 +104,8 @@ export const refreshModules = (formID, biginConf, setBiginConf, setIsLoading, se const refreshModulesRequestParams = { formID, id: biginConf.id, - dataCenter: biginConf.dataCenter, - clientId: biginConf.clientId, - clientSecret: biginConf.clientSecret, - tokenDetails: biginConf.tokenDetails + ...buildAuthRequestParams(biginConf), + dataCenter: biginConf.dataCenter } bitsFetch(refreshModulesRequestParams, 'zbigin_refresh_modules') .then(result => { @@ -142,10 +149,8 @@ export const refreshPipelinesLayout = (formID, biginConf, setBiginConf, setIsLoa const refreshLayoutRequestParams = { formID, id: biginConf.id, - dataCenter: biginConf.dataCenter, - clientId: biginConf.clientId, - clientSecret: biginConf.clientSecret, - tokenDetails: biginConf.tokenDetails + ...buildAuthRequestParams(biginConf), + dataCenter: biginConf.dataCenter } bitsFetch(refreshLayoutRequestParams, 'zbigin_refresh_playouts') .then(result => { @@ -192,10 +197,8 @@ export const refreshRelatedList = (formID, biginConf, setBiginConf, setIsLoading const relatedListRequestParams = { formID, module: biginConf.module, - dataCenter: biginConf.dataCenter, - clientId: biginConf.clientId, - clientSecret: biginConf.clientSecret, - tokenDetails: biginConf.tokenDetails + ...buildAuthRequestParams(biginConf), + dataCenter: biginConf.dataCenter } bitsFetch(relatedListRequestParams, 'zbigin_refresh_related_lists') .then(result => { @@ -240,10 +243,8 @@ export const getFields = (recordTab, formID, biginConf, setBiginConf, setIsLoadi const getFieldsRequestParams = { formID, module, - dataCenter: biginConf.dataCenter, - clientId: biginConf.clientId, - clientSecret: biginConf.clientSecret, - tokenDetails: biginConf.tokenDetails + ...buildAuthRequestParams(biginConf), + dataCenter: biginConf.dataCenter } bitsFetch(getFieldsRequestParams, 'zbigin_refresh_fields') .then(result => { @@ -296,10 +297,8 @@ export const refreshTags = (recordTab, formID, biginConf, setBiginConf, setIsLoa const getTagsRequestParams = { formID, module, - dataCenter: biginConf.dataCenter, - clientId: biginConf.clientId, - clientSecret: biginConf.clientSecret, - tokenDetails: biginConf.tokenDetails + ...buildAuthRequestParams(biginConf), + dataCenter: biginConf.dataCenter } bitsFetch(getTagsRequestParams, 'zbigin_refresh_tags') .then(result => { @@ -332,10 +331,8 @@ export const refreshUsers = (formID, biginConf, setBiginConf, setIsLoading, setS setIsLoading(true) const getUsersRequestParams = { formID, - dataCenter: biginConf.dataCenter, - clientId: biginConf.clientId, - clientSecret: biginConf.clientSecret, - tokenDetails: biginConf.tokenDetails + ...buildAuthRequestParams(biginConf), + dataCenter: biginConf.dataCenter } bitsFetch(getUsersRequestParams, 'zbigin_refresh_users') .then(result => { diff --git a/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRM.jsx b/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRM.jsx index 4da3d0489..9c3145055 100644 --- a/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRM.jsx +++ b/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRM.jsx @@ -1,11 +1,11 @@ /* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { saveActionConf, setGrantTokenResponse } from '../IntegrationHelpers/IntegrationHelpers' +import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import ZohoCRMAuthorization from './ZohoCRMAuthorization' import { checkMappedFields, handleInput } from './ZohoCRMCommonFunc' @@ -31,10 +31,6 @@ function ZohoCRM({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('zohoCRM') - }, []) - const saveConfig = () => { saveActionConf({ flow, diff --git a/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRMAuthorization.jsx b/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRMAuthorization.jsx index eb9eb5d58..3dfdc4def 100644 --- a/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRMAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRMAuthorization.jsx @@ -1,12 +1,9 @@ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import CopyText from '../../Utilities/CopyText' -import LoaderSm from '../../Loaders/LoaderSm' import { refreshModules } from './ZohoCRMCommonFunc' -import { handleAuthorize } from '../IntegrationHelpers/IntegrationHelpers' -import { $appConfigState } from '../../../GlobalStates' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' export default function ZohoCRMAuthorization({ formID, @@ -14,163 +11,86 @@ export default function ZohoCRMAuthorization({ setCrmConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '', clientSecret: '' }) - const btcbi = useRecoilValue($appConfigState) - const scopes = 'ZohoCRM.modules.ALL,ZohoCRM.settings.ALL,ZohoCRM.users.Read,zohocrm.files.CREATE' - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - - setstep(2) - !crmConf.module && refreshModules(formID, crmConf, setCrmConf, setIsLoading, setSnackbar) - } - const handleInput = e => { - const newConf = { ...crmConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setCrmConf(newConf) - } - - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Data Center:', 'bit-integrations')} -
    - -
    {error.dataCenter}
    + const dataCenterOptions = [ + { value: 'com', label: 'zoho.com' }, + { value: 'eu', label: 'zoho.eu' }, + { value: 'com.cn', label: 'zoho.com.cn' }, + { value: 'in', label: 'zoho.in' }, + { value: 'com.au', label: 'zoho.com.au' } + ] -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - + const scopes = 'ZohoCRM.modules.ALL,ZohoCRM.settings.ALL,ZohoCRM.users.Read,zohocrm.files.CREATE' - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Zoho API Console', 'bit-integrations')} - - + const loadModules = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...crmConf, connection_id: connectionId } : crmConf + if (!nextConf.module) { + refreshModules(formID, nextConf, setCrmConf, setIsLoading, setSnackbar) + } + }, + [crmConf, formID, setCrmConf, setIsLoading, setSnackbar] + ) -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    + const handleSetStep = useCallback( + value => { + if (value === 2 && !crmConf.module) { + loadModules() + } + setstep(value) + }, + [crmConf.module, loadModules, setstep] + ) -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    + const note = `

    ${__('Zoho CRM OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create app in Zoho API Console.', 'bit-integrations')}
    • +
    • ${__('Choose data center matching your Zoho account.', 'bit-integrations')}
    • +
    • ${__('Use callback URL shown in connection form.', 'bit-integrations')}
    • +
    ` - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRMCommonFunc.js b/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRMCommonFunc.js index fbe9e44b7..f4ca310d8 100644 --- a/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRMCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ZohoCRM/ZohoCRMCommonFunc.js @@ -1,6 +1,15 @@ import { __, sprintf } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const handleInput = ( e, recordTab, @@ -137,10 +146,8 @@ export const refreshModules = (formID, crmConf, setCrmConf, setIsLoading, setSna const refreshModulesRequestParams = { formID, id: crmConf.id, - dataCenter: crmConf.dataCenter, - clientId: crmConf.clientId, - clientSecret: crmConf.clientSecret, - tokenDetails: crmConf.tokenDetails + ...buildAuthRequestParams(crmConf), + dataCenter: crmConf.dataCenter } bitsFetch(refreshModulesRequestParams, 'zcrm_refresh_modules') .then(result => { @@ -187,10 +194,8 @@ export const refreshLayouts = (recordTab, formID, crmConf, setCrmConf, setIsLoad const refreshLayoutsRequestParams = { formID, module, - dataCenter: newConf.dataCenter, - clientId: newConf.clientId, - clientSecret: newConf.clientSecret, - tokenDetails: newConf.tokenDetails + ...buildAuthRequestParams(newConf), + dataCenter: newConf.dataCenter } bitsFetch(refreshLayoutsRequestParams, 'zcrm_refresh_layouts') .then(result => { @@ -255,10 +260,8 @@ export const refreshRelatedList = (formID, crmConf, setCrmConf, setIsLoading, se const relatedListRequestParams = { formID, module: crmConf.module, - dataCenter: crmConf.dataCenter, - clientId: crmConf.clientId, - clientSecret: crmConf.clientSecret, - tokenDetails: crmConf.tokenDetails + ...buildAuthRequestParams(crmConf), + dataCenter: crmConf.dataCenter } bitsFetch(relatedListRequestParams, 'zcrm_get_related_lists') .then(result => { @@ -301,10 +304,8 @@ export const refreshTags = (recordTab, formID, crmConf, setCrmConf, setIsLoading const refreshTagsParams = { formID, module, - dataCenter: crmConf.dataCenter, - clientId: crmConf.clientId, - clientSecret: crmConf.clientSecret, - tokenDetails: crmConf.tokenDetails + ...buildAuthRequestParams(crmConf), + dataCenter: crmConf.dataCenter } bitsFetch(refreshTagsParams, 'zcrm_get_tags') .then(result => { @@ -343,10 +344,8 @@ export const refreshOwners = (formID, crmConf, setCrmConf, setIsLoading, setSnac setIsLoading(true) const getOwnersParams = { formID, - dataCenter: crmConf.dataCenter, - clientId: crmConf.clientId, - clientSecret: crmConf.clientSecret, - tokenDetails: crmConf.tokenDetails + ...buildAuthRequestParams(crmConf), + dataCenter: crmConf.dataCenter } bitsFetch(getOwnersParams, 'zcrm_get_users') .then(result => { @@ -375,10 +374,8 @@ export const refreshAssigmentRules = (recordTab, crmConf, setCrmConf, setIsLoadi setIsLoading(true) const getAssigmentRulesParams = { module, - dataCenter: crmConf.dataCenter, - clientId: crmConf.clientId, - clientSecret: crmConf.clientSecret, - tokenDetails: crmConf.tokenDetails + ...buildAuthRequestParams(crmConf), + dataCenter: crmConf.dataCenter } bitsFetch(getAssigmentRulesParams, 'zcrm_get_assignment_rules') .then(result => { diff --git a/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaigns.jsx b/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaigns.jsx index 2fe8585b3..5a07307dd 100644 --- a/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaigns.jsx +++ b/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaigns.jsx @@ -5,7 +5,7 @@ import BackIcn from '../../../Icons/BackIcn' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { saveIntegConfig, setGrantTokenResponse } from '../IntegrationHelpers/IntegrationHelpers' +import { saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import ZohoCampaignsAuthorization from './ZohoCampaignsAuthorization' import { checkMappedFields, handleInput } from './ZohoCampaignsCommonFunc' @@ -26,10 +26,6 @@ function ZohoCampaigns({ formFields, setFlow, flow, allIntegURL }) { field_map: [{ formField: '', zohoFormField: '' }] }) - useEffect(() => { - window.opener && setGrantTokenResponse('zohoCampaigns') - }, []) - const nextPage = () => { setTimeout(() => { document.getElementById('btcd-settings-wrp').scrollTop = 0 diff --git a/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaignsAuthorization.jsx b/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaignsAuthorization.jsx index 2ded0abf8..c596832d4 100644 --- a/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaignsAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaignsAuthorization.jsx @@ -1,13 +1,9 @@ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import CopyText from '../../Utilities/CopyText' -import LoaderSm from '../../Loaders/LoaderSm' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' import { refreshLists } from './ZohoCampaignsCommonFunc' -import BackIcn from '../../../Icons/BackIcn' -import { $appConfigState } from '../../../GlobalStates' -import { handleAuthorize } from '../IntegrationHelpers/IntegrationHelpers' -import TutorialLink from '../../Utilities/TutorialLink' export default function ZohoCampaignsAuthorization({ formID, @@ -15,163 +11,83 @@ export default function ZohoCampaignsAuthorization({ setCampaignsConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '', clientSecret: '' }) - const btcbi = useRecoilValue($appConfigState) - const scopes = 'ZohoCampaigns.contact.READ,ZohoCampaigns.contact.CREATE,ZohoCampaigns.contact.UPDATE' - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - refreshLists(formID, campaignsConf, setCampaignsConf, setIsLoading, setSnackbar) - } - - const handleInput = e => { - const newConf = { ...campaignsConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setCampaignsConf(newConf) - } - - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Data Center:', 'bit-integrations')} -
    - -
    {error.dataCenter}
    + const dataCenterOptions = [ + { value: 'com', label: 'zoho.com' }, + { value: 'eu', label: 'zoho.eu' }, + { value: 'com.cn', label: 'zoho.com.cn' }, + { value: 'in', label: 'zoho.in' }, + { value: 'com.au', label: 'zoho.com.au' } + ] -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - + const scopes = 'ZohoCampaigns.contact.READ,ZohoCampaigns.contact.CREATE,ZohoCampaigns.contact.UPDATE' - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Zoho API Console', 'bit-integrations')} - - + const loadLists = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...campaignsConf, connection_id: connectionId } : campaignsConf + refreshLists(formID, nextConf, setCampaignsConf, setIsLoading, setSnackbar) + }, + [campaignsConf, formID, setCampaignsConf, setIsLoading, setSnackbar] + ) -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    + const handleSetStep = useCallback( + value => { + if (value === 2) { + loadLists() + } + setstep(value) + }, + [loadLists, setstep] + ) -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    + const note = `

    ${__('Zoho Campaigns OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create app in Zoho API Console.', 'bit-integrations')}
    • +
    • ${__('Use account data center for auth endpoints.', 'bit-integrations')}
    • +
    • ${__('Use callback URL shown in connection form.', 'bit-integrations')}
    • +
    ` - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaignsCommonFunc.js b/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaignsCommonFunc.js index 299641ca8..0b85af1ac 100644 --- a/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaignsCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ZohoCampaigns/ZohoCampaignsCommonFunc.js @@ -1,6 +1,15 @@ import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const handleInput = (e, formID, campaignsConf, setCampaignsConf, setIsLoading, setSnackbar) => { let newConf = { ...campaignsConf } newConf[e.target.name] = e.target.value @@ -30,10 +39,8 @@ export const refreshLists = (formID, campaignsConf, setCampaignsConf, setIsLoadi const refreshListsRequestParams = { formID, id: campaignsConf.id, - dataCenter: campaignsConf.dataCenter, - clientId: campaignsConf.clientId, - clientSecret: campaignsConf.clientSecret, - tokenDetails: campaignsConf.tokenDetails + ...buildAuthRequestParams(campaignsConf), + dataCenter: campaignsConf.dataCenter } bitsFetch(refreshListsRequestParams, 'zcampaigns_refresh_lists') .then(result => { @@ -84,10 +91,8 @@ export const refreshContactFields = ( const refreshContactFieldsRequestParams = { formID, list, - dataCenter: campaignsConf.dataCenter, - clientId: campaignsConf.clientId, - clientSecret: campaignsConf.clientSecret, - tokenDetails: campaignsConf.tokenDetails + ...buildAuthRequestParams(campaignsConf), + dataCenter: campaignsConf.dataCenter } bitsFetch(refreshContactFieldsRequestParams, 'zcampaigns_refresh_contact_fields') .then(result => { diff --git a/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreator.jsx b/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreator.jsx index 408460d75..16a6913b4 100644 --- a/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreator.jsx +++ b/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreator.jsx @@ -5,7 +5,7 @@ import BackIcn from '../../../Icons/BackIcn' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { saveIntegConfig, setGrantTokenResponse } from '../IntegrationHelpers/IntegrationHelpers' +import { saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import ZohoCreatorAuthorization from './ZohoCreatorAuthorization' import { checkMappedFields, handleInput } from './ZohoCreatorCommonFunc' @@ -27,10 +27,6 @@ function ZohoCreator({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('zohoCreator') - }, []) - const nextPage = () => { setTimeout(() => { document.getElementById('btcd-settings-wrp').scrollTop = 0 diff --git a/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreatorAuthorization.jsx b/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreatorAuthorization.jsx index d8a6048f6..d2ef540b9 100644 --- a/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreatorAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreatorAuthorization.jsx @@ -1,10 +1,9 @@ -import { useState } from 'react' -import BackIcn from '../../../Icons/BackIcn' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { handleAuthorize, refreshApplications } from './ZohoCreatorCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { refreshApplications } from './ZohoCreatorCommonFunc' export default function ZohoCreatorAuthorization({ formID, @@ -12,179 +11,94 @@ export default function ZohoCreatorAuthorization({ setCreatorConf, step, setStep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ - dataCenter: '', - clientId: '', - clientSecret: '', - ownerEmail: '' - }) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - if (!creatorConf.accountOwner) { - setError({ accountOwner: __('Account Owner Name is mandatory!', 'bit-integrations') }) - return - } - setStep(2) - if (!creatorConf.department) { - refreshApplications(formID, creatorConf, setCreatorConf, setIsLoading, setSnackbar) - } - } + const dataCenterOptions = [ + { value: 'com', label: 'zoho.com' }, + { value: 'eu', label: 'zoho.eu' }, + { value: 'com.cn', label: 'zoho.com.cn' }, + { value: 'in', label: 'zoho.in' }, + { value: 'com.au', label: 'zoho.com.au' } + ] - const handleInput = e => { - const newConf = { ...creatorConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setCreatorConf(newConf) - } + const scopes = + 'ZohoCreator.dashboard.READ,ZohoCreator.meta.application.READ,ZohoCreator.meta.form.READ,ZohoCreator.form.CREATE,ZohoCreator.report.CREATE,ZohoCreator.report.UPDATE' - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Data Center:', 'bit-integrations')} -
    - -
    {error.dataCenter}
    - -
    - {__('Homepage URL:', 'bit-integrations')} -
    - + const loadApplications = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...creatorConf, connection_id: connectionId } : creatorConf -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - + if (!nextConf.department) { + refreshApplications(formID, nextConf, setCreatorConf, setIsLoading, setSnackbar) + } + }, + [creatorConf, formID, setCreatorConf, setIsLoading, setSnackbar] + ) - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Zoho API Console', 'bit-integrations')} - - + const handleSetStep = useCallback( + value => { + if (value === 2 && !creatorConf.department) { + loadApplications() + } -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    + setStep(value) + }, + [creatorConf.department, loadApplications, setStep] + ) -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    -
    - {__('Owner Name (Your Zoho Creator screen name):', 'bit-integrations')} -
    - handleInput(e, creatorConf, setCreatorConf)} - name="accountOwner" - value={creatorConf.accountOwner} - type="text" - placeholder={__('Your Zoho Creator screen name...', 'bit-integrations')} - disabled={isInfo} - /> -
    {error.accountOwner}
    + const note = `

    ${__('Zoho Creator OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create app in Zoho API Console.', 'bit-integrations')}
    • +
    • ${__('Set account screen name as Account Owner field.', 'bit-integrations')}
    • +
    • ${__('Use callback URL shown in connection form.', 'bit-integrations')}
    • +
    ` - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreatorCommonFunc.js b/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreatorCommonFunc.js index 787222674..a375f326e 100644 --- a/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreatorCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ZohoCreator/ZohoCreatorCommonFunc.js @@ -1,120 +1,14 @@ import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' -export const setGrantTokenResponse = () => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation - .replace(`${window.opener.location.href}/redirect`, '') - .split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails } - }) - } - localStorage.setItem('__zohoCreator', JSON.stringify(grantTokenResponse)) - window.close() -} - -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar -) => { - if (!confTmp.dataCenter || !confTmp.clientId || !confTmp.clientSecret) { - setError({ - dataCenter: !confTmp.dataCenter ? __("Data center can't be empty", 'bit-integrations') : '', - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - const scopes = - 'ZohoCreator.dashboard.READ,ZohoCreator.meta.application.READ,ZohoCreator.meta.form.READ,ZohoCreator.form.CREATE,ZohoCreator.report.CREATE,ZohoCreator.report.UPDATE' - const apiEndpoint = `https://accounts.zoho.${ - confTmp.dataCenter - }/oauth/v2/auth?scope=${scopes}&response_type=code&client_id=${ - confTmp.clientId - }&prompt=Consent&access_type=offline&redirect_uri=${encodeURIComponent(window.location.href)}/redirect` - const authWindow = window.open(apiEndpoint, 'zohoCreator', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitformsZoho = localStorage.getItem('__zohoCreator') - if (bitformsZoho) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitformsZoho) - localStorage.removeItem('__zohoCreator') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - grantTokenResponse['accounts-server'] = decodeURIComponent(grantTokenResponse['accounts-server']) - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper(grantTokenResponse, newConf, setConf, setisAuthorized, setIsLoading, setSnackbar) - } - } - }, 500) -} - -const tokenHelper = (grantToken, confTmp, setConf, setisAuthorized, setIsLoading, setSnackbar) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.dataCenter = confTmp.dataCenter - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - tokenRequestParams.redirectURI = `${encodeURIComponent(window.location.href)}/redirect` - bitsFetch(tokenRequestParams, 'zcreator_generate_token') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} export const handleInput = ( e, @@ -185,10 +79,8 @@ export const refreshApplications = (formID, creatorConf, setCreatorConf, setIsLo const refreshApplicationsRequestParams = { formID, id: creatorConf.id, - dataCenter: creatorConf.dataCenter, - clientId: creatorConf.clientId, - clientSecret: creatorConf.clientSecret, - tokenDetails: creatorConf.tokenDetails + ...buildAuthRequestParams(creatorConf), + dataCenter: creatorConf.dataCenter } bitsFetch(refreshApplicationsRequestParams, 'zcreator_refresh_applications') .then(result => { @@ -226,10 +118,8 @@ export const refreshForms = (formID, creatorConf, setCreatorConf, setIsLoading, const refreshFormsRequestParams = { formID, id: creatorConf.id, + ...buildAuthRequestParams(creatorConf), dataCenter: creatorConf.dataCenter, - clientId: creatorConf.clientId, - clientSecret: creatorConf.clientSecret, - tokenDetails: creatorConf.tokenDetails, accountOwner, applicationId } @@ -274,10 +164,8 @@ export const refreshFields = (formID, creatorConf, setCreatorConf, setIsLoading, setIsLoading(true) const refreshFieldsRequestParams = { formID, + ...buildAuthRequestParams(creatorConf), dataCenter: creatorConf.dataCenter, - clientId: creatorConf.clientId, - clientSecret: creatorConf.clientSecret, - tokenDetails: creatorConf.tokenDetails, accountOwner, applicationId, formId diff --git a/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDesk.jsx b/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDesk.jsx index d92a6b701..b38b011bd 100644 --- a/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDesk.jsx +++ b/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDesk.jsx @@ -6,7 +6,7 @@ import BackIcn from '../../../Icons/BackIcn' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { saveActionConf, setGrantTokenResponse } from '../IntegrationHelpers/IntegrationHelpers' +import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import ZohoDeskAuthorization from './ZohoDeskAuthorization' import { checkMappedFields, handleInput, refreshOrganizations } from './ZohoDeskCommonFunc' @@ -28,10 +28,6 @@ function ZohoDesk({ formFields, setFlow, flow, allIntegURL }) { field_map: [{ formField: '', zohoFormField: '' }], actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('zohoDesk') - }, []) - const nextPage = val => { if (val === 3) { if (!checkMappedFields(deskConf)) { diff --git a/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDeskAuthorization.jsx b/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDeskAuthorization.jsx index 7dad942f3..0c5281ff9 100644 --- a/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDeskAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDeskAuthorization.jsx @@ -1,13 +1,9 @@ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' -import { $appConfigState } from '../../../GlobalStates' -import BackIcn from '../../../Icons/BackIcn' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { handleAuthorize } from '../IntegrationHelpers/IntegrationHelpers' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' import { refreshOrganizations } from './ZohoDeskCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' export default function ZohoDeskAuthorization({ formID, @@ -15,164 +11,84 @@ export default function ZohoDeskAuthorization({ setDeskConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '', clientSecret: '' }) + const dataCenterOptions = [ + { value: 'com', label: 'zoho.com' }, + { value: 'eu', label: 'zoho.eu' }, + { value: 'com.cn', label: 'zoho.com.cn' }, + { value: 'in', label: 'zoho.in' }, + { value: 'com.au', label: 'zoho.com.au' } + ] + const scopes = 'Desk.settings.READ,Desk.basic.READ,Desk.search.READ,Desk.contacts.READ,Desk.contacts.CREATE,Desk.contacts.UPDATE,Desk.tickets.CREATE,Desk.tickets.UPDATE' - const btcbi = useRecoilValue($appConfigState) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - refreshOrganizations(formID, deskConf, setDeskConf, setIsLoading, setSnackbar) - } - - const handleInput = e => { - const newConf = { ...deskConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setDeskConf(newConf) - } - - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Data Center:', 'bit-integrations')} -
    - -
    {error.dataCenter}
    -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - - - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Zoho API Console', 'bit-integrations')} - - + const loadOrganizations = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...deskConf, connection_id: connectionId } : deskConf + refreshOrganizations(formID, nextConf, setDeskConf, setIsLoading, setSnackbar) + }, + [deskConf, formID, setDeskConf, setIsLoading, setSnackbar] + ) -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    + const handleSetStep = useCallback( + value => { + if (value === 2) { + loadOrganizations() + } + setstep(value) + }, + [loadOrganizations, setstep] + ) -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    + const note = `

    ${__('Zoho Desk OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create app in Zoho API Console.', 'bit-integrations')}
    • +
    • ${__('Choose correct data center.', 'bit-integrations')}
    • +
    • ${__('Use callback URL shown in connection form.', 'bit-integrations')}
    • +
    ` - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDeskCommonFunc.js b/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDeskCommonFunc.js index 284c717f0..730e8f3ee 100644 --- a/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDeskCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ZohoDesk/ZohoDeskCommonFunc.js @@ -1,6 +1,15 @@ import { __, sprintf } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const handleInput = ( e, deskConf, @@ -64,10 +73,8 @@ export const refreshOrganizations = (formID, deskConf, setDeskConf, setIsLoading const refreshOrganizationsRequestParams = { formID, id: deskConf.id, - dataCenter: deskConf.dataCenter, - clientId: deskConf.clientId, - clientSecret: deskConf.clientSecret, - tokenDetails: deskConf.tokenDetails + ...buildAuthRequestParams(deskConf), + dataCenter: deskConf.dataCenter } bitsFetch(refreshOrganizationsRequestParams, 'zdesk_refresh_organizations') .then(result => { @@ -107,10 +114,8 @@ export const refreshDepartments = (formID, deskConf, setDeskConf, setIsLoading, const refreshDepartmentsRequestParams = { formID, id: deskConf.id, + ...buildAuthRequestParams(deskConf), dataCenter: deskConf.dataCenter, - clientId: deskConf.clientId, - clientSecret: deskConf.clientSecret, - tokenDetails: deskConf.tokenDetails, orgId: deskConf.orgId } bitsFetch(refreshDepartmentsRequestParams, 'zdesk_refresh_departments') @@ -159,10 +164,8 @@ export const refreshFields = (formID, deskConf, setDeskConf, setIsLoading, setSn setIsLoading(true) const refreshFieldsRequestParams = { formID, + ...buildAuthRequestParams(deskConf), dataCenter: deskConf.dataCenter, - clientId: deskConf.clientId, - clientSecret: deskConf.clientSecret, - tokenDetails: deskConf.tokenDetails, orgId: deskConf.orgId } bitsFetch(refreshFieldsRequestParams, 'zdesk_refresh_fields') @@ -208,10 +211,8 @@ export const refreshOwners = (formID, deskConf, setDeskConf, setIsLoading, setSn const refreshOwnersRequestParams = { formID, id: deskConf.id, + ...buildAuthRequestParams(deskConf), dataCenter: deskConf.dataCenter, - clientId: deskConf.clientId, - clientSecret: deskConf.clientSecret, - tokenDetails: deskConf.tokenDetails, orgId: deskConf.orgId } bitsFetch(refreshOwnersRequestParams, 'zdesk_refresh_owners') @@ -255,10 +256,8 @@ export const refreshProducts = (formID, deskConf, setDeskConf, setIsLoading, set const refreshProductsRequestParams = { formID, id: deskConf.id, + ...buildAuthRequestParams(deskConf), dataCenter: deskConf.dataCenter, - clientId: deskConf.clientId, - clientSecret: deskConf.clientSecret, - tokenDetails: deskConf.tokenDetails, orgId: deskConf.orgId, departmentId: deskConf.department } diff --git a/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHub.jsx b/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHub.jsx index ca85bdfe0..9e57c838f 100644 --- a/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHub.jsx +++ b/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHub.jsx @@ -8,12 +8,7 @@ import Steps from '../../Utilities/Steps' import { saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import ZohoMarketingHubAuthorization from './ZohoMarketingHubAuthorization' -import { - checkMappedFields, - handleInput, - refreshLists, - setGrantTokenResponse -} from './ZohoMarketingHubCommonFunc' +import { checkMappedFields, handleInput, refreshLists } from './ZohoMarketingHubCommonFunc' import ZohoMarketingHubIntegLayout from './ZohoMarketingHubIntegLayout' function ZohoMarketingHub({ formFields, setFlow, flow, allIntegURL }) { @@ -31,10 +26,6 @@ function ZohoMarketingHub({ formFields, setFlow, flow, allIntegURL }) { field_map: [{ formField: '', zohoFormField: '' }] }) - useEffect(() => { - window.opener && setGrantTokenResponse('zohoMarketingHub') - }, []) - const nextPage = val => { if (val === 3) { if (!checkMappedFields(marketingHubConf)) { diff --git a/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHubAuthorization.jsx b/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHubAuthorization.jsx index 108ef876e..e8695a13c 100644 --- a/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHubAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHubAuthorization.jsx @@ -1,13 +1,9 @@ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' -import { $appConfigState } from '../../../GlobalStates' -import BackIcn from '../../../Icons/BackIcn' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { handleAuthorize } from '../IntegrationHelpers/IntegrationHelpers' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' import { refreshLists } from './ZohoMarketingHubCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' export default function ZohoMarketingAuthorization({ formID, @@ -15,163 +11,85 @@ export default function ZohoMarketingAuthorization({ setMarketingHubConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '', clientSecret: '' }) - const btcbi = useRecoilValue($appConfigState) - const scopes = 'ZohoMarketingHub.lead.READ,ZohoMarketingHub.lead.CREATE,ZohoMarketingHub.lead.UPDATE' - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - refreshLists(formID, marketingHubConf, setMarketingHubConf, setIsLoading, setSnackbar) - } - - const handleInput = e => { - const newConf = { ...marketingHubConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setMarketingHubConf(newConf) - } - - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Data Center:', 'bit-integrations')} -
    - -
    {error.dataCenter}
    + const dataCenterOptions = [ + { value: 'com', label: 'zoho.com' }, + { value: 'eu', label: 'zoho.eu' }, + { value: 'com.cn', label: 'zoho.com.cn' }, + { value: 'in', label: 'zoho.in' }, + { value: 'com.au', label: 'zoho.com.au' } + ] -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - + const scopes = 'ZohoMarketingHub.lead.READ,ZohoMarketingHub.lead.CREATE,ZohoMarketingHub.lead.UPDATE' - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Zoho API Console', 'bit-integrations')} - - + const loadLists = useCallback( + async connectionId => { + const nextConf = connectionId + ? { ...marketingHubConf, connection_id: connectionId } + : marketingHubConf + refreshLists(formID, nextConf, setMarketingHubConf, setIsLoading, setSnackbar) + }, + [formID, marketingHubConf, setMarketingHubConf, setIsLoading, setSnackbar] + ) -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    + const handleSetStep = useCallback( + value => { + if (value === 2) { + loadLists() + } + setstep(value) + }, + [loadLists, setstep] + ) -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    + const note = `

    ${__('Zoho Marketing Hub OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create app in Zoho API Console.', 'bit-integrations')}
    • +
    • ${__('Choose matching data center.', 'bit-integrations')}
    • +
    • ${__('Use callback URL shown in connection form.', 'bit-integrations')}
    • +
    ` - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHubCommonFunc.js b/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHubCommonFunc.js index fd396c501..9206e3cd4 100644 --- a/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHubCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ZohoMarketingHub/ZohoMarketingHubCommonFunc.js @@ -1,6 +1,15 @@ import { __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const handleInput = ( e, formID, @@ -51,10 +60,8 @@ export const refreshLists = ( const refreshListsRequestParams = { formID, id: marketingHubConf.id, - dataCenter: marketingHubConf.dataCenter, - clientId: marketingHubConf.clientId, - clientSecret: marketingHubConf.clientSecret, - tokenDetails: marketingHubConf.tokenDetails + ...buildAuthRequestParams(marketingHubConf), + dataCenter: marketingHubConf.dataCenter } bitsFetch(refreshListsRequestParams, 'zmarketingHub_refresh_lists') .then(result => { @@ -106,10 +113,8 @@ export const refreshContactFields = ( const refreshContactFieldsRequestParams = { formID, list, - dataCenter: marketingHubConf.dataCenter, - clientId: marketingHubConf.clientId, - clientSecret: marketingHubConf.clientSecret, - tokenDetails: marketingHubConf.tokenDetails + ...buildAuthRequestParams(marketingHubConf), + dataCenter: marketingHubConf.dataCenter } bitsFetch(refreshContactFieldsRequestParams, 'zmarketingHub_refresh_contact_fields') .then(result => { @@ -162,22 +167,3 @@ export const checkMappedFields = marketingHubConf => { return true } - -export const setGrantTokenResponse = () => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation - .replace(`${window.opener.location.href}/redirect`, '') - .split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] - } - }) - } - localStorage.setItem('__zohoMarkatingHub', JSON.stringify(grantTokenResponse)) - window.close() -} diff --git a/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruit.jsx b/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruit.jsx index 39b885d82..171710786 100644 --- a/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruit.jsx +++ b/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruit.jsx @@ -5,7 +5,7 @@ import BackIcn from '../../../Icons/BackIcn' import { __ } from '../../../Utils/i18nwrap' import SnackMsg from '../../Utilities/SnackMsg' import Steps from '../../Utilities/Steps' -import { saveIntegConfig, setGrantTokenResponse } from '../IntegrationHelpers/IntegrationHelpers' +import { saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import ZohoRecruitAuthorization from './ZohoRecruitAuthorization' import { checkMappedFields, handleInput } from './ZohoRecruitCommonFunc' @@ -29,10 +29,6 @@ function ZohoRecruit({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('zohoRecruit') - }, []) - const nextPage = () => { setTimeout(() => { document.getElementById('btcd-settings-wrp').scrollTop = 0 diff --git a/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruitAuthorization.jsx b/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruitAuthorization.jsx index 015db6f74..7f837ea92 100644 --- a/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruitAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruitAuthorization.jsx @@ -1,13 +1,9 @@ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' -import { $appConfigState } from '../../../GlobalStates' -import BackIcn from '../../../Icons/BackIcn' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { handleAuthorize } from '../IntegrationHelpers/IntegrationHelpers' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' import { refreshModules } from './ZohoRecruitCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' export default function ZohoRecruitAuthorization({ formID, @@ -15,163 +11,83 @@ export default function ZohoRecruitAuthorization({ setRecruitConf, step, setstep, - isLoading, setIsLoading, setSnackbar, - redirectLocation, isInfo }) { - const [isAuthorized, setisAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '', clientSecret: '' }) - const btcbi = useRecoilValue($appConfigState) - const scopes = 'ZohoRecruit.users.ALL,ZohoRecruit.modules.all' - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) - setstep(2) - refreshModules(formID, recruitConf, setRecruitConf, setIsLoading, setSnackbar) - } - - const handleInput = e => { - const newConf = { ...recruitConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setRecruitConf(newConf) - } - - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Data Center:', 'bit-integrations')} -
    - -
    {error.dataCenter}
    + const dataCenterOptions = [ + { value: 'com', label: 'zoho.com' }, + { value: 'eu', label: 'zoho.eu' }, + { value: 'com.cn', label: 'zoho.com.cn' }, + { value: 'in', label: 'zoho.in' }, + { value: 'com.au', label: 'zoho.com.au' } + ] -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - + const scopes = 'ZohoRecruit.users.ALL,ZohoRecruit.modules.all' - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Zoho API Console', 'bit-integrations')} - - + const loadModules = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...recruitConf, connection_id: connectionId } : recruitConf + refreshModules(formID, nextConf, setRecruitConf, setIsLoading, setSnackbar) + }, + [formID, recruitConf, setRecruitConf, setIsLoading, setSnackbar] + ) -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    + const handleSetStep = useCallback( + value => { + if (value === 2) { + loadModules() + } + setstep(value) + }, + [loadModules, setstep] + ) -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    + const note = `

    ${__('Zoho Recruit OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create app in Zoho API Console.', 'bit-integrations')}
    • +
    • ${__('Choose account data center.', 'bit-integrations')}
    • +
    • ${__('Use callback URL shown in connection form.', 'bit-integrations')}
    • +
    ` - {!isInfo && ( - <> - -
    - - - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruitCommonFunc.js b/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruitCommonFunc.js index f212cc610..60a105eb1 100644 --- a/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruitCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ZohoRecruit/ZohoRecruitCommonFunc.js @@ -1,6 +1,15 @@ import { sprintf, __ } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const handleInput = ( e, recordTab, @@ -95,10 +104,8 @@ export const refreshModules = (formID, recruitConf, setRecruitConf, setIsLoading const refreshModulesRequestParams = { formID, id: recruitConf.id, - dataCenter: recruitConf.dataCenter, - clientId: recruitConf.clientId, - clientSecret: recruitConf.clientSecret, - tokenDetails: recruitConf.tokenDetails + ...buildAuthRequestParams(recruitConf), + dataCenter: recruitConf.dataCenter } bitsFetch(refreshModulesRequestParams, 'zrecruit_refresh_modules') .then(result => { @@ -142,10 +149,8 @@ export const refreshNoteTypes = (formID, recruitConf, setRecruitConf, setIsLoadi const refreshModulesRequestParams = { formID, id: recruitConf.id, - dataCenter: recruitConf.dataCenter, - clientId: recruitConf.clientId, - clientSecret: recruitConf.clientSecret, - tokenDetails: recruitConf.tokenDetails + ...buildAuthRequestParams(recruitConf), + dataCenter: recruitConf.dataCenter } bitsFetch(refreshModulesRequestParams, 'zrecruit_refresh_notetypes') .then(result => { @@ -192,10 +197,8 @@ export const refreshRelatedList = (formID, recruitConf, setRecruitConf, setIsLoa const relatedListRequestParams = { formID, module: recruitConf.module, - dataCenter: recruitConf.dataCenter, - clientId: recruitConf.clientId, - clientSecret: recruitConf.clientSecret, - tokenDetails: recruitConf.tokenDetails + ...buildAuthRequestParams(recruitConf), + dataCenter: recruitConf.dataCenter } bitsFetch(relatedListRequestParams, 'zrecruit_refresh_related_lists') .then(result => { @@ -241,10 +244,8 @@ const getFields = (recordTab, formID, recruitConf, setRecruitConf, setIsLoading, const getFieldsRequestParams = { formID, module, - dataCenter: recruitConf.dataCenter, - clientId: recruitConf.clientId, - clientSecret: recruitConf.clientSecret, - tokenDetails: recruitConf.tokenDetails + ...buildAuthRequestParams(recruitConf), + dataCenter: recruitConf.dataCenter } bitsFetch(getFieldsRequestParams, 'zrecruit_get_fields') .then(result => { diff --git a/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheet.jsx b/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheet.jsx index e5d493cff..d68517dfd 100644 --- a/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheet.jsx +++ b/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheet.jsx @@ -1,6 +1,6 @@ /* eslint-disable no-console */ /* eslint-disable no-unused-expressions */ -import { useEffect, useState } from 'react' +import { useState } from 'react' import { useNavigate } from 'react-router' import toast from 'react-hot-toast' import { __ } from '../../../Utils/i18nwrap' @@ -9,7 +9,7 @@ import Steps from '../../Utilities/Steps' import { saveIntegConfig } from '../IntegrationHelpers/IntegrationHelpers' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import ZohoSheetAuthorization from './ZohoSheetAuthorization' -import { checkMappedFields, setGrantTokenResponse } from './ZohoSheetCommonFunc' +import { checkMappedFields } from './ZohoSheetCommonFunc' import ZohoSheetIntegLayout from './ZohoSheetIntegLayout' function ZohoSheet({ formFields, setFlow, flow, allIntegURL }) { @@ -41,10 +41,6 @@ function ZohoSheet({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - window.opener && setGrantTokenResponse('zohoSheet') - }, []) - const saveConfig = () => { setIsLoading(true) const resp = saveIntegConfig( diff --git a/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheetAuthorization.jsx b/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheetAuthorization.jsx index be70ce70f..8040f38d8 100644 --- a/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheetAuthorization.jsx +++ b/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheetAuthorization.jsx @@ -1,13 +1,9 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -/* eslint-disable no-unused-expressions */ -import { useState } from 'react' -import { useRecoilValue } from 'recoil' +import { useCallback } from 'react' +import { AUTH_TYPES } from '../../../Utils/connectionAuth' import { __ } from '../../../Utils/i18nwrap' -import LoaderSm from '../../Loaders/LoaderSm' -import CopyText from '../../Utilities/CopyText' -import { $appConfigState } from '../../../GlobalStates' -import { handleAuthorization } from './ZohoSheetCommonFunc' -import TutorialLink from '../../Utilities/TutorialLink' +import tutorialLinks from '../../../Utils/StaticData/tutorialLinks' +import Authorization from '../../Connections/Authorization' +import { getAllWorkbooks } from './ZohoSheetCommonFunc' export default function ZohoSheetAuthorization({ zohoSheetConf, @@ -16,160 +12,83 @@ export default function ZohoSheetAuthorization({ setStep, loading, setLoading, - isInfo, - setSnackbar, - redirectLocation + isInfo }) { - const [isAuthorized, setIsAuthorized] = useState(false) - const [error, setError] = useState({ dataCenter: '', clientId: '', clientSecret: '' }) - const btcbi = useRecoilValue($appConfigState) - const nextPage = () => { - setTimeout(() => { - document.getElementById('btcd-settings-wrp').scrollTop = 0 - }, 300) + const dataCenterOptions = [ + { value: 'com', label: 'zoho.com' }, + { value: 'eu', label: 'zoho.eu' }, + { value: 'com.cn', label: 'zoho.com.cn' }, + { value: 'in', label: 'zoho.in' }, + { value: 'com.au', label: 'zoho.com.au' } + ] - !zohoSheetConf?.default - setStep(2) - } + const scope = 'ZohoSheet.dataAPI.READ,ZohoSheet.dataAPI.UPDATE' - const handleInput = e => { - const newConf = { ...zohoSheetConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setZohoSheetConf(newConf) - } - - return ( -
    - - -
    - {__('Integration Name:', 'bit-integrations')} -
    - - -
    - {__('Data Center:', 'bit-integrations')} -
    - -
    {error.dataCenter}
    - -
    - {__('Homepage URL:', 'bit-integrations')} -
    - - -
    - {__('Authorized Redirect URIs:', 'bit-integrations')} -
    - - - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Zoho API Console', 'bit-integrations')} - - + const loadWorkbooks = useCallback( + async connectionId => { + const nextConf = connectionId ? { ...zohoSheetConf, connection_id: connectionId } : zohoSheetConf + if (!nextConf?.workbooks?.length) { + getAllWorkbooks(nextConf, setZohoSheetConf, loading, setLoading) + } + }, + [loading, setLoading, setZohoSheetConf, zohoSheetConf] + ) -
    - {__('Client id:', 'bit-integrations')} -
    - -
    {error.clientId}
    + const handleSetStep = useCallback( + value => { + if (value === 2 && !zohoSheetConf?.workbooks?.length) { + loadWorkbooks() + } + setStep(value) + }, + [loadWorkbooks, setStep, zohoSheetConf?.workbooks?.length] + ) -
    - {__('Client secret:', 'bit-integrations')} -
    - -
    {error.clientSecret}
    -
    -
    + const note = `

    ${__('Zoho Sheet OAuth setup', 'bit-integrations')}

    +
      +
    • ${__('Create app in Zoho API Console.', 'bit-integrations')}
    • +
    • ${__('Select account data center.', 'bit-integrations')}
    • +
    • ${__('Use callback URL shown in connection form.', 'bit-integrations')}
    • +
    ` - {!isInfo && ( -
    - -
    - -
    - )} -
    + return ( + ) } diff --git a/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheetCommonFunc.js b/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheetCommonFunc.js index 53cce7f97..ef86acf1d 100644 --- a/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheetCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ZohoSheet/ZohoSheetCommonFunc.js @@ -4,6 +4,15 @@ import toast from 'react-hot-toast' import bitsFetch from '../../../Utils/bitsFetch' import { __ } from '../../../Utils/i18nwrap' +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails + } + export const handleInput = (e, zohoSheetConf, setZohoSheetConf) => { const newConf = { ...zohoSheetConf } const { name } = e.target @@ -33,9 +42,7 @@ export const checkMappedFields = zohoSheetConf => { export const getAllWorkbooks = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, workbooks: true }) const requestParams = { - tokenDetails: confTmp.tokenDetails, - clientId: confTmp.clientId, - clientSecret: confTmp.clientSecret, + ...buildAuthRequestParams(confTmp), dataCenter: confTmp.dataCenter } @@ -58,9 +65,7 @@ export const getAllWorkbooks = (confTmp, setConf, loading, setLoading) => { export const getAllWorksheets = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, worksheets: true }) const requestParams = { - tokenDetails: confTmp.tokenDetails, - clientId: confTmp.clientId, - clientSecret: confTmp.clientSecret, + ...buildAuthRequestParams(confTmp), dataCenter: confTmp.dataCenter, workbook: confTmp.selectedWorkbook } @@ -84,9 +89,7 @@ export const getAllWorksheets = (confTmp, setConf, loading, setLoading) => { export const getWorksheetHeader = (confTmp, setConf, loading, setLoading) => { setLoading({ ...loading, header: true, workSheetHeaders: false }) const requestParams = { - tokenDetails: confTmp.tokenDetails, - clientId: confTmp.clientId, - clientSecret: confTmp.clientSecret, + ...buildAuthRequestParams(confTmp), dataCenter: confTmp.dataCenter, workbook: confTmp.selectedWorkbook, worksheet: confTmp.selectedWorksheet, @@ -108,119 +111,3 @@ export const getWorksheetHeader = (confTmp, setConf, loading, setLoading) => { toast.error(__(`${result.data}`, 'bit-integrations')) }) } - -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation - .replace(`${window.opener.location.href}/redirect`, '') - .split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] - } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} - -export const handleAuthorization = ( - confTmp, - setConf, - setError, - setisAuthorized, - loading, - setLoading, - btcbi -) => { - if (!confTmp.dataCenter || !confTmp.clientId || !confTmp.clientSecret) { - setError({ - dataCenter: !confTmp.dataCenter ? __("Data center can't be empty") : '', - clientId: !confTmp.clientId ? __("Client ID can't be empty") : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty") : '' - }) - return - } - setLoading({ ...loading, auth: true }) - const scopes = 'ZohoSheet.dataAPI.READ,ZohoSheet.dataAPI.UPDATE' - const apiEndpoint = `https://accounts.zoho.${ - confTmp.dataCenter - }/oauth/v2/auth?scope=${scopes}&response_type=code&client_id=${ - confTmp.clientId - }&prompt=Consent&access_type=offline&state=${encodeURIComponent( - window.location.href - )}/redirect&redirect_uri=${encodeURIComponent(`${btcbi.api}/redirect`)}` - const authWindow = window.open(apiEndpoint, '__zohoSheet', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const ZohoSheet = localStorage.getItem('__zohoSheet') - if (ZohoSheet) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(ZohoSheet) - localStorage.removeItem('__zohoSheet') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - toast.error( - __( - `${__('Authorization Failed')} ${errorCause}. ${__('please try again')}`, - 'bit-integrations' - ) - ) - setLoading({ ...loading, auth: false }) - } else { - const newConf = { ...confTmp } - grantTokenResponse['accounts-server'] = decodeURIComponent(grantTokenResponse['accounts-server']) - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper(grantTokenResponse, newConf, setConf, setisAuthorized, loading, setLoading, btcbi) - } - } - }, 500) -} - -const tokenHelper = (grantToken, confTmp, setConf, setisAuthorized, loading, setLoading, btcbi) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.dataCenter = confTmp.dataCenter - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - bitsFetch(tokenRequestParams, 'zohoSheet_generate_token') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - toast.success(__('Authorized Successfully', 'bit-integrations')) - getAllWorkbooks(newConf, setConf, loading, setLoading) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - toast.error( - __( - `${__('Authorization failed Cause:')}${result.data.data || result.data}. ${__( - 'please try again' - )}`, - 'bit-integrations' - ) - ) - } else { - toast.error(__('Authorization failed. please try again', 'bit-integrations')) - } - setLoading({ ...loading, auth: false }) - }) -} diff --git a/frontend/src/components/AllIntegrations/Zoom/Zoom.jsx b/frontend/src/components/AllIntegrations/Zoom/Zoom.jsx index 3f0465b7f..e74afe58c 100644 --- a/frontend/src/components/AllIntegrations/Zoom/Zoom.jsx +++ b/frontend/src/components/AllIntegrations/Zoom/Zoom.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' import BackIcn from '../../../Icons/BackIcn' @@ -8,7 +8,6 @@ import Steps from '../../Utilities/Steps' import ZoomAuthorization from './ZoomAuthorization' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import { handleInput, checkMappedFields } from './ZoomCommonFunc' -import { setGrantTokenResponse } from './ZoomCommonFunc' import ZoomIntegLayout from './ZoomIntegLayout' import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' @@ -36,10 +35,6 @@ function Zoom({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - // eslint-disable-next-line no-unused-expressions - window.opener && setGrantTokenResponse('zoom') - }, []) const nextPage = () => { setTimeout(() => { document.getElementById('btcd-settings-wrp').scrollTop = 0 @@ -61,16 +56,7 @@ function Zoom({ formFields, setFlow, flow, allIntegURL }) { {/* STEP 1 */} - + {/* STEP 2 */}
    { - const newConf = { ...zoomConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setZoomConf(newConf) - } - const nextPage = () => { - zoomAllMeeting(formID, zoomConf, setZoomConf, setIsLoading, setSnackbar) - setStep(2) - } - - const ZoomInstructions = `

    ${__('Pro or higher plan only .', 'bit-integrations')}

    +export default function ZoomAuthorization({ zoomConf, setZoomConf, step, setStep, isInfo }) { + const note = `

    ${__('Pro or higher plan only .', 'bit-integrations')}

    ${__('Client Id and Client Secret generate with OAuth .', 'bit-integrations')}

    ${__('Scope:', 'bit-integrations')}

      @@ -56,114 +23,28 @@ export default function ZoomAuthorization({ ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Homepage URL:', 'bit-integrations')} -
      - - -
      - {__('Authorized Redirect URIs:', 'bit-integrations')} -
      - - - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Get Zoom client id and secret', 'bit-integrations')} - - - -
      - {__('Client id:', 'bit-integrations')} -
      - -
      {error.clientId}
      - -
      - {__('Client secret:', 'bit-integrations')} -
      - -
      {error.clientSecret}
      - {!isInfo && ( - <> - -
      - - - )} - -
      + ) } diff --git a/frontend/src/components/AllIntegrations/Zoom/ZoomCommonFunc.js b/frontend/src/components/AllIntegrations/Zoom/ZoomCommonFunc.js index 3ab747f7d..387b86e72 100644 --- a/frontend/src/components/AllIntegrations/Zoom/ZoomCommonFunc.js +++ b/frontend/src/components/AllIntegrations/Zoom/ZoomCommonFunc.js @@ -38,32 +38,22 @@ export const handleInput = ( setZoomConf({ ...newConf }) } -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation.replace(`${window.opener.location.href}`, '').split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails, + accessToken: conf?.tokenDetails?.access_token, + refreshToken: conf?.tokenDetails?.refresh_token } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} export const zoomAllMeeting = (formID, zoomConf, setZoomConf, setIsLoading, setSnackbar) => { setIsLoading(true) const fetchMeetingModulesRequestParams = { formID, - clientId: zoomConf.clientId, - accessToken: zoomConf.tokenDetails.access_token, - clientSecret: zoomConf.clientSecret, - refreshToken: zoomConf.tokenDetails.refresh_token, - tokenDetails: zoomConf.tokenDetails + ...buildAuthRequestParams(zoomConf) } bitsFetch(fetchMeetingModulesRequestParams, 'zoom_fetch_all_meetings') .then(result => { @@ -107,11 +97,7 @@ export const refreshFields = (zoomConf, setZoomConf, setIsLoading, setSnackbar) setIsLoading(true) const fetchMeetingModulesRequestParams = { meetingId: zoomConf.id, - clientId: zoomConf.clientId, - accessToken: zoomConf.tokenDetails.access_token, - clientSecret: zoomConf.clientSecret, - refreshToken: zoomConf.tokenDetails.refresh_token, - tokenDetails: zoomConf.tokenDetails + ...buildAuthRequestParams(zoomConf) } bitsFetch(fetchMeetingModulesRequestParams, 'zoom_fetch_all_fields') .then(result => { @@ -136,118 +122,6 @@ export const refreshFields = (zoomConf, setZoomConf, setIsLoading, setSnackbar) .catch(() => setIsLoading(false)) } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - const apiEndpoint = `https://zoom.us/oauth/authorize?response_type=code&client_id=${ - confTmp.clientId - }&state=${encodeURIComponent(window.location.href)}/redirect&redirect_uri=${encodeURIComponent( - `${btcbi.api}/redirect` - )}` - const authWindow = window.open(apiEndpoint, 'zoom', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitsGoogleSheet = localStorage.getItem('__zoom') - if (bitsGoogleSheet) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsGoogleSheet) - localStorage.removeItem('__zoom') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper( - grantTokenResponse, - newConf, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi - ) - } - } - }, 500) -} - -const tokenHelper = ( - grantToken, - confTmp, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - // eslint-disable-next-line no-undef - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - bitsFetch(tokenRequestParams, 'zoom_generate_token') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ - show: true, - msg: __('Authorized Successfully', 'bit-integrations') - }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} - export const checkMappedFields = zoomConf => { const mappedFleld = zoomConf.field_map ? zoomConf.field_map.filter(mapped => !mapped.formField && !mapped.zoomConf) diff --git a/frontend/src/components/AllIntegrations/ZoomWebinar/ZoomCommonFunc.js b/frontend/src/components/AllIntegrations/ZoomWebinar/ZoomCommonFunc.js index 30804a974..61dfdac1c 100644 --- a/frontend/src/components/AllIntegrations/ZoomWebinar/ZoomCommonFunc.js +++ b/frontend/src/components/AllIntegrations/ZoomWebinar/ZoomCommonFunc.js @@ -2,22 +2,16 @@ import { __, sprintf } from '../../../Utils/i18nwrap' import bitsFetch from '../../../Utils/bitsFetch' import { deepCopy } from '../../../Utils/Helpers' -export const setGrantTokenResponse = integ => { - const grantTokenResponse = {} - const authWindowLocation = window.location.href - const queryParams = authWindowLocation.replace(`${window.opener.location.href}`, '').split('&') - if (queryParams) { - queryParams.forEach(element => { - const gtKeyValue = element.split('=') - if (gtKeyValue[1]) { - // eslint-disable-next-line prefer-destructuring - grantTokenResponse[gtKeyValue[0]] = gtKeyValue[1] +const buildAuthRequestParams = conf => + conf?.connection_id + ? { connection_id: conf.connection_id } + : { + clientId: conf.clientId, + clientSecret: conf.clientSecret, + tokenDetails: conf.tokenDetails, + accessToken: conf?.tokenDetails?.access_token, + refreshToken: conf?.tokenDetails?.refresh_token } - }) - } - localStorage.setItem(`__${integ}`, JSON.stringify(grantTokenResponse)) - window.close() -} export const handleInput = ( e, @@ -49,11 +43,7 @@ export const zoomAllWebinar = ( setIsLoading(true) const fetchWebinarModulesRequestParams = { formID, - clientId: zoomWebinarConf.clientId, - accessToken: zoomWebinarConf.tokenDetails.access_token, - clientSecret: zoomWebinarConf.clientSecret, - refreshToken: zoomWebinarConf.tokenDetails.refresh_token, - tokenDetails: zoomWebinarConf.tokenDetails + ...buildAuthRequestParams(zoomWebinarConf) } bitsFetch(fetchWebinarModulesRequestParams, 'zoom_webinar_fetch_all_webinar') .then(result => { @@ -92,115 +82,6 @@ export const zoomAllWebinar = ( .catch(() => setIsLoading(false)) } -export const handleAuthorize = ( - confTmp, - setConf, - setError, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - if (!confTmp.clientId || !confTmp.clientSecret) { - setError({ - clientId: !confTmp.clientId ? __("Client Id can't be empty", 'bit-integrations') : '', - clientSecret: !confTmp.clientSecret ? __("Secret key can't be empty", 'bit-integrations') : '' - }) - return - } - setIsLoading(true) - const apiEndpoint = `https://zoom.us/oauth/authorize?response_type=code&client_id=${ - confTmp.clientId - }&state=${encodeURIComponent(window.location.href)}/redirect&redirect_uri=${encodeURIComponent( - `${btcbi.api}/redirect` - )}` - const authWindow = window.open(apiEndpoint, 'zoom', 'width=400,height=609,toolbar=off') - const popupURLCheckTimer = setInterval(() => { - if (authWindow.closed) { - clearInterval(popupURLCheckTimer) - let grantTokenResponse = {} - let isauthRedirectLocation = false - const bitsGoogleSheet = localStorage.getItem('__zoom') - if (bitsGoogleSheet) { - isauthRedirectLocation = true - grantTokenResponse = JSON.parse(bitsGoogleSheet) - localStorage.removeItem('__zoom') - } - if ( - !grantTokenResponse.code || - grantTokenResponse.error || - !grantTokenResponse || - !isauthRedirectLocation - ) { - const errorCause = grantTokenResponse.error ? `Cause: ${grantTokenResponse.error}` : '' - setSnackbar({ - show: true, - msg: `${__('Authorization Failed', 'bit-integrations')} ${errorCause}. ${__( - 'please try again', - 'bit-integrations' - )}` - }) - setIsLoading(false) - } else { - const newConf = { ...confTmp } - newConf.accountServer = grantTokenResponse['accounts-server'] - tokenHelper( - grantTokenResponse, - newConf, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi - ) - } - } - }, 500) -} - -const tokenHelper = ( - grantToken, - confTmp, - setConf, - setisAuthorized, - setIsLoading, - setSnackbar, - btcbi -) => { - const tokenRequestParams = { ...grantToken } - tokenRequestParams.clientId = confTmp.clientId - tokenRequestParams.clientSecret = confTmp.clientSecret - // eslint-disable-next-line no-undef - tokenRequestParams.redirectURI = `${btcbi.api}/redirect` - bitsFetch(tokenRequestParams, 'zoom_webinar_generate_token') - .then(result => result) - .then(result => { - if (result && result.success) { - const newConf = { ...confTmp } - newConf.tokenDetails = result.data - setConf(newConf) - setisAuthorized(true) - setSnackbar({ show: true, msg: __('Authorized Successfully', 'bit-integrations') }) - } else if ( - (result && result.data && result.data.data) || - (!result.success && typeof result.data === 'string') - ) { - setSnackbar({ - show: true, - msg: `${__('Authorization failed Cause:', 'bit-integrations')}${ - result.data.data || result.data - }. ${__('please try again', 'bit-integrations')}` - }) - } else { - setSnackbar({ - show: true, - msg: __('Authorization failed. please try again', 'bit-integrations') - }) - } - setIsLoading(false) - }) -} - export const checkMappedFields = zoomWebinarConf => { const mappedFleld = zoomWebinarConf.field_map ? zoomWebinarConf.field_map.filter(mapped => !mapped.formField && !mapped.zoomWebinarConf) diff --git a/frontend/src/components/AllIntegrations/ZoomWebinar/ZoomWebinar.jsx b/frontend/src/components/AllIntegrations/ZoomWebinar/ZoomWebinar.jsx index b0ba98e95..eb667b431 100644 --- a/frontend/src/components/AllIntegrations/ZoomWebinar/ZoomWebinar.jsx +++ b/frontend/src/components/AllIntegrations/ZoomWebinar/ZoomWebinar.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import 'react-multiple-select-dropdown-lite/dist/index.css' import { useNavigate, useParams } from 'react-router' import BackIcn from '../../../Icons/BackIcn' @@ -8,7 +8,6 @@ import Steps from '../../Utilities/Steps' import ZoomWebinarAuthorization from './ZoomWebinarAuthorization' import IntegrationStepThree from '../IntegrationHelpers/IntegrationStepThree' import { handleInput, checkMappedFields } from './ZoomCommonFunc' -import { setGrantTokenResponse } from './ZoomCommonFunc' import { saveActionConf } from '../IntegrationHelpers/IntegrationHelpers' import ZoomWebinarIntegLayout from './ZoomWebinarIntegLayout' @@ -46,10 +45,6 @@ function ZoomWebinar({ formFields, setFlow, flow, allIntegURL }) { actions: {} }) - useEffect(() => { - // eslint-disable-next-line no-unused-expressions - window.opener && setGrantTokenResponse('zoom') - }, []) const nextPage = () => { setTimeout(() => { document.getElementById('btcd-settings-wrp').scrollTop = 0 @@ -72,14 +67,10 @@ function ZoomWebinar({ formFields, setFlow, flow, allIntegURL }) { {/* STEP 1 */} {/* STEP 2 */}
      { - const newConf = { ...zoomWebinarConf } - const rmError = { ...error } - rmError[e.target.name] = '' - newConf[e.target.name] = e.target.value - setError(rmError) - setZoomWebinarConf(newConf) - } - const nextPage = () => { - zoomAllWebinar(formID, zoomWebinarConf, setZoomWebinarConf, setIsLoading, setSnackbar) - setStep(2) - } - - const ZoomInstructions = `

      ${__('Pro or higher plan only .', 'bit-integrations')}

      + const note = `

      ${__('Pro or higher plan only .', 'bit-integrations')}

      ${__('Client Id and Client Secret generate with OAuth .', 'bit-integrations')}

      ${__('Scope:', 'bit-integrations')}

        @@ -55,114 +28,28 @@ export default function ZoomWebinarAuthorization({
      ` return ( -
      - - -
      - {__('Integration Name:', 'bit-integrations')} -
      - - -
      - {__('Homepage URL:', 'bit-integrations')} -
      - - -
      - {__('Authorized Redirect URIs:', 'bit-integrations')} -
      - - - - {__('To get Client ID and SECRET , Please Visit', 'bit-integrations')}{' '} - - {__('Get Zoom client id and secret', 'bit-integrations')} - - - -
      - {__('Client id:', 'bit-integrations')} -
      - -
      {error.clientId}
      - -
      - {__('Client secret:', 'bit-integrations')} -
      - -
      {error.clientSecret}
      - {!isInfo && ( - <> - -
      - - - )} - -
      + ) } diff --git a/frontend/src/components/Connections/AddNewConnection.jsx b/frontend/src/components/Connections/AddNewConnection.jsx new file mode 100644 index 000000000..296492773 --- /dev/null +++ b/frontend/src/components/Connections/AddNewConnection.jsx @@ -0,0 +1,16 @@ +import { AUTH_TYPES } from '../../Utils/connectionAuth' +import ApiConnection from './ApiConnection' +import Oauth1Connection from './Oauth1Connection' +import Oauth2Connection from './Oauth2Connection' + +export default function AddNewConnection(props) { + if (props?.authDetails?.authType === AUTH_TYPES.OAUTH2) { + return + } + + if (props?.authDetails?.authType === AUTH_TYPES.OAUTH1) { + return + } + + return +} diff --git a/frontend/src/components/Connections/ApiConnection.jsx b/frontend/src/components/Connections/ApiConnection.jsx new file mode 100644 index 000000000..ce92868e3 --- /dev/null +++ b/frontend/src/components/Connections/ApiConnection.jsx @@ -0,0 +1,420 @@ +import { useCallback, useState } from 'react' +import toast from 'react-hot-toast' +import { authorizeConnection, saveConnection } from '../../Utils/connectionApi' +import { AUTH_TYPES, defaultEncryptKeys } from '../../Utils/connectionAuth' +import { __ } from '../../Utils/i18nwrap' +import LoaderSm from '../Loaders/LoaderSm' + +const ERROR_TEXT_STYLE = { color: 'red', fontSize: '15px' } + +const normalizeAdditionalHeaders = headers => { + if (!headers || typeof headers !== 'object') { + return {} + } + + return Object.entries(headers).reduce((acc, [key, value]) => { + const normalizedKey = String(key || '').trim() + const normalizedValue = value == null ? '' : String(value).trim() + + if (normalizedKey && normalizedValue) { + acc[normalizedKey] = normalizedValue + } + + return acc + }, {}) +} + +// Resolves {fieldName} placeholders in URL templates using authData values. +// Strips trailing slashes from substituted values to avoid double-slash in paths. +// Unknown tokens are replaced with '' (empty required fields caught by validation). +const resolveTemplate = (template, data) => { + if (!template) return '' + return template.replace(/\{(\w+)\}/g, (_, key) => { + const val = data[key] + if (val == null) return '' + return typeof val === 'string' ? val.replace(/\/+$/, '') : String(val) + }) +} + +const resolveHeaderTemplates = (headers, data) => { + if (!headers || typeof headers !== 'object') { + return {} + } + + return Object.entries(headers).reduce((acc, [key, value]) => { + acc[key] = typeof value === 'string' ? resolveTemplate(value, data) : value + return acc + }, {}) +} + +const resolvePayloadTemplates = (payload, data) => { + if (Array.isArray(payload)) { + return payload.map(item => resolvePayloadTemplates(item, data)) + } + + if (payload && typeof payload === 'object') { + return Object.entries(payload).reduce((acc, [key, value]) => { + acc[key] = resolvePayloadTemplates(value, data) + return acc + }, {}) + } + + if (typeof payload === 'string') { + return resolveTemplate(payload, data) + } + + return payload +} + +const resolveConfigValue = (value, data) => { + if (typeof value === 'function') { + return value(data) + } + + return value +} + +const getAuthPayload = ({ authType, apiEndpoint, method, authData, authDetails }) => { + const resolvedApiEndpoint = resolveConfigValue(apiEndpoint, authData) + const resolvedHeaders = resolveConfigValue(authDetails?.headers, authData) + const resolvedPayload = resolveConfigValue(authDetails?.payload, authData) + const additionalHeaders = resolveHeaderTemplates(normalizeAdditionalHeaders(resolvedHeaders), authData) + const sslVerify = authDetails?.ssl_verify !== false + + // Extra fields captured first; standard auth keys below always win on collision. + // Reserved auth_details keys: value, token, key, addTo, username, password, ssl_verify + const extraAuthDetails = (authDetails?.extraFields || []).reduce((acc, { name }) => { + if (authData[name] != null) acc[name] = authData[name] + return acc + }, {}) + + const basePayload = { + auth_type: authType, + api_endpoint: resolveTemplate(resolvedApiEndpoint, authData), + method: method || 'GET', + auth_details: { + ...extraAuthDetails, + ssl_verify: sslVerify + }, + headers: additionalHeaders + } + + if (resolvedPayload !== undefined) { + basePayload.payload = resolvePayloadTemplates(resolvedPayload, authData) + } + + if (authType === AUTH_TYPES.API_KEY) { + basePayload.auth_details = { + ...extraAuthDetails, + key: authDetails?.key || 'X-API-Key', + value: authData.api_key, + addTo: authData.addTo || authDetails?.addTo || 'header', + ssl_verify: sslVerify + } + return basePayload + } + + if (authType === AUTH_TYPES.BASIC_AUTH) { + basePayload.auth_details = { + ...extraAuthDetails, + username: authData.username, + password: authData.password, + ssl_verify: sslVerify + } + return basePayload + } + + if (authType === AUTH_TYPES.BEARER_TOKEN) { + basePayload.auth_details = { + ...extraAuthDetails, + token: authData.token, + ssl_verify: sslVerify + } + } + + return basePayload +} + +const getValidationErrors = (authType, authData, extraFields = [], authDetails = {}) => { + const nextErrors = {} + + if (!authData.connectionName?.trim()) { + nextErrors.connectionName = __('Connection name is required', 'bit-integrations') + } + + if (authType === AUTH_TYPES.API_KEY && !authData.api_key?.trim()) { + nextErrors.api_key = __('API key is required', 'bit-integrations') + } + + if (authType === AUTH_TYPES.BASIC_AUTH) { + if (!authData.username?.trim()) { + nextErrors.username = __('Username is required', 'bit-integrations') + } + + if (!authDetails?.allowEmptyPassword && !authData.password?.trim()) { + nextErrors.password = __('Password is required', 'bit-integrations') + } + } + + if (authType === AUTH_TYPES.BEARER_TOKEN && !authData.token?.trim()) { + nextErrors.token = __('Bearer token is required', 'bit-integrations') + } + + extraFields.forEach(field => { + const value = authData[field.name] + const normalized = typeof value === 'string' ? value.trim() : value + if (field.required && (normalized === '' || normalized == null)) { + nextErrors[field.name] = `${field.label} ${__('is required', 'bit-integrations')}` + } + }) + + return nextErrors +} + +export default function ApiConnection({ + authDetails, + config, + setConfig, + isInfo = false, + customAuthFields, + onConnectionSaved +}) { + const [authData, setAuthData] = useState({}) + const [isAuthorized, setIsAuthorized] = useState(false) + const [isLoading, setIsLoading] = useState(false) + const [errors, setErrors] = useState({}) + const { authType, apiEndpoint, method } = authDetails || {} + + const handleChange = useCallback(event => { + const { name, value } = event.target + + setAuthData(prev => ({ ...prev, [name]: value })) + setErrors(prev => ({ ...prev, [name]: '' })) + }, []) + + const handleAuthorize = useCallback(async () => { + const validationErrors = getValidationErrors( + authType, + authData, + authDetails?.extraFields, + authDetails + ) + setErrors(validationErrors) + + if (Object.keys(validationErrors).length > 0) { + return + } + + const payload = getAuthPayload({ + authType, + apiEndpoint, + method, + authData, + authDetails + }) + + setIsLoading(true) + + try { + const authorizeRes = await authorizeConnection(payload) + + if (!authorizeRes?.success) { + setIsAuthorized(false) + toast.error( + `${__('Authorization failed Cause:', 'bit-integrations')}${ + authorizeRes?.data?.data || authorizeRes?.data || 'Unknown error' + }. ${__('please try again', 'bit-integrations')}` + ) + return + } + + const saveRes = await saveConnection({ + app_slug: config?.app_slug || config?.type, + auth_type: authType, + connection_name: authData.connectionName, + account_name: authData.connectionName, + auth_details: payload.auth_details, + encrypt_keys: authDetails?.encryptKeys || defaultEncryptKeys[authType] || [] + }) + + if (!saveRes?.success) { + toast.error( + `${__('Failed to save connection Cause:', 'bit-integrations')}${ + saveRes?.data?.data || saveRes?.data || '' + }. ${__('please try again', 'bit-integrations')}` + ) + return + } + + const connection = saveRes?.data?.data || null + const persistedExtraFields = (authDetails?.extraFields || []).reduce((acc, { name }) => { + if (authData[name] != null) { + acc[name] = authData[name] + } + return acc + }, {}) + + setConfig(prev => ({ ...prev, connection_id: connection?.id, ...persistedExtraFields })) + + if (onConnectionSaved) { + await onConnectionSaved(connection) + } + + setIsAuthorized(true) + toast.success(__('Authorized Successfully', 'bit-integrations')) + } catch (error) { + setIsAuthorized(false) + toast.error( + `${__('Authorization failed Cause:', 'bit-integrations')} ${ + error?.message || 'Unknown error' + }. ${__('please try again', 'bit-integrations')}` + ) + } finally { + setIsLoading(false) + } + }, [ + apiEndpoint, + authData, + authDetails, + authType, + config?.app_slug, + config?.type, + method, + onConnectionSaved, + setConfig + ]) + + return ( + <> +
      + {__('Connection Name:', 'bit-integrations')} +
      + +
      {errors.connectionName || ''}
      + + {customAuthFields} + + {authDetails?.extraFields?.map(field => ( +
      +
      + {field.label}: +
      + {field.type === 'select' ? ( + + ) : ( + + )} +
      {errors[field.name] || ''}
      +
      + ))} + + {authType === AUTH_TYPES.API_KEY && ( + <> +
      + {__('API Key:', 'bit-integrations')} +
      + +
      {errors.api_key || ''}
      + + )} + + {authType === AUTH_TYPES.BASIC_AUTH && ( + <> +
      + {__('Username:', 'bit-integrations')} +
      + +
      {errors.username || ''}
      + + {!authDetails?.allowEmptyPassword && ( + <> +
      + {__('Password:', 'bit-integrations')} +
      + +
      {errors.password || ''}
      + + )} + + )} + + {authType === AUTH_TYPES.BEARER_TOKEN && ( + <> +
      + {__('Bearer Token:', 'bit-integrations')} +
      + +
      {errors.token || ''}
      + + )} + + + + ) +} diff --git a/frontend/src/components/Connections/Authorization.jsx b/frontend/src/components/Connections/Authorization.jsx new file mode 100644 index 000000000..860a5a100 --- /dev/null +++ b/frontend/src/components/Connections/Authorization.jsx @@ -0,0 +1,254 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import BackIcn from '../../Icons/BackIcn' +import { isWpPluginCheckType } from '../../Utils/connectionAuth' +import { verifyPluginActivation, listConnections } from '../../Utils/connectionApi' +import { __ } from '../../Utils/i18nwrap' +import LoaderSm from '../Loaders/LoaderSm' +import Note from '../Utilities/Note' +import TutorialLink from '../Utilities/TutorialLink' +import AddNewConnection from './AddNewConnection' +import ConnectionAccountSelect from './ConnectionAccountSelect' + +const STEP_ONE_STYLE = { width: 900, height: 'auto' } +const ERROR_TEXT_STYLE = { color: 'red', fontSize: '15px' } + +export default function Authorization({ + config, + setConfig, + step, + setStep, + isInfo, + tutorialTitle, + tutorialLinks, + extraFields, + noteDetails = undefined, + authDetails = {}, + customAuthFields, + onConnectionSelected +}) { + const [errors, setErrors] = useState({ name: '' }) + const [connections, setConnections] = useState([]) + const [showNewConnection, setShowNewConnection] = useState(false) + const [isLoading, setIsLoading] = useState(false) + const [isVerifying, setIsVerifying] = useState(false) + const [isVerified, setIsVerified] = useState(false) + const [isPendingPostAuth, setIsPendingPostAuth] = useState(false) + + const appSlug = config?.app_slug || config?.type + const isWpPluginCheck = isWpPluginCheckType(authDetails?.authType) + + const refreshConnections = useCallback(async () => { + if (!appSlug) { + setConnections([]) + setShowNewConnection(true) + return [] + } + + setIsLoading(true) + + try { + const res = await listConnections(appSlug) + const savedConnections = res?.success && Array.isArray(res?.data?.data) ? res.data.data : [] + + setConnections(savedConnections) + setShowNewConnection(savedConnections.length === 0) + return savedConnections + } catch { + return [] + } finally { + setIsLoading(false) + } + }, [appSlug]) + + useEffect(() => { + if (isWpPluginCheck) { + return + } + + refreshConnections() + }, [appSlug, isWpPluginCheck]) + + const handleNameChange = useCallback( + event => { + const { name, value } = event.target + setConfig(prev => ({ ...prev, [name]: value })) + + if (name === 'name') { + setErrors(prev => ({ ...prev, name: '' })) + } + }, + [setConfig] + ) + + const pluginCheck = authDetails?.pluginCheck + + const handleVerifyPluginActivation = useCallback(async () => { + if (!config?.name?.trim()) { + setErrors({ name: __('Integration name is required', 'bit-integrations') }) + return + } + + const hasGroups = Array.isArray(pluginCheck?.groups) && pluginCheck.groups.length > 0 + const hasChecks = Array.isArray(pluginCheck?.checks) && pluginCheck.checks.length > 0 + + if (!hasGroups && !hasChecks) { + setErrors({ + name: __('Plugin checks are not defined for this integration', 'bit-integrations') + }) + return + } + + setIsVerifying(true) + setErrors({}) + + try { + const res = await verifyPluginActivation({ + logic: pluginCheck.logic || 'AND', + ...(hasGroups ? { groups: pluginCheck.groups } : { checks: pluginCheck.checks }) + }) + + if (res?.success) { + setIsVerified(true) + } else { + setIsVerified(false) + setErrors({ + name: res?.data || __('Plugin is not installed or activated', 'bit-integrations') + }) + } + } catch (error) { + setIsVerified(false) + setErrors({ + name: error?.message || __('Plugin check failed', 'bit-integrations') + }) + } finally { + setIsVerifying(false) + } + }, [config?.name, pluginCheck]) + + const handleNext = useCallback(() => { + if (!config?.name?.trim()) { + setErrors({ name: __('Integration name is required', 'bit-integrations') }) + return + } + + setStep(2) + }, [config?.name, setStep]) + + const fireConnectionSelected = useCallback( + async connectionId => { + if (!onConnectionSelected || !connectionId) return + setIsPendingPostAuth(true) + try { + await onConnectionSelected(connectionId) + } finally { + setIsPendingPostAuth(false) + } + }, + [onConnectionSelected] + ) + + const canGoNext = isWpPluginCheck ? isVerified : Boolean(config?.connection_id) && !isPendingPostAuth + + const pageStyle = useMemo(() => (step === 1 ? STEP_ONE_STYLE : undefined), [step]) + + const handleConnectionSaved = useCallback( + async savedConnection => { + const refreshedConnections = await refreshConnections() + const savedConnectionId = savedConnection?.id + + if (savedConnectionId) { + const matchedConnection = refreshedConnections.find( + conn => String(conn.id) === String(savedConnectionId) + ) + const selectedConnectionId = matchedConnection?.id || savedConnectionId + setConfig(prev => ({ ...prev, connection_id: selectedConnectionId })) + setShowNewConnection(false) + await fireConnectionSelected(selectedConnectionId) + return + } + + if (refreshedConnections.length > 0) { + setShowNewConnection(false) + } + }, + [refreshConnections, setConfig, fireConnectionSelected] + ) + + return ( +
      + {tutorialTitle && } + +
      + {__('Integration Name:', 'bit-integrations')} +
      + +
      {errors.name || ''}
      + + {!isWpPluginCheck && ( + <> + + + {showNewConnection && !isInfo && (extraFields || null)} + + {showNewConnection && !isInfo && ( + + )} + + )} + + {isWpPluginCheck && !isInfo && ( + + )} + + {!isInfo && canGoNext && ( + + )} +
      +
      + + {noteDetails && ( + + {noteDetails?.children || null} + + )} +
      + ) +} diff --git a/frontend/src/components/Connections/ConnectionAccountSelect.jsx b/frontend/src/components/Connections/ConnectionAccountSelect.jsx new file mode 100644 index 000000000..d1e83c1c6 --- /dev/null +++ b/frontend/src/components/Connections/ConnectionAccountSelect.jsx @@ -0,0 +1,105 @@ +import { useCallback, useMemo } from 'react' +import MultiSelect from 'react-multiple-select-dropdown-lite' +import { useParams } from 'react-router' +import { __ } from '../../Utils/i18nwrap' +import 'react-multiple-select-dropdown-lite/dist/index.css' + +const NEW_VALUE = '__new__' + +const buildConnectionOption = conn => { + const accountName = conn.account_name || conn.connection_name + const hasDifferentNames = + conn.connection_name && conn.account_name && conn.connection_name !== conn.account_name + + return { + label: hasDifferentNames + ? `${conn.connection_name} (${accountName})` + : conn.connection_name || accountName, + value: String(conn.id) + } +} + +const getConnectionOptionById = (connections, connection_id) => { + if (connection_id) return String(connection_id) + if (connection_id === '') return NEW_VALUE + if (Array.isArray(connections) && connections.length > 0) return '' + + return NEW_VALUE +} + +export default function ConnectionAccountSelect({ + config, + setConfig, + connections, + setShowNewConnection, + isInfo, + onRefresh, + isRefreshing = false, + onConnectionSelected +}) { + const { integUrlName } = useParams() + const dropdownValue = getConnectionOptionById(connections, config?.connection_id) + + const options = useMemo( + () => [ + ...(Array.isArray(connections) ? connections.map(buildConnectionOption) : []), + { label: __('+ Add new connection', 'bit-integrations'), value: NEW_VALUE } + ], + [connections] + ) + + const handleChange = useCallback( + value => { + const isNewConnection = value === NEW_VALUE + setShowNewConnection(isNewConnection) + + if (isNewConnection) { + setConfig(prev => ({ ...prev, connection_id: '' })) + return + } + + setConfig(prev => ({ ...prev, connection_id: value })) + onConnectionSelected?.(value) + }, + [setConfig, setShowNewConnection, onConnectionSelected] + ) + + const connectionTitle = integUrlName + ? `${integUrlName} ${__('Connections:', 'bit-integrations')}` + : __('Connections:', 'bit-integrations') + const fetchConnections = onRefresh + const isLoading = isRefreshing + + return ( +
      +
      + {connectionTitle} +
      +
      + + {!isInfo && fetchConnections && ( + + )} +
      +
      + ) +} diff --git a/frontend/src/components/Connections/Oauth1Connection.jsx b/frontend/src/components/Connections/Oauth1Connection.jsx new file mode 100644 index 000000000..59afc2fe9 --- /dev/null +++ b/frontend/src/components/Connections/Oauth1Connection.jsx @@ -0,0 +1,476 @@ +import { useCallback, useMemo, useState } from 'react' +import toast from 'react-hot-toast' +import { authorizeConnection, saveConnection } from '../../Utils/connectionApi' +import { AUTH_TYPES, defaultEncryptKeys } from '../../Utils/connectionAuth' +import { + buildCallbackState, + createOauthChannelKey, + getCallbackState, + openOauthPopup +} from '../../Utils/oauthHelper' +import { __ } from '../../Utils/i18nwrap' +import { APP_CONFIG } from '../../config/app' +import LoaderSm from '../Loaders/LoaderSm' +import CopyText from '../Utilities/CopyText' + +const ERROR_TEXT_STYLE = { color: 'red', fontSize: '15px' } + +const resolveTemplate = (template, data) => { + if (!template) return '' + return template.replace(/\{(\w+)\}/g, (_, key) => { + const val = data[key] + if (val == null) return '' + return typeof val === 'string' ? val.replace(/\/+$/, '') : String(val) + }) +} + +const appendQueryParam = (url, key, value) => { + if (value == null || value === '') return + url.searchParams.append(key, String(value)) +} + +const buildOauth1AuthUrl = (authEndpoint, extraParams = {}) => { + const url = new URL(authEndpoint.url) + const queryParams = authEndpoint.queryParams || {} + + Object.entries(queryParams).forEach(([key, value]) => appendQueryParam(url, key, value)) + Object.entries(extraParams).forEach(([key, value]) => appendQueryParam(url, key, value)) + + return url.toString() +} + +const normalizeAdditionalHeaders = headers => { + if (!headers || typeof headers !== 'object') return {} + + return Object.entries(headers).reduce((acc, [key, value]) => { + const normalizedKey = String(key || '').trim() + const normalizedValue = value == null ? '' : String(value).trim() + + if (normalizedKey && normalizedValue) { + acc[normalizedKey] = normalizedValue + } + + return acc + }, {}) +} + +const resolveHeaderTemplates = (headers, data) => { + if (!headers || typeof headers !== 'object') return {} + + return Object.entries(headers).reduce((acc, [key, value]) => { + acc[key] = typeof value === 'string' ? resolveTemplate(value, data) : value + return acc + }, {}) +} + +const resolvePayloadTemplates = (payload, data) => { + if (Array.isArray(payload)) { + return payload.map(item => resolvePayloadTemplates(item, data)) + } + + if (payload && typeof payload === 'object') { + return Object.entries(payload).reduce((acc, [key, value]) => { + acc[key] = resolvePayloadTemplates(value, data) + return acc + }, {}) + } + + if (typeof payload === 'string') { + return resolveTemplate(payload, data) + } + + return payload +} + +const resolveConfigValue = (value, data) => { + if (typeof value === 'function') { + return value(data) + } + + return value +} + +const normalizePopupResponse = popupResponse => { + if (popupResponse && typeof popupResponse === 'object') return popupResponse + + if (typeof popupResponse === 'string') { + const parsed = {} + const source = popupResponse.replace(/^#/, '').replace(/^\?/, '') + const params = new URLSearchParams(source) + for (const [key, value] of params.entries()) { + if (value) parsed[key] = value + } + return parsed + } + + return {} +} + +const getOauth1Payload = ({ + authDetails, + formData, + accessToken, + accessTokenSecret, + consumerKeyParam, + tokenParam +}) => { + const resolvedApiEndpoint = resolveConfigValue(authDetails?.apiEndpoint, formData) + const resolvedHeaders = resolveConfigValue(authDetails?.headers, formData) + const resolvedPayload = resolveConfigValue(authDetails?.payload, formData) + const additionalHeaders = resolveHeaderTemplates(normalizeAdditionalHeaders(resolvedHeaders), formData) + const sslVerify = authDetails?.ssl_verify !== false + + const extraAuthDetails = (authDetails?.extraFields || []).reduce((acc, { name }) => { + if (formData[name] != null) acc[name] = formData[name] + return acc + }, {}) + + const payload = { + auth_type: AUTH_TYPES.OAUTH1, + api_endpoint: resolveTemplate(resolvedApiEndpoint, formData), + method: authDetails?.method || 'GET', + auth_details: { + ...extraAuthDetails, + consumer_key: formData.clientId, + consumer_secret: formData.clientSecret || '', + access_token: accessToken, + access_token_secret: accessTokenSecret || '', + consumer_key_param: consumerKeyParam, + token_param: tokenParam, + addTo: authDetails?.addTo || 'query', + ssl_verify: sslVerify + }, + headers: additionalHeaders + } + + if (authDetails?.signatureMethod) { + payload.auth_details.signature_method = authDetails.signatureMethod + } + + if (resolvedPayload !== undefined) { + payload.payload = resolvePayloadTemplates(resolvedPayload, formData) + } + + return payload +} + +export default function Oauth1Connection({ + authDetails, + config, + setConfig, + isInfo = false, + customAuthFields, + onConnectionSaved +}) { + const [formData, setFormData] = useState({}) + const [errors, setErrors] = useState({}) + const [isLoading, setIsLoading] = useState(false) + const [isAuthorized, setIsAuthorized] = useState(false) + + const callbackUrl = useMemo( + () => authDetails?.callbackUrl || getCallbackState(), + [authDetails?.callbackUrl] + ) + + const resolvedAuthEndpoint = useMemo(() => { + if (!authDetails?.authCodeEndpoint?.url) return null + + return { + ...authDetails.authCodeEndpoint, + url: resolveTemplate(authDetails.authCodeEndpoint.url, formData) + } + }, [authDetails?.authCodeEndpoint, formData]) + + const requireClientSecret = authDetails?.requireClientSecret !== false + const consumerKeyParam = authDetails?.consumerKeyParam || 'oauth_consumer_key' + const tokenParam = authDetails?.tokenParam || 'oauth_token' + const responseTokenField = authDetails?.responseTokenField || tokenParam + const responseTokenSecretField = authDetails?.responseTokenSecretField || 'oauth_token_secret' + + const handleChange = useCallback(event => { + const { name, value } = event.target + setFormData(prev => ({ ...prev, [name]: value })) + setErrors(prev => ({ ...prev, [name]: '' })) + }, []) + + const validate = useCallback(() => { + const nextErrors = {} + const extraFields = authDetails?.extraFields || [] + + if (!formData.connectionName?.trim()) { + nextErrors.connectionName = __('Connection name is required', 'bit-integrations') + } + + if (!formData.clientId?.trim()) { + nextErrors.clientId = __('Client ID is required', 'bit-integrations') + } + + if (requireClientSecret && !formData.clientSecret?.trim()) { + nextErrors.clientSecret = __('Client secret is required', 'bit-integrations') + } + + extraFields.forEach(field => { + if (field.required && !formData[field.name]?.trim()) { + nextErrors[field.name] = `${field.label} ${__('is required', 'bit-integrations')}` + } + }) + + if (!resolvedAuthEndpoint?.url) { + nextErrors.authorize = __('OAuth1 authorization URL is required', 'bit-integrations') + } + + setErrors(nextErrors) + return Object.keys(nextErrors).length === 0 + }, [authDetails?.extraFields, formData, requireClientSecret, resolvedAuthEndpoint?.url]) + + const saveOauth1Connection = useCallback( + async payload => { + const saveRes = await saveConnection({ + app_slug: config?.app_slug || config?.type, + auth_type: AUTH_TYPES.OAUTH1, + connection_name: formData.connectionName, + account_name: formData.connectionName, + auth_details: payload.auth_details, + encrypt_keys: authDetails?.encryptKeys || defaultEncryptKeys[AUTH_TYPES.OAUTH1] || [] + }) + + if (!saveRes?.success) { + const reason = saveRes?.data?.data || saveRes?.data || '' + toast.error(`${__('Failed to save connection Cause:', 'bit-integrations')}${reason}`) + return null + } + + const connection = saveRes?.data?.data || null + const persistedExtraFields = (authDetails?.extraFields || []).reduce((acc, { name }) => { + if (formData[name] != null) acc[name] = formData[name] + return acc + }, {}) + + setConfig(prev => ({ ...prev, connection_id: connection?.id, ...persistedExtraFields })) + + if (onConnectionSaved) await onConnectionSaved(connection) + + setIsAuthorized(true) + toast.success(__('Authorized Successfully', 'bit-integrations')) + return connection + }, + [authDetails?.encryptKeys, authDetails?.extraFields, config, formData, onConnectionSaved, setConfig] + ) + + const handleAuthorize = useCallback(async () => { + if (!validate()) return + + const declaredQueryParams = resolvedAuthEndpoint?.queryParams || {} + const queryParams = { ...declaredQueryParams } + const authExtraParams = {} + const callbackUrlParam = authDetails?.callbackUrlParam || '' + const stateParam = authDetails?.stateParam || 'state' + + if (!queryParams[consumerKeyParam]) { + authExtraParams[consumerKeyParam] = formData.clientId + } + + if (callbackUrlParam && !queryParams[callbackUrlParam]) { + authExtraParams[callbackUrlParam] = callbackUrl + } + + setIsLoading(true) + + try { + const oauthChannelKey = createOauthChannelKey() + const callbackState = buildCallbackState(oauthChannelKey) + + const authUrl = buildOauth1AuthUrl( + { + ...resolvedAuthEndpoint, + queryParams + }, + { + ...authExtraParams, + ...(queryParams[stateParam] ? {} : { [stateParam]: callbackState }) + } + ) + + const popupResponse = normalizePopupResponse( + await openOauthPopup(authUrl, authDetails?.authorizationWindowLabel || 'OAuth1', { + channelKey: oauthChannelKey, + includeLegacyFallback: true + }) + ) + + if (popupResponse?.error) { + throw new Error( + popupResponse.error === 'popup_blocked' + ? __('Popup blocked. Please allow popups and try again.', 'bit-integrations') + : popupResponse.error_description || + __('Authorization window closed before completing.', 'bit-integrations') + ) + } + + const accessToken = + popupResponse?.[responseTokenField] || popupResponse?.[tokenParam] || popupResponse?.token || '' + const accessTokenSecret = + popupResponse?.[responseTokenSecretField] || popupResponse?.oauth_token_secret || '' + + if (!accessToken) { + throw new Error(__('Authorization token missing', 'bit-integrations')) + } + + const payload = getOauth1Payload({ + authDetails, + formData, + accessToken, + accessTokenSecret, + consumerKeyParam, + tokenParam + }) + + if (!authDetails?.skipAuthorizationCheck) { + const authorizeRes = await authorizeConnection(payload) + + if (!authorizeRes?.success) { + throw new Error( + authorizeRes?.data?.data?.message || + authorizeRes?.data?.message || + authorizeRes?.data?.data || + authorizeRes?.data || + __('Authorization failed', 'bit-integrations') + ) + } + } + + await saveOauth1Connection(payload) + } catch (error) { + setIsAuthorized(false) + toast.error( + `${__('Authorization failed Cause:', 'bit-integrations')} ${error?.message || 'Unknown error'}` + ) + } finally { + setIsLoading(false) + } + }, [ + authDetails, + callbackUrl, + consumerKeyParam, + formData, + resolvedAuthEndpoint, + responseTokenField, + responseTokenSecretField, + saveOauth1Connection, + tokenParam, + validate + ]) + + return ( + <> +
      + {__('Connection Name:', 'bit-integrations')} +
      + +
      {errors.connectionName || ''}
      + + {authDetails?.showCallbackInfo !== false && ( + <> +
      + {__('Homepage URL:', 'bit-integrations')} +
      + +
      + {authDetails?.callbackLabel || __('Callback / Return URL:', 'bit-integrations')} +
      + + + )} + + {customAuthFields} + + {(authDetails?.extraFields || []).map(field => ( +
      +
      + {field.label}: +
      + {field.type === 'select' ? ( + + ) : ( + + )} +
      {errors[field.name] || ''}
      +
      + ))} + +
      + {authDetails?.clientIdLabel || __('Client ID:', 'bit-integrations')} +
      + +
      {errors.clientId || ''}
      + + {requireClientSecret && ( + <> +
      + {authDetails?.clientSecretLabel || __('Client Secret:', 'bit-integrations')} +
      + +
      {errors.clientSecret || ''}
      + + )} + +
      {errors.authorize || ''}
      + + + + ) +} diff --git a/frontend/src/components/Connections/Oauth2Connection.jsx b/frontend/src/components/Connections/Oauth2Connection.jsx new file mode 100644 index 000000000..9387285f5 --- /dev/null +++ b/frontend/src/components/Connections/Oauth2Connection.jsx @@ -0,0 +1,435 @@ +import { useCallback, useMemo, useState } from 'react' +import toast from 'react-hot-toast' +import { AUTH_TYPES, defaultEncryptKeys } from '../../Utils/connectionAuth' +import { saveConnection } from '../../Utils/connectionApi' +import { + buildAuthUrl, + buildCallbackState, + createOauthChannelKey, + exchangeAuthCodeForToken, + exchangeClientCredentialsForToken, + generateCodeChallengeS256, + generateCodeVerifier, + getRedirectUri, + openOauthPopup +} from '../../Utils/oauthHelper' +import { __ } from '../../Utils/i18nwrap' +import LoaderSm from '../Loaders/LoaderSm' +import CopyText from '../Utilities/CopyText' +import { APP_CONFIG } from '../../config/app' + +const ERROR_TEXT_STYLE = { color: 'red', fontSize: '15px' } +const READONLY_INPUT_STYLE = { backgroundColor: '#f5f5f5' } + +// Resolves {fieldName} placeholders in URL templates using form data. +// Strips trailing slashes from substituted values. Unknown tokens → ''. +const resolveTemplate = (template, data) => { + if (!template) return '' + return template.replace(/\{(\w+)\}/g, (_, key) => { + const val = data[key] + if (val == null) return '' + return typeof val === 'string' ? val.replace(/\/+$/, '') : String(val) + }) +} + +const GRANT_TYPES = Object.freeze({ + AUTHORIZATION_CODE: 'authorization_code', + AUTHORIZATION_CODE_PKCE: 'authorization_code_pkce', + CLIENT_CREDENTIALS: 'client_credentials' +}) + +const buildSavedAuthDetails = ({ + tokenResponse, + clientId, + clientSecret, + clientAuthentication, + grantType, + refreshTokenUrl, + tokenUrl, + scope, + sslVerify, + extraTokenFields = [], + extraFormData = {} +}) => { + const expiresIn = Number(tokenResponse?.expires_in) || 0 + const persistedGrantType = + grantType === GRANT_TYPES.AUTHORIZATION_CODE_PKCE ? GRANT_TYPES.AUTHORIZATION_CODE : grantType + + const base = { + access_token: tokenResponse?.access_token || '', + refresh_token: tokenResponse?.refresh_token || '', + token_type: tokenResponse?.token_type || 'Bearer', + expires_in: expiresIn, + generated_at: Math.floor(Date.now() / 1000), + client_id: clientId, + client_secret: clientSecret, + clientAuthentication, + grant_type: persistedGrantType, + refresh_token_url: refreshTokenUrl || tokenUrl, + scope: scope || '', + ssl_verify: sslVerify !== false + } + + // Capture provider-specific extra fields from token response (e.g., instance_url for Salesforce) + extraTokenFields.forEach(field => { + if (tokenResponse?.[field] != null) base[field] = tokenResponse[field] + }) + + // Merge extra form fields (e.g., baseUrl for Mautic self-hosted instance) + Object.assign(base, extraFormData) + + return base +} + +export default function Oauth2Connection({ + authDetails, + config, + setConfig, + isInfo = false, + customAuthFields, + onConnectionSaved +}) { + const [formData, setFormData] = useState({}) + const [errors, setErrors] = useState({}) + const [isLoading, setIsLoading] = useState(false) + const [isAuthorized, setIsAuthorized] = useState(false) + + const { + authCodeEndpoint, + tokenEndpoint, + refreshTokenUrl, + grantType = GRANT_TYPES.AUTHORIZATION_CODE, + clientAuthentication = 'body', + scope, + sslVerify = true, + extraTokenFields = [], + extraFields = [] + } = authDetails || {} + + const redirectUri = useMemo(() => getRedirectUri(), []) + + // Resolve {fieldName} templates in endpoint URLs using current form values (e.g., Mautic baseUrl) + const resolvedAuthCodeEndpoint = useMemo(() => { + if (!authCodeEndpoint) return null + return { ...authCodeEndpoint, url: resolveTemplate(authCodeEndpoint.url, formData) } + }, [authCodeEndpoint, formData]) + + const resolvedTokenEndpoint = useMemo(() => { + if (!tokenEndpoint) return null + return { ...tokenEndpoint, url: resolveTemplate(tokenEndpoint.url, formData) } + }, [tokenEndpoint, formData]) + + const handleChange = useCallback(event => { + const { name, value } = event.target + setFormData(prev => ({ ...prev, [name]: value })) + setErrors(prev => ({ ...prev, [name]: '' })) + }, []) + + const validate = useCallback(() => { + const next = {} + if (!formData.connectionName?.trim()) { + next.connectionName = __('Connection name is required', 'bit-integrations') + } + if (!formData.clientId?.trim()) { + next.clientId = __('Client ID is required', 'bit-integrations') + } + if (!formData.clientSecret?.trim()) { + next.clientSecret = __('Client secret is required', 'bit-integrations') + } + extraFields.forEach(field => { + if (field.required && !formData[field.name]?.trim()) { + next[field.name] = `${field.label} ${__('is required', 'bit-integrations')}` + } + }) + setErrors(next) + return Object.keys(next).length === 0 + }, [extraFields, formData]) + + const storeConnection = useCallback( + async authPayload => { + const saveRes = await saveConnection({ + app_slug: config?.app_slug || config?.type, + auth_type: AUTH_TYPES.OAUTH2, + connection_name: formData.connectionName, + account_name: formData.connectionName, + auth_details: authPayload, + encrypt_keys: defaultEncryptKeys[AUTH_TYPES.OAUTH2] || [] + }) + + if (!saveRes?.success) { + const reason = saveRes?.data?.data || saveRes?.data || '' + toast.error(`${__('Failed to save connection Cause:', 'bit-integrations')}${reason}`) + return null + } + + const connection = saveRes?.data?.data || null + setConfig(prev => ({ ...prev, connection_id: connection?.id })) + + if (onConnectionSaved) await onConnectionSaved(connection) + + setIsAuthorized(true) + toast.success(__('Authorized Successfully', 'bit-integrations')) + return connection + }, + [config, formData.connectionName, onConnectionSaved, setConfig] + ) + + const handleAuthorizationCodeFlow = useCallback(async () => { + const isPkce = grantType === GRANT_TYPES.AUTHORIZATION_CODE_PKCE + const declaredQueryParams = resolvedAuthCodeEndpoint?.queryParams || {} + let codeVerifier + + const extraParams = { client_id: formData.clientId } + if (!declaredQueryParams.response_type) extraParams.response_type = 'code' + if (scope && !declaredQueryParams.scope) extraParams.scope = scope + + if (isPkce) { + codeVerifier = generateCodeVerifier() + extraParams.code_challenge = await generateCodeChallengeS256(codeVerifier) + extraParams.code_challenge_method = 'S256' + } + + // Drop integration-declared client_id placeholder; URL.searchParams.append does not dedupe, + // so a placeholder + extraParams.client_id would emit two client_id entries. + const { client_id: _ignored, ...queryParams } = declaredQueryParams + const populatedAuthCodeEndpoint = { ...resolvedAuthCodeEndpoint, queryParams } + + const oauthChannelKey = createOauthChannelKey() + const state = buildCallbackState(oauthChannelKey) + const authUrl = buildAuthUrl(populatedAuthCodeEndpoint, { state, redirectUri, extraParams }) + const popupResponse = await openOauthPopup(authUrl, formData.connectionName || 'OAuth', { + channelKey: oauthChannelKey, + includeLegacyFallback: true + }) + + if (popupResponse?.error) { + throw new Error( + popupResponse.error === 'popup_blocked' + ? __('Popup blocked. Please allow popups and try again.', 'bit-integrations') + : __('Authorization window closed before completing.', 'bit-integrations') + ) + } + + if (!popupResponse?.code) { + throw new Error( + popupResponse?.error_description || __('Authorization code missing', 'bit-integrations') + ) + } + + const tokenRes = await exchangeAuthCodeForToken({ + tokenEndpoint: resolvedTokenEndpoint, + clientId: formData.clientId, + clientSecret: formData.clientSecret, + clientAuthentication, + code: popupResponse.code, + codeVerifier, + redirectUri, + sslVerify + }) + + if (!tokenRes?.success) { + throw new Error(tokenRes?.data?.message || __('Token exchange failed', 'bit-integrations')) + } + + return tokenRes?.data?.data || {} + }, [ + clientAuthentication, + formData, + grantType, + redirectUri, + resolvedAuthCodeEndpoint, + resolvedTokenEndpoint, + scope, + sslVerify + ]) + + const handleClientCredentialsFlow = useCallback(async () => { + const tokenRes = await exchangeClientCredentialsForToken({ + tokenEndpoint: resolvedTokenEndpoint, + clientId: formData.clientId, + clientSecret: formData.clientSecret, + clientAuthentication, + scope, + sslVerify + }) + + if (!tokenRes?.success) { + throw new Error(tokenRes?.data?.message || __('Token exchange failed', 'bit-integrations')) + } + + return tokenRes?.data?.data || {} + }, [ + clientAuthentication, + formData.clientId, + formData.clientSecret, + resolvedTokenEndpoint, + scope, + sslVerify + ]) + + const handleAuthorize = useCallback(async () => { + if (!validate()) return + + setIsLoading(true) + try { + let tokenResponse + if (grantType === GRANT_TYPES.CLIENT_CREDENTIALS) { + tokenResponse = await handleClientCredentialsFlow() + } else { + tokenResponse = await handleAuthorizationCodeFlow() + } + + const extraFormData = extraFields.reduce((acc, { name }) => { + if (formData[name] != null) acc[name] = formData[name] + return acc + }, {}) + + const savedAuthDetails = buildSavedAuthDetails({ + tokenResponse, + clientId: formData.clientId, + clientSecret: formData.clientSecret, + clientAuthentication, + grantType, + refreshTokenUrl, + tokenUrl: resolvedTokenEndpoint?.url, + scope, + sslVerify, + extraTokenFields, + extraFormData + }) + + await storeConnection(savedAuthDetails) + } catch (error) { + setIsAuthorized(false) + toast.error( + `${__('Authorization failed Cause:', 'bit-integrations')} ${error?.message || 'Unknown error'}` + ) + } finally { + setIsLoading(false) + } + }, [ + clientAuthentication, + extraFields, + extraTokenFields, + formData, + grantType, + handleAuthorizationCodeFlow, + handleClientCredentialsFlow, + refreshTokenUrl, + resolvedTokenEndpoint, + scope, + sslVerify, + storeConnection, + validate + ]) + + const isAuthCodeFlow = + grantType === GRANT_TYPES.AUTHORIZATION_CODE || grantType === GRANT_TYPES.AUTHORIZATION_CODE_PKCE + + return ( + <> +
      + {__('Connection Name:', 'bit-integrations')} +
      + +
      {errors.connectionName || ''}
      + + {extraFields.map(field => ( +
      +
      + {field.label}: +
      + {field.type === 'select' ? ( + + ) : ( + + )} +
      {errors[field.name] || ''}
      +
      + ))} + + {customAuthFields} + + {isAuthCodeFlow && ( + <> +
      + {__('Homepage URL:', 'bit-integrations')} +
      + +
      + {__('Callback / Redirect URL:', 'bit-integrations')} +
      + + + )} + +
      + {__('Client ID:', 'bit-integrations')} +
      + +
      {errors.clientId || ''}
      + +
      + {__('Client Secret:', 'bit-integrations')} +
      + +
      {errors.clientSecret || ''}
      + + + + ) +} diff --git a/frontend/src/hooks/useFetch.js b/frontend/src/hooks/useFetch.js index eb1bb2554..578be73f5 100644 --- a/frontend/src/hooks/useFetch.js +++ b/frontend/src/hooks/useFetch.js @@ -1,9 +1,9 @@ import useSWR from 'swr' import bitsFetch from '../Utils/bitsFetch' -const useFetch = ({ payload, action, method = 'POST' }) => { +const useFetch = ({ payload, action, method = 'POST', params = null }) => { const { data, error, mutate } = useSWR(action, uri => - bitsFetch(payload, Array.isArray(uri) ? uri[0] : uri, null, method) + bitsFetch(payload, Array.isArray(uri) ? uri[0] : uri, params, method) ) return { data, diff --git a/frontend/src/pages/AuthResponse.jsx b/frontend/src/pages/AuthResponse.jsx index f2b195dac..19d45a0b2 100644 --- a/frontend/src/pages/AuthResponse.jsx +++ b/frontend/src/pages/AuthResponse.jsx @@ -1,21 +1,22 @@ import { useEffect } from 'react' import { useSetRecoilState } from 'recoil' import { authInfoAtom } from '../GlobalStates' +import { broadcastAuthCodeResponse, readAuthResponseFromUrl } from '../Utils/oauthHelper' // popup window: render when redirected from oauth to bit-integration with code export default function AuthResponse() { const setAuthInfo = useSetRecoilState(authInfoAtom) useEffect(() => { - const urlParams = new URLSearchParams(window.location.hash) - const code = urlParams.get('code') + const response = readAuthResponseFromUrl() - if (code) { - setAuthInfo({ code: code }) + if (Object.keys(response).length > 0) { + broadcastAuthCodeResponse(response) + if (response.code) setAuthInfo({ code: response.code }) setTimeout(() => { window.close() - }, 100) + }, 200) } }, []) diff --git a/frontend/src/pages/Connections.jsx b/frontend/src/pages/Connections.jsx new file mode 100644 index 000000000..01fcffb4e --- /dev/null +++ b/frontend/src/pages/Connections.jsx @@ -0,0 +1,447 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import toast from 'react-hot-toast' +import CloseIcn from '../Icons/CloseIcn' +import EditIcn from '../Icons/EditIcn' +import TrashIcn from '../Icons/TrashIcn' +import Table from '../components/Utilities/Table' +import ConfirmModal from '../components/Utilities/ConfirmModal' +import { deleteConnection, listConnections, updateConnection } from '../Utils/connectionApi' +import { __, sprintf } from '../Utils/i18nwrap' + +export default function Connections() { + const getDeleteErrorMessage = useCallback(res => { + const linkedCount = Number(res?.data?.linked_count || 0) + + if (linkedCount > 0) { + return sprintf( + __('Connection is used in %d integrations. Unlink first, then delete.', 'bit-integrations'), + linkedCount + ) + } + + if (typeof res?.data?.message === 'string' && res.data.message.trim() !== '') { + return res.data.message + } + + if (typeof res?.data === 'string' && res.data.trim() !== '') { + return res.data + } + + return __('Failed to delete', 'bit-integrations') + }, []) + + const [connections, setConnections] = useState([]) + const [isLoading, setIsLoading] = useState(true) + const [filterApp, setFilterApp] = useState('') + const [editingId, setEditingId] = useState(null) + const [deletingId, setDeletingId] = useState(null) + const [savingId, setSavingId] = useState(null) + const [isConfirmPending, setIsConfirmPending] = useState(false) + const inputRef = useRef(null) + const editValueRef = useRef('') + + const fetchConnections = useCallback(() => { + setIsLoading(true) + listConnections('', { includeLinkedIntegrations: true }) + .then(res => { + if (res?.success && Array.isArray(res.data?.data)) { + setConnections(res.data.data) + } else { + setConnections([]) + } + }) + .catch(() => toast.error(__('Failed to load connections', 'bit-integrations'))) + .finally(() => setIsLoading(false)) + }, []) + + useEffect(() => { + fetchConnections() + }, [fetchConnections]) + + useEffect(() => { + if (editingId !== null && inputRef.current) inputRef.current.focus() + }, [editingId]) + + const apps = useMemo(() => { + const set = new Set(connections.map(c => c.app_slug).filter(Boolean)) + return Array.from(set).sort() + }, [connections]) + + const filteredConnections = useMemo( + () => connections.filter(conn => !filterApp || conn.app_slug === filterApp), + [connections, filterApp] + ) + + const startEdit = useCallback(conn => { + setEditingId(conn.id) + editValueRef.current = conn.connection_name || '' + }, []) + + const cancelEdit = useCallback(() => { + setEditingId(null) + editValueRef.current = '' + }, []) + + const saveRename = useCallback( + (id, rawName = editValueRef.current) => { + const next = (rawName || '').trim() + if (!next) { + toast.error(__('Connection name cannot be empty', 'bit-integrations')) + return + } + + const previous = connections.find(c => c.id === id) + if (previous && previous.connection_name === next) { + cancelEdit() + return + } + + setSavingId(id) + const promise = updateConnection({ id, connection_name: next }).then(res => { + if (!res?.success) throw new Error('rename_failed') + const row = res.data?.data + setConnections(prev => + prev.map(item => (item.id === id ? { ...item, ...(row || { connection_name: next }) } : item)) + ) + cancelEdit() + return __('Renamed', 'bit-integrations') + }) + + toast + .promise(promise, { + success: msg => msg, + error: __('Failed to rename', 'bit-integrations'), + loading: __('Saving...', 'bit-integrations') + }) + .finally(() => setSavingId(null)) + }, + [connections, cancelEdit] + ) + + const handleKeyDown = (e, id) => { + if (e.key === 'Enter') { + e.preventDefault() + saveRename(id, e.currentTarget.value) + } else if (e.key === 'Escape') { + e.preventDefault() + cancelEdit() + } + } + + const confirmDelete = useCallback(() => { + const id = deletingId + if (!id || isConfirmPending) return + + setDeletingId(null) + setIsConfirmPending(true) + + const promise = deleteConnection(id).then(res => { + if (!res?.success) { + throw new Error(getDeleteErrorMessage(res)) + } + setConnections(prev => prev.filter(c => c.id !== id)) + return __('Connection deleted', 'bit-integrations') + }) + + toast + .promise(promise, { + success: msg => msg, + error: error => error?.message || __('Failed to delete', 'bit-integrations'), + loading: __('Deleting...', 'bit-integrations') + }) + .finally(() => setIsConfirmPending(false)) + }, [deletingId, isConfirmPending, getDeleteErrorMessage]) + + const setBulkDelete = useCallback( + rows => { + const ids = [] + + if (Array.isArray(rows)) { + rows.forEach(row => { + if (row?.original?.id) { + ids.push(row.original.id) + } + }) + } else if (rows?.original?.id) { + ids.push(rows.original.id) + } + + if (ids.length < 1) { + return + } + + const promise = Promise.allSettled( + ids.map(id => + deleteConnection(id).then(res => { + if (!res?.success) { + throw new Error(getDeleteErrorMessage(res)) + } + return id + }) + ) + ).then(results => { + const deletedIds = results.filter(r => r.status === 'fulfilled').map(r => r.value) + + if (deletedIds.length > 0) { + setConnections(prev => prev.filter(item => !deletedIds.includes(item.id))) + } + + const failedCount = results.filter(r => r.status === 'rejected').length + + if (failedCount > 0) { + const uniqueFailureMessages = [ + ...new Set( + results + .filter(r => r.status === 'rejected') + .map(r => r.reason?.message) + .filter(Boolean) + ) + ] + + if (failedCount === ids.length && uniqueFailureMessages.length === 1) { + throw new Error(uniqueFailureMessages[0]) + } + + throw new Error(failedCount === ids.length ? 'bulk_delete_failed' : 'bulk_delete_partial') + } + + return deletedIds.length > 1 + ? __('Connections deleted', 'bit-integrations') + : __('Connection deleted', 'bit-integrations') + }) + + toast.promise(promise, { + success: msg => msg, + error: error => { + if (error?.message === 'bulk_delete_partial') { + return __('Some selected connections could not be deleted.', 'bit-integrations') + } + + if (error?.message === 'bulk_delete_failed') { + return __('Failed to delete', 'bit-integrations') + } + + return error?.message || __('Failed to delete', 'bit-integrations') + }, + loading: __('Deleting...', 'bit-integrations') + }) + }, + [getDeleteErrorMessage] + ) + + const columns = useMemo( + () => [ + { + Header: __('Action', 'bit-integrations'), + accessor: 'app_slug', + width: 130, + minWidth: 90, + Cell: ({ value }) => {value || '—'} + }, + { + Header: __('Connection Name', 'bit-integrations'), + accessor: 'connection_name', + width: 250, + minWidth: 170, + className: 'connections-name-cell', + Cell: ({ row, value }) => { + const conn = row.original + + if (editingId === conn.id) { + return ( +
      + { + editValueRef.current = e.target.value + }} + onKeyDown={e => handleKeyDown(e, conn.id)} + disabled={savingId === conn.id} + /> + + +
      + ) + } + + return ( +
      + {value || '—'} + +
      + ) + } + }, + { + Header: __('Linked Integrations', 'bit-integrations'), + accessor: 'linked_count', + width: 250, + minWidth: 180, + Cell: ({ row }) => { + const linkedIntegrations = Array.isArray(row?.original?.linked_integrations) + ? row.original.linked_integrations + : [] + + if (linkedIntegrations.length < 1) { + return '—' + } + + const previewLimit = 3 + const previewNames = linkedIntegrations + .slice(0, previewLimit) + .map(item => item?.name || `#${item?.id || ''}`) + .filter(Boolean) + + const extraCount = linkedIntegrations.length - previewNames.length + const linkedText = + extraCount > 0 ? `${previewNames.join(', ')} +${extraCount}` : previewNames.join(', ') + const tooltipText = linkedIntegrations + .map(item => item?.name || `#${item?.id || ''}`) + .filter(Boolean) + .join(', ') + + return ( + + {linkedText} + + ) + } + }, + { + Header: __('Auth Type', 'bit-integrations'), + accessor: 'auth_type', + width: 120, + minWidth: 95, + Cell: ({ value }) => {value || '—'} + }, + { + Header: __('Created', 'bit-integrations'), + accessor: 'created_at', + width: 140, + minWidth: 110, + Cell: ({ value }) => value || '—' + }, + { + id: 't_action', + Header: '', + accessor: 'id', + width: 70, + minWidth: 60, + maxWidth: 80, + disableSortBy: true, + Cell: ({ row }) => ( +
      + +
      + ) + } + ], + [editingId, savingId, cancelEdit, saveRename, startEdit] + ) + + return ( +
      + setDeletingId(null)} + btnTxt={__('Delete', 'bit-integrations')} + btnClass="" + /> + +
      +

      {__('Connections', 'bit-integrations')}

      +
      + +
      + +
      + +
      + +
      + +
      + + } + /> + + {!isLoading && filteredConnections.length === 0 && ( +

      + {connections.length === 0 + ? __( + 'No connections saved yet. Authorize an app from any integration to add one.', + 'bit-integrations' + ) + : __('No connections match the current filters.', 'bit-integrations')} +

      + )} + + + ) +} diff --git a/frontend/src/resource/sass/app.scss b/frontend/src/resource/sass/app.scss index c225adbe8..602ee04e4 100644 --- a/frontend/src/resource/sass/app.scss +++ b/frontend/src/resource/sass/app.scss @@ -7172,7 +7172,7 @@ _:-ms-fullscreen, justify-content: space-between; align-items: center; gap: 12px; - margin: 0 16px 12px; + margin: 0 5px 10px; } .btcd-table-top .btcd-t-actions { @@ -7529,6 +7529,220 @@ _:-ms-fullscreen, } } +#connections-page { + .f-table .thead .tr .th, + .f-table .tbody .tr .td { + padding-left: 6px; + padding-right: 6px; + } + + .f-table .thead .tr .th:first-child, + .f-table .tbody .tr .td:first-child { + padding-left: 10px; + } + + .connections-toolbar { + align-items: center; + justify-content: start; + gap: 12px; + flex-wrap: wrap; + } + + .connections-toolbar-meta { + align-items: center; + gap: 8px; + } + + .connections-count-txt { + display: inline-flex; + align-items: center; + font-size: 12px; + font-weight: 600; + color: #5f6d8a; + line-height: 1; + border: 1px solid #dce4f3; + border-radius: 999px; + background: #f3f6fd; + padding: 7px 10px; + } + + .connections-table-filters { + align-items: center; + gap: 8px; + flex-wrap: wrap; + min-width: 220px; + flex: 1 1 220px; + } + + .connections-filter-select { + width: 220px; + margin: 0 !important; + } + + .connections-app-tag { + display: inline-block; + padding: 4px 9px; + border-radius: 999px; + border: 1px solid #d8e0f1; + background: #f2f5fb; + color: #34415f; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.04em; + text-transform: uppercase; + } + + .connections-auth-tag { + display: inline-block; + padding: 4px 9px; + border-radius: 999px; + border: 1px solid #f4d8b8; + background: #fff4e7; + color: #8f5c21; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.03em; + text-transform: uppercase; + } + + .connections-name-cell { + line-height: 1.35; + overflow: visible !important; + white-space: normal !important; + position: relative; + z-index: 0; + padding-top: 6px !important; + padding-bottom: 6px !important; + } + + .connections-name-row { + align-items: center; + justify-content: space-between; + gap: 6px; + min-width: 0; + width: 100%; + position: relative; + z-index: 1; + + .connections-name-txt { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + .connections-rename-btn { + width: 24px; + height: 24px; + min-width: 24px; + min-height: 24px; + border-radius: 7px; + background: transparent; + color: #9246f7; + margin: 0; + + &:hover { + background: #efe4ff; + color: #6f26dc; + } + + &:active { + background: #e5d6ff; + } + } + + .connections-edit-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto auto; + width: 100%; + align-items: center; + gap: 6px; + } + + .connections-edit-input { + min-width: 0 !important; + margin: 0 !important; + min-height: 34px !important; + padding: 6px 10px !important; + font-size: 13px !important; + } + + .connections-edit-btn { + margin: 0 !important; + min-height: 28px; + font-size: 11px; + line-height: 1.1; + border-radius: 6px; + padding: 5px 8px; + } + + .connections-edit-cancel-btn { + width: 28px; + height: 28px; + min-width: 28px; + min-height: 28px; + margin: 0 !important; + border-radius: 7px; + background: #f2f4fa; + color: #6a7693; + border: 1px solid #e0e7f3; + + &:hover { + background: #e8edf8; + color: #4c5979; + } + + &:active { + background: #dfe6f3; + } + } + + .connections-edit-cancel-icn { + display: block; + margin: 0 auto; + } + + .connections-edit-cancel-icn .svg-icn { + stroke: currentcolor; + } + + .connections-action-cell { + justify-content: center; + width: 100%; + } + + .connections-empty-note { + color: #697694; + line-height: 1.5; + padding-bottom: 12px; + } +} + +@media only screen and (max-width: 767px) { + #connections-page { + .connections-toolbar { + width: 100%; + align-items: stretch; + } + + .connections-table-filters { + width: 100%; + min-width: 0; + flex: 1 1 auto; + } + + .connections-filter-select { + width: 100%; + max-width: none; + } + + .connections-toolbar-meta { + width: 100%; + justify-content: space-between; + } + } +} + @keyframes fadeIn { from { opacity: 0;